mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
TUT: bridge using external lumped point motion (#1341)
- see its accompanying README for additional setup instructions
This commit is contained in:
13
tutorials/incompressible/lumpedPointMotion/bridge/code/Allclean
Executable file
13
tutorials/incompressible/lumpedPointMotion/bridge/code/Allclean
Executable file
@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
cd "${0%/*}" || exit # Run from this directory
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
wclean
|
||||
|
||||
# Remove executable
|
||||
rm -f polynomial-motion
|
||||
|
||||
# Remove known output/debug files
|
||||
rm -f *.txt *.vtp *.vtp.series
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
@ -0,0 +1,3 @@
|
||||
polynomial-motion.C
|
||||
|
||||
EXE = $(PWD)/polynomial-motion
|
||||
@ -0,0 +1,13 @@
|
||||
EXE_INC = \
|
||||
-I$(LIB_SRC)/finiteVolume/lnInclude \
|
||||
-I$(LIB_SRC)/fileFormats/lnInclude \
|
||||
-I$(LIB_SRC)/meshTools/lnInclude \
|
||||
-I$(LIB_SRC)/dynamicMesh/lnInclude \
|
||||
-I$(LIB_SRC)/lumpedPointMotion/lnInclude
|
||||
|
||||
EXE_LIBS = \
|
||||
-lfiniteVolume \
|
||||
-lfileFormats \
|
||||
-lmeshTools \
|
||||
-ldynamicMesh \
|
||||
-llumpedPointMotion
|
||||
@ -0,0 +1,642 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2020 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
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
|
||||
polynomial-motion
|
||||
|
||||
Description
|
||||
Polynomial representations of position/angle for fluid-structure
|
||||
interface check.
|
||||
Generates position and rotation angle of each node.
|
||||
|
||||
Note
|
||||
The values may or may not correspond to experimental values.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "argList.H"
|
||||
#include "Time.H"
|
||||
#include "Fstream.H"
|
||||
#include "polynomialFunction.H"
|
||||
#include "unitConversion.H"
|
||||
#include "foamVtkSeriesWriter.H"
|
||||
#include "lumpedPointTools.H"
|
||||
#include "lumpedPointState.H"
|
||||
#include "lumpedPointIOMovement.H"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace Foam;
|
||||
|
||||
//- Position/angle generator based on polynomials
|
||||
class position_generator
|
||||
{
|
||||
// Private Data
|
||||
|
||||
typedef FixedList<polynomialFunction,3> xyzPoly;
|
||||
|
||||
List<xyzPoly> points_;
|
||||
List<xyzPoly> angles_;
|
||||
|
||||
|
||||
// Private Member Functions
|
||||
|
||||
//- Calculate position/rotation at given time
|
||||
lumpedPointState calc(scalar currTime) const
|
||||
{
|
||||
// Limit the time
|
||||
currTime = min(currTime, maxTime);
|
||||
|
||||
const auto polyToValue =
|
||||
[=](const xyzPoly& p) -> vector
|
||||
{
|
||||
return vector
|
||||
(
|
||||
p[0].value(currTime),
|
||||
p[1].value(currTime),
|
||||
p[2].value(currTime)
|
||||
);
|
||||
};
|
||||
|
||||
pointField pts(points_.size());
|
||||
std::transform
|
||||
(
|
||||
points_.cbegin(), points_.cend(), pts.begin(), polyToValue
|
||||
);
|
||||
|
||||
vectorField ang(angles_.size());
|
||||
std::transform
|
||||
(
|
||||
angles_.cbegin(), angles_.cend(), ang.begin(), polyToValue
|
||||
);
|
||||
|
||||
return lumpedPointState{pts, ang};
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// Control Parameters
|
||||
|
||||
// Upper time limit on polynomials
|
||||
scalar maxTime = GREAT;
|
||||
|
||||
|
||||
// Constructors
|
||||
|
||||
//- Default construct
|
||||
position_generator() = default;
|
||||
|
||||
//- Read construct from dictionary
|
||||
position_generator(const dictionary& dict)
|
||||
{
|
||||
dict.readIfPresent("maxTime", maxTime);
|
||||
dict.readEntry("points", points_);
|
||||
dict.readEntry("angles", angles_);
|
||||
|
||||
if (angles_.size() != points_.size())
|
||||
{
|
||||
Info<< "Resized angles to match number of points" << nl;
|
||||
angles_.resize(points_.size(), xyzPoly(polynomialFunction()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Member Functions
|
||||
|
||||
//- Calculate position/rotation at given time
|
||||
lumpedPointState state(const scalar currTime) const
|
||||
{
|
||||
return calc(currTime);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
argList::addNote
|
||||
(
|
||||
"Polynomial representations of position/angle for fluid-structure"
|
||||
" interface check."
|
||||
" Generates position and rotation angle of each node."
|
||||
);
|
||||
|
||||
argList::noBanner();
|
||||
argList::noParallel();
|
||||
|
||||
// Time control
|
||||
argList::addOption
|
||||
(
|
||||
"time",
|
||||
"value",
|
||||
"The time to use"
|
||||
);
|
||||
argList::addOption
|
||||
(
|
||||
"deltaT",
|
||||
"value",
|
||||
"The time increment for multiple time loops"
|
||||
);
|
||||
argList::addOption
|
||||
(
|
||||
"nTimes",
|
||||
"value",
|
||||
"The number of time loops"
|
||||
);
|
||||
|
||||
// Query, output
|
||||
argList::addBoolOption
|
||||
(
|
||||
"query",
|
||||
"Report values only and exit"
|
||||
);
|
||||
argList::addOption
|
||||
(
|
||||
"output",
|
||||
"file",
|
||||
"write to file, with header"
|
||||
);
|
||||
|
||||
argList::addOption
|
||||
(
|
||||
"scale",
|
||||
"factor",
|
||||
"Scaling factor for movement (default: 1)"
|
||||
);
|
||||
argList::addOption
|
||||
(
|
||||
"visual-length",
|
||||
"len",
|
||||
"Visualization length for planes (visualized as triangles)"
|
||||
);
|
||||
|
||||
// Run controls
|
||||
argList::addBoolOption
|
||||
(
|
||||
"dry-run",
|
||||
"Test movement without a mesh"
|
||||
);
|
||||
argList::addBoolOption
|
||||
(
|
||||
"removeLock",
|
||||
"Remove lock-file on termination of slave"
|
||||
);
|
||||
argList::addBoolOption
|
||||
(
|
||||
"slave",
|
||||
"Invoke as a slave responder for testing"
|
||||
);
|
||||
|
||||
argList::addArgument
|
||||
(
|
||||
"file",
|
||||
"Points/angles as triples of polynomials.\n"
|
||||
"Dictionary format"
|
||||
);
|
||||
|
||||
#include "setRootCase.H"
|
||||
|
||||
// The position/angle generator
|
||||
position_generator gen;
|
||||
|
||||
{
|
||||
// Input polynomials file
|
||||
IFstream is(args[1]);
|
||||
|
||||
dictionary dict(is);
|
||||
gen = position_generator(dict);
|
||||
}
|
||||
|
||||
|
||||
// Control parameters
|
||||
const bool dryrun = args.found("dry-run");
|
||||
const bool slave = args.found("slave");
|
||||
const bool removeLock = args.found("removeLock");
|
||||
|
||||
const bool optQuery = args.found("query");
|
||||
const fileName outputFile(args.getOrDefault<fileName>("output", ""));
|
||||
|
||||
const scalar relax = args.getOrDefault<scalar>("scale", 1);
|
||||
|
||||
args.readIfPresent("visual-length", lumpedPointState::visLength);
|
||||
|
||||
// Time parameters
|
||||
scalar currTime = args.getOrDefault<scalar>("time", 0);
|
||||
const scalar deltaT = args.getOrDefault("deltaT", 0.001);
|
||||
const label nTimes = args.getOrDefault<label>("nTimes", 1);
|
||||
|
||||
// Loop handling for slave
|
||||
const bool infiniteLoop = slave && !args.found("nTimes");
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Slave mode
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
if (slave)
|
||||
{
|
||||
Info<< "Running as slave responder" << endl;
|
||||
|
||||
if (Pstream::parRun())
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "Running as slave responder is not permitted in parallel"
|
||||
<< nl
|
||||
<< exit(FatalError);
|
||||
}
|
||||
|
||||
#include "createTime.H"
|
||||
|
||||
// Create movement without a mesh
|
||||
autoPtr<lumpedPointIOMovement> movementPtr =
|
||||
lumpedPointIOMovement::New(runTime);
|
||||
|
||||
if (!movementPtr)
|
||||
{
|
||||
Info<< "No valid movement found" << endl;
|
||||
return 1;
|
||||
}
|
||||
auto& movement = *movementPtr;
|
||||
|
||||
externalFileCoupler& coupler = movement.coupler();
|
||||
|
||||
for (label timei = 0; infiniteLoop || (timei < nTimes); ++timei)
|
||||
{
|
||||
Info<< args.executable() << ": waiting for master" << endl;
|
||||
|
||||
// Wait for master, but stop if status=done was seen
|
||||
if (!coupler.waitForMaster())
|
||||
{
|
||||
Info<< args.executable()
|
||||
<< ": stopping status=done was detected" << endl;
|
||||
break;
|
||||
}
|
||||
|
||||
scalar timeValue = currTime;
|
||||
|
||||
if (infiniteLoop)
|
||||
{
|
||||
// Get output file
|
||||
IFstream is(coupler.resolveFile(movement.outputName()));
|
||||
|
||||
dictionary dict(is);
|
||||
|
||||
timeValue = dict.get<scalar>("time");
|
||||
}
|
||||
|
||||
lumpedPointState state(gen.state(timeValue));
|
||||
|
||||
// Generate input for OpenFOAM
|
||||
{
|
||||
OFstream os(coupler.resolveFile(movement.inputName()));
|
||||
if
|
||||
(
|
||||
movement.inputFormat()
|
||||
== lumpedPointState::inputFormatType::PLAIN
|
||||
)
|
||||
{
|
||||
state.writePlain(os);
|
||||
}
|
||||
else
|
||||
{
|
||||
os.writeEntry("time", timeValue);
|
||||
state.writeDict(os);
|
||||
}
|
||||
}
|
||||
|
||||
Info<< args.executable()
|
||||
<< ": updating state " << timei
|
||||
<< " - switch to master"
|
||||
<< endl;
|
||||
|
||||
// Let OpenFOAM know that it can continue
|
||||
coupler.useMaster();
|
||||
|
||||
currTime += deltaT;
|
||||
}
|
||||
|
||||
if (removeLock)
|
||||
{
|
||||
Info<< args.executable() << ": removing lock file" << endl;
|
||||
coupler.useSlave(); // This removes the lock-file
|
||||
}
|
||||
|
||||
Info<< args.executable() << ": finishing" << nl;
|
||||
|
||||
Info<< "\nEnd\n" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// dry-run
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
if (dryrun)
|
||||
{
|
||||
Info<< "dry-run: creating states only" << nl;
|
||||
|
||||
autoPtr<Time> runTimePtr;
|
||||
autoPtr<lumpedPointIOMovement> movementPtr;
|
||||
|
||||
const bool throwingIOError = FatalIOError.throwExceptions();
|
||||
const bool throwingError = FatalError.throwExceptions();
|
||||
|
||||
try
|
||||
{
|
||||
Info<< "Create time" << flush;
|
||||
|
||||
runTimePtr = Time::New(args);
|
||||
|
||||
// Create movement without a mesh
|
||||
movementPtr = lumpedPointIOMovement::New(*runTimePtr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Info<< " ... failed (optional for dry-run)";
|
||||
}
|
||||
Info<< nl << endl;
|
||||
|
||||
FatalError.throwExceptions(throwingError);
|
||||
FatalIOError.throwExceptions(throwingIOError);
|
||||
|
||||
if (!movementPtr)
|
||||
{
|
||||
Info<< "No time, run without movement information\n" << endl;
|
||||
}
|
||||
|
||||
const lumpedPointState state0(gen.state(0));
|
||||
|
||||
vtk::seriesWriter stateSeries;
|
||||
|
||||
for
|
||||
(
|
||||
label timei = 0, outputCount = 0;
|
||||
timei < nTimes;
|
||||
++timei
|
||||
)
|
||||
{
|
||||
lumpedPointState state(gen.state(currTime));
|
||||
state.relax(relax, state0);
|
||||
|
||||
Info<< "output [" << timei << '/' << nTimes << ']';
|
||||
|
||||
// State/response = what comes back from FEM
|
||||
{
|
||||
const word outputName =
|
||||
word::printf("state_%06d.vtp", outputCount);
|
||||
|
||||
Info<< " " << outputName;
|
||||
|
||||
if (movementPtr)
|
||||
{
|
||||
movementPtr->writeStateVTP(state, outputName);
|
||||
}
|
||||
else
|
||||
{
|
||||
state.writeVTP(outputName);
|
||||
}
|
||||
stateSeries.append(outputCount, outputName);
|
||||
}
|
||||
|
||||
Info<< endl;
|
||||
|
||||
++outputCount;
|
||||
currTime += deltaT;
|
||||
}
|
||||
|
||||
// Write file series
|
||||
if (stateSeries.size())
|
||||
{
|
||||
Info<< nl << "write state.vtp.series" << nl;
|
||||
stateSeries.write("state.vtp");
|
||||
}
|
||||
|
||||
Info<< "\nEnd\n" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Report values or generate a file
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
if (optQuery || !outputFile.empty())
|
||||
{
|
||||
autoPtr<OFstream> osPtr;
|
||||
|
||||
if (!outputFile.empty())
|
||||
{
|
||||
osPtr.reset(new OFstream(outputFile));
|
||||
auto& os = *osPtr;
|
||||
|
||||
os.precision(8);
|
||||
|
||||
// One file with everything, output using OpenFOAM syntax
|
||||
|
||||
IOobject::writeBanner(os)
|
||||
<< "FoamFile\n{\n"
|
||||
<< " version " << os.version() << ";\n"
|
||||
<< " format " << os.format() << ";\n"
|
||||
<< " class " << "dictionary" << ";\n"
|
||||
<< " object " << "response" << ";\n"
|
||||
<< "}\n";
|
||||
|
||||
IOobject::writeDivider(os) << nl;
|
||||
|
||||
os << "// angles are Euler angles z-x-z (intrinsic)" << nl;
|
||||
os.writeEntry("degrees", "false");
|
||||
os << nl;
|
||||
os << "response" << nl;
|
||||
os << '(' << nl;
|
||||
}
|
||||
else
|
||||
{
|
||||
Info().precision(8);
|
||||
}
|
||||
|
||||
|
||||
for (label timei = 0; timei < nTimes; ++timei)
|
||||
{
|
||||
lumpedPointState state(gen.state(currTime));
|
||||
|
||||
if (osPtr)
|
||||
{
|
||||
// Report position/angle
|
||||
auto& os = *osPtr;
|
||||
|
||||
os.beginBlock();
|
||||
|
||||
os.writeEntry("time", currTime);
|
||||
|
||||
state.writeDict(os);
|
||||
|
||||
os.endBlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Report position/angle
|
||||
auto& os = Info();
|
||||
|
||||
os.writeEntry("time", currTime);
|
||||
state.writeDict(os);
|
||||
}
|
||||
|
||||
currTime += deltaT;
|
||||
}
|
||||
|
||||
|
||||
if (osPtr)
|
||||
{
|
||||
auto& os = *osPtr;
|
||||
|
||||
os << ')' << token::END_STATEMENT << nl;
|
||||
|
||||
IOobject::writeEndDivider(os);
|
||||
|
||||
Info<< nl << "wrote " << nTimes << " time values" << nl;
|
||||
Info<< "\nEnd\n" << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
InfoErr << "wrote " << nTimes << " time values" << nl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// test patch movement
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
#include "createTime.H"
|
||||
|
||||
runTime.setTime(instant(runTime.constant()), 0);
|
||||
|
||||
#include "createNamedMesh.H"
|
||||
|
||||
// Create movement with mesh
|
||||
autoPtr<lumpedPointIOMovement> movementPtr =
|
||||
lumpedPointIOMovement::New(mesh);
|
||||
|
||||
if (!movementPtr)
|
||||
{
|
||||
Info<< "No valid movement found" << endl;
|
||||
return 1;
|
||||
}
|
||||
auto& movement = *movementPtr;
|
||||
|
||||
// Reference state0
|
||||
const lumpedPointState& state0 = movement.state0();
|
||||
|
||||
pointIOField points0(lumpedPointTools::points0Field(mesh));
|
||||
|
||||
const label nPatches = lumpedPointTools::setPatchControls(mesh, points0);
|
||||
if (!nPatches)
|
||||
{
|
||||
Info<< "No point patches with lumped movement found" << endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
Info<< "Lumped point patch controls set on "
|
||||
<< nPatches << " patches" << nl;
|
||||
|
||||
lumpedPointTools::setInterpolators(mesh, points0);
|
||||
|
||||
|
||||
// Output vtk file series
|
||||
vtk::seriesWriter stateSeries;
|
||||
vtk::seriesWriter geomSeries;
|
||||
|
||||
// Initial geometry
|
||||
movement.writeVTP("geom_init.vtp", state0, mesh, points0);
|
||||
|
||||
lumpedPointTools::setInterpolators(mesh);
|
||||
|
||||
for
|
||||
(
|
||||
label timei = 0, outputCount = 0;
|
||||
timei < nTimes;
|
||||
++timei
|
||||
)
|
||||
{
|
||||
lumpedPointState state(gen.state(currTime));
|
||||
|
||||
state += movement.origin();
|
||||
movement.scalePoints(state);
|
||||
state.relax(relax, state0);
|
||||
|
||||
Info<< "output [" << timei << '/' << nTimes << ']';
|
||||
|
||||
// State/response = what comes back from FEM
|
||||
{
|
||||
const word outputName =
|
||||
word::printf("state_%06d.vtp", outputCount);
|
||||
|
||||
Info<< " " << outputName;
|
||||
|
||||
movement.writeStateVTP(state, outputName);
|
||||
stateSeries.append(outputCount, outputName);
|
||||
}
|
||||
|
||||
{
|
||||
const word outputName =
|
||||
word::printf("geom_%06d.vtp", outputCount);
|
||||
|
||||
Info<< " " << outputName;
|
||||
|
||||
movement.writeVTP(outputName, state, mesh, points0);
|
||||
geomSeries.append(outputCount, outputName);
|
||||
}
|
||||
|
||||
Info<< endl;
|
||||
|
||||
++outputCount;
|
||||
currTime += deltaT;
|
||||
}
|
||||
|
||||
|
||||
// Write file series
|
||||
|
||||
if (geomSeries.size())
|
||||
{
|
||||
Info<< nl << "write geom.vtp.series" << nl;
|
||||
geomSeries.write("geom.vtp");
|
||||
}
|
||||
if (stateSeries.size())
|
||||
{
|
||||
Info<< nl << "write state.vtp.series" << nl;
|
||||
stateSeries.write("state.vtp");
|
||||
}
|
||||
|
||||
Info<< "\nEnd\n" << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
Reference in New Issue
Block a user