MaMiCo 1.2
Loading...
Searching...
No Matches
IcoFoamInterface.h
1// This file is part of the Mamico project. For conditions of distribution
2// and use, please see the copyright notice in Mamico's main folder, or at
3// www5.in.tum.de/mamico
4#ifndef _COUPLING_SOLVERS_IcoFoamInterface_H_
5#define _COUPLING_SOLVERS_IcoFoamInterface_H_
6
7#include "coupling/solvers/CouetteSolver.h"
8// Includes from OpenFOAM fdCFD.H, unnecessary includes are removed
9#include "adjustPhi.H"
10#include "constrainHbyA.H"
11#include "constrainPressure.H"
12#include "coupling/datastructures/FlexibleCellContainer.h"
13#include "findRefCell.H"
14#include "fvc.H"
15#include "fvm.H"
16#include "pisoControl.H"
17#include <map>
18
19namespace coupling {
20namespace solvers {
22}
23} // namespace coupling
24
35public:
36 IcoFoamInterface(int rank, int plotEveryTimestep, double channelheight, std::string dict, std::string folder,
38 : AbstractCouetteSolver<3>(), runTime(Foam::Time::controlDictName, dict, folder),
39 mesh(Foam::IOobject(Foam::fvMesh::defaultRegion, runTime.timeName(), runTime, Foam::IOobject::MUST_READ)),
40 transportProperties(Foam::IOobject("transportProperties", runTime.constant(), mesh, Foam::IOobject::MUST_READ_IF_MODIFIED, Foam::IOobject::NO_WRITE)),
41 nu("nu", Foam::dimViscosity, transportProperties),
42 p(Foam::IOobject("p", runTime.timeName(), mesh, Foam::IOobject::MUST_READ, Foam::IOobject::AUTO_WRITE), mesh),
43 U(Foam::IOobject("U", runTime.timeName(), mesh, Foam::IOobject::MUST_READ, Foam::IOobject::AUTO_WRITE), mesh),
44 phi(Foam::IOobject("phi", runTime.timeName(), mesh, Foam::IOobject::READ_IF_PRESENT, Foam::IOobject::AUTO_WRITE), Foam::fvc::flux(U)), piso(mesh),
45 _boundariesWithMD(boundariesWithMD), _dx(std::cbrt(Foam::max(mesh.cellVolumes()))), _channelheight(channelheight), _rank(rank),
46 _plotEveryTimestep(plotEveryTimestep), _uWall(uWall) {
47 if (skipRank()) {
48 return;
49 }
50 Foam::setRefCell(p, mesh.solutionDict().subDict("PISO"), pRefCell, pRefValue);
51 mesh.setFluxRequired(p.name());
52 }
53
54 virtual ~IcoFoamInterface() {
55 if (skipRank()) {
56 return;
57 }
58 if (_boundaryIndicesInner) {
59 delete[] _boundaryIndicesInner;
60 _boundaryIndicesInner = NULL;
61 }
62 if (_boundaryIndices) {
63 delete[] _boundaryIndices;
64 _boundaryIndices = NULL;
65 }
66 }
67
68 // advance the solver dt in time, this may include several timesteps,
69 // the sequence is based on the OpenFOAM IcoFoam solver
70 void advance(double dt) override {
71 if (skipRank()) {
72 return;
73 }
74 size_t number = floor(dt / runTime.deltaTValue() + 0.5);
75 for (size_t i = 0; i < number; i++) {
76 using namespace Foam;
77 ++runTime;
78 Info << "Time = " << runTime.timeName() << nl << endl;
79
80 // Momentum predictor
81 fvVectorMatrix UEqn(fvm::ddt(U) + fvm::div(phi, U) - fvm::laplacian(nu, U));
82 if (piso.momentumPredictor()) {
83 solve(UEqn == -fvc::grad(p));
84 }
85 // --- PISO loop
86 while (piso.correct()) {
87 volScalarField rAU(1.0 / UEqn.A());
88 volVectorField HbyA(constrainHbyA(rAU * UEqn.H(), U, p));
89 surfaceScalarField phiHbyA("phiHbyA", fvc::flux(HbyA) + fvc::interpolate(rAU) * fvc::ddtCorr(U, phi));
90 adjustPhi(phiHbyA, U, p);
91
92 // Update the pressure BCs to ensure flux consistency
93 constrainPressure(p, U, phiHbyA, rAU);
94
95 // Non-orthogonal pressure corrector loop
96 while (piso.correctNonOrthogonal()) {
97 // Pressure corrector
98 fvScalarMatrix pEqn(fvm::laplacian(rAU, p) == fvc::div(phiHbyA));
99 pEqn.setReference(pRefCell, pRefValue);
100 pEqn.solve();
101 if (piso.finalNonOrthogonalIter()) {
102 phi = phiHbyA - pEqn.flux();
103 }
104 }
105 U = HbyA - rAU * fvc::grad(p);
106 U.correctBoundaryConditions();
107 }
108 // runTime.write(); // writes the original OpenFOAM output
109 // plot();
110 _timestepCounter++;
111 plottxt();
112 }
113 }
114
115 // Get the velocity at position pos for the current time step
116 tarch::la::Vector<3, double> getVelocity(tarch::la::Vector<3, double> pos) const override {
117 const Foam::vector foamPosition(pos[0], pos[1], pos[2]);
118 const int foamIndice = U.mesh().findCell(foamPosition);
119 if (foamIndice > 0) {
120 return tarch::la::Vector<3, double>(U[foamIndice][0], U[foamIndice][1], U[foamIndice][2]);
121 } else {
122 return tarch::la::Vector<3, double>(0, 0, 0);
123 }
124 };
125
126 // Changes the velocity on the moving wall (refers to Couette szenario)
127 void setWallVelocity(tarch::la::Vector<3, double> wallVelocity) override {
128 const size_t pointsInBoundary = U.boundaryFieldRef()[0].size();
129 for (size_t i = 0; i < pointsInBoundary; i++) {
130 U.boundaryFieldRef()[0][i].x() = wallVelocity[0];
131 U.boundaryFieldRef()[0][i].y() = wallVelocity[1];
132 U.boundaryFieldRef()[0][i].z() = wallVelocity[2];
133 }
134 };
135
136 // Applies the MD data (just velocities) as boundary condition, the mapping between the continuum and the MD is provided by the setMDBoundary()
137 void setMDBoundaryValues(coupling::datastructures::FlexibleCellContainer<3>& md2macroBuffer) {
138 if (skipRank()) {
139 return;
140 }
141 size_t pointsWritten = 0;
142 I00 idx;
143 coupling::datastructures::CouplingCell<3>* cell;
144 for (auto pair : md2macroBuffer) {
145 std::tie(cell, idx) = pair;
146 if(_boundaryPointMap.count(idx) > 0){
147 for(auto[it, rangeEnd] = _boundaryPointMap.equal_range(idx); it != rangeEnd; ++it){
148 unsigned int boundarypoint = it->second;
149 tarch::la::Vector<3, double> localOuterVel = getVelocity(_boundaryPointsOuter.at(boundarypoint));
150 tarch::la::Vector<3, double> localInnerVel = (1.0 / cell->getMacroscopicMass()) * cell->getMacroscopicMomentum();
151 _boundaryIndices[boundarypoint]->x() = (localOuterVel[0] + localInnerVel[0]) * 0.5;
152 _boundaryIndices[boundarypoint]->y() = (localOuterVel[1] + localInnerVel[1]) * 0.5;
153 _boundaryIndices[boundarypoint]->z() = (localOuterVel[2] + localInnerVel[2]) * 0.5;
154 pointsWritten++;
155 }
156 }
157 }
158 if(pointsWritten != _numberBoundaryPoints)
159 throw std::runtime_error(std::string("IcoFoamInterface::setMDBoundaryValues(): boundary point mapping error!"));
160 }
161
162 // Setup for the mapping from MD to continuum data. Velocity values are necessary directly on the boundary. Therefore the MD values from the two cells beside
163 // the boundary are interpolated. The function looks for the index of every cell that data is necessary from. This indices will be stored in two arrays.
164 void setupMDBoundary() {
165 if (skipRank()) {
166 return;
167 }
168 size_t innerMDBoundaryIndex = 0;
169 while (_boundariesWithMD[innerMDBoundaryIndex] == 0) {
170 innerMDBoundaryIndex++;
171 }
172 _numberBoundaryPoints = 6 * U.boundaryFieldRef()[innerMDBoundaryIndex].size();
173 _boundaryPointsOuter.resize(_numberBoundaryPoints);
174 _boundaryIndicesInner = new I00[_numberBoundaryPoints];
175 _boundaryIndices = new Foam::vector*[_numberBoundaryPoints];
176 size_t counter = 0;
177 const unsigned int dim = 3;
178 for (size_t boundary = 0; boundary < 12; boundary++) {
179 if (_boundariesWithMD[boundary] == 1) {
180 size_t MDPointsPerBoundary = _numberBoundaryPoints / 6;
181 for (size_t j = 0; j < MDPointsPerBoundary; j++) {
182 _boundaryIndices[counter] = &(U.boundaryFieldRef()[boundary][j]);
183 _boundaryPointsOuter[counter] = getOuterPointFromBoundary(boundary, j);
184 _boundaryIndicesInner[counter] = IDXS.getCellIndex(getInnerPointFromBoundary(boundary, j));
185 counter++;
186 }
187 }
188 }
189 if(counter != _numberBoundaryPoints)
190 throw std::runtime_error(std::string("IcoFoamInterface::setupMDBoundary(): boundary point mapping error!"));
191 for (size_t i = 0; i < _numberBoundaryPoints; i++)
192 _boundaryPointMap.insert({_boundaryIndicesInner[i], i});
193 }
194
195private:
196 // Gets the next cell center beside the boundary, necessary to set the boundary condition from MD data
197 const tarch::la::Vector<3, double> getOuterPointFromBoundary(const int layer, const int index) {
198 const Foam::vectorField FoamCoord(U.boundaryFieldRef()[layer].patch().Cf()[index] - (U.boundaryFieldRef()[layer].patch().nf() * _dx * 0.5));
199 const tarch::la::Vector<3, double> FoamCoordVector(FoamCoord[0][0], FoamCoord[0][1], FoamCoord[0][2]);
200 return FoamCoordVector;
201 }
202
203 // Gets the next cell center beside the boundary, necessary to set the boundary condition from MD data
204 const tarch::la::Vector<3, double> getInnerPointFromBoundary(const int layer, const int index) {
205 const Foam::vectorField FoamCoord(U.boundaryFieldRef()[layer].patch().Cf()[index] + (U.boundaryFieldRef()[layer].patch().nf() * _dx * 0.5));
206 const tarch::la::Vector<3, double> FoamCoordVector(FoamCoord[0][0], FoamCoord[0][1], FoamCoord[0][2]);
207 return FoamCoordVector;
208 }
209
210 // Gets the next cell center beside the boundary, necessary to set the boundary condition from MD data
211 const tarch::la::Vector<3, double> getSecondOuterPointFromBoundary(const int layer, const int index) {
212 const Foam::vectorField FoamCoord(U.boundaryFieldRef()[layer].patch().Cf()[index] + (U.boundaryFieldRef()[layer].patch().nf() * _dx * 1.5));
213 const tarch::la::Vector<3, double> FoamCoordVector(FoamCoord[0][0], FoamCoord[0][1], FoamCoord[0][2]);
214 return FoamCoordVector;
215 }
216
217 // Calculates the analytical solution for U for z=pos for the current time step
218 const double getAnalyticalCouetteU(const double& pos) const {
219 const double pi = 3.141592653589793238;
220 double u_analytical = 0.0;
221 for (int k = 1; k < 30; k++) {
222 u_analytical += 1.0 / k * std::sin(k * pi * pos / _channelheight) *
223 std::exp(-k * k * pi * pi / (_channelheight * _channelheight) * nu.value() * runTime.timeOutputValue());
224 }
225 return _uWall[0] * (1.0 - pos / _channelheight - 2.0 / pi * u_analytical);
226 }
227
229 void plottxt() const {
230 if (_plotEveryTimestep < 1 || _timestepCounter % _plotEveryTimestep > 0)
231 return;
232 std::stringstream ss;
233 ss << "velocity_" << _timestepCounter << ".txt";
234 std::ofstream file(ss.str().c_str());
235 if (!file.is_open()) {
236 std::cout << "ERROR NumericalSolver::plottxt(): Could not open file " << ss.str() << "!" << std::endl;
237 exit(EXIT_FAILURE);
238 }
239 std::stringstream velocity;
240
241 // loop over domain (incl. boundary)
242 double y = _channelheight / 2;
243 double x = _channelheight / 2;
244 for (double z = _dx / 2; z < _channelheight; z = z + _dx) {
245 const int foamIndice = U.mesh().findCell(Foam::vector(x, y, z));
246 // write information to streams
247 if (foamIndice > 0) {
248 velocity << U[foamIndice][0] << ", " << U[foamIndice][1] << ", " << U[foamIndice][2] << std::endl;
249 }
250 }
251 file << velocity.str() << std::endl;
252 file.close();
253 }
254
256 void plot() const {
257 // only plot output if this is the correct timestep
258 if (_plotEveryTimestep < 1) {
259 return;
260 }
261 if (_timestepCounter % _plotEveryTimestep != 0) {
262 return;
263 }
264
265 std::stringstream ss;
266 ss << "Continuum_Velocity_IcoFoam_" << _rank << "_" << _timestepCounter << ".vtk";
267 std::ofstream file(ss.str().c_str());
268 if (!file.is_open()) {
269 std::cout << "ERROR NumericalSolver::plot(): Could not open file " << ss.str() << "!" << std::endl;
270 exit(EXIT_FAILURE);
271 }
272 std::stringstream velocity;
273
274 file << "# vtk DataFile Version 2.0" << std::endl;
275 file << "MaMiCo FoamSolver" << std::endl;
276 file << "ASCII" << std::endl << std::endl;
277 file << "DATASET UNSTRUCTURED_GRID" << std::endl;
278 // file << "DATASET STRUCTURED_GRID" << std::endl;
279 // int pointsPerDimension = 20;//U.mesh().nCells();
280 // file << "DIMENSIONS " << pointsPerDimension << " " << pointsPerDimension << " " << pointsPerDimension << std::endl; // everything +1 cause of change in
281 // index
282 file << "POINTS " << U.mesh().nCells() << " float" << std::endl;
283
284 velocity << std::setprecision(12);
285 velocity << "POINT_DATA " << U.mesh().nCells() << std::endl;
286 velocity << "VECTORS velocity float" << std::endl;
287
288 // loop over domain (incl. boundary)
289 int size = U.size();
290 for (int i = 0; i < size; i++) {
291 // write information to streams;
292 file << U.mesh().C()[i][0] << " " << U.mesh().C()[i][1] << " " << U.mesh().C()[i][2] << std::endl; // boundary is missing, how to include? Just not do?
293 velocity << U[i][0] << " " << U[i][1] << " " << U[i][2] << std::endl;
294 }
295
296 file << std::endl;
297 file << velocity.str() << std::endl;
298 velocity.str("");
299 file.close();
300 }
301
302 // The solver runs sequentially on rank 0. Therefore the function checks if the acutal rank is zero.
303 bool skipRank() const { return !(_rank == 0); }
304
305 // the following are original OpenFOAM variables, their names shall not be changed
306 Foam::Time runTime;
307 Foam::fvMesh mesh;
308 Foam::IOdictionary transportProperties;
309 Foam::dimensionedScalar nu;
310 Foam::volScalarField p;
311 Foam::volVectorField U;
312 Foam::surfaceScalarField phi;
313 Foam::pisoControl piso;
314 // this are additional variables, they can be changed
315 // the entries define which boundaries are for coupling with MD
316 // 0 means no MD boundary and 1 means MD boundary
317 tarch::la::Vector<12, unsigned int> _boundariesWithMD;
318 float _dx; // mesh size
319 double _channelheight; // overall height of the Couette channel
320 std::multimap<I00, unsigned int> _boundaryPointMap;
321 std::vector<tarch::la::Vector<3, double>> _boundaryPointsOuter;
322 I00* _boundaryIndicesInner{nullptr}; // pointer to an array with data for communication
323 Foam::vector** _boundaryIndices{nullptr}; // pointer to OpenFOAM data for communication
324 int _rank; // rank of the actual process
325 int _plotEveryTimestep; // every n-th time step should be plotted
326 int _timestepCounter{0}; // actual time step number
327 // the following are original OpenFOAM variables, their names shall not be changed
328 Foam::label pRefCell{0};
329 Foam::scalar pRefValue{0.0};
330 size_t _numberBoundaryPoints{0}; // the number of CFD boundary points which need data from the MD
331 tarch::la::Vector<3, double> _uWall;
332};
333#endif // _COUPLING_SOLVERS_IcoFoamInterface_H_
const tarch::la::Vector< dim, double > & getMacroscopicMomentum() const
Definition CouplingCell.h:64
const double & getMacroscopicMass() const
Definition CouplingCell.h:58
interface for continuum/macro fluid solvers for the Couette scenario
Definition CouetteSolver.h:19
Definition IcoFoamInterface.h:34
void plot() const
Definition IcoFoamInterface.h:256
void plottxt() const
Definition IcoFoamInterface.h:229
void advance(double dt) override
advances the solver in time
Definition IcoFoamInterface.h:70
Definition Vector.h:24
all numerical solvers are defined in the namespace, and their interfaces
Definition CouetteSolver.h:14
everything necessary for coupling operations, is defined in here
Definition AdditiveMomentumInsertion.h:15