CFD Online Logo CFD Online URL
www.cfd-online.com
[Sponsors]
Home > Forums > Software User Forums > OpenFOAM > OpenFOAM Programming & Development

Data exchange for internal field values between two different mesh

Register Blogs Community New Posts Updated Threads Search

Like Tree6Likes
  • 6 Post By ch1

Reply
 
LinkBack Thread Tools Search this Thread Display Modes
Old   September 25, 2019, 07:54
Default Data exchange for internal field values between two different mesh
  #1
Senior Member
 
Join Date: Jan 2012
Posts: 197
Rep Power: 14
itsme_kit is on a distinguished road
Hi OpenFOAMers,

My problem is similar to these two threads:

How to get cell indices and manipulate field data

runTime mapFields from fineMesh to coarseMesh

I want to do data exchange for internal field values between two identical meshes with different mesh density.

Each mesh has own solver. For example, solver1 gets results of velocity and sends them to solver2 for temperature calculation. And temperature is returned to solver1 for velocity calculation, the loop continues like this.

Those two threads make use of the code of mapfields and implement it in solver.

I'm not sure if I need to merge those two solvers as a new solver before using mapfields code in the solver.

Where to the put the code in the solver?

Similar work in OpenFOAM has been found in journal paper, it states that "The computations on the two meshes are fully
coupled, meaning that data (temperature from macro- to meso-,
and solid fraction from meso- to macroscale) is exchanged in both
directions at every time step via a linear interpolation framework"

I'm not sure how the author implement the data exchange, limited information provided in the paper.

Thanks in advance.
itsme_kit is offline   Reply With Quote

Old   September 26, 2019, 09:22
Default
  #2
ch1
New Member
 
Christian
Join Date: Jun 2019
Posts: 14
Rep Power: 7
ch1 is on a distinguished road
Hello,
with "two identical meshes with different mesh density" you mean that they have the same geometry but different mesh refinement, right? Which version of OpenFOAM do you use?

In my thread, which you refer to, I used foam-extend 4.0. So for that I can give you some hints.

To use a second mesh I do the following (code can be inserted after e.g. #include "createTime.H" ):


Code:
        
    fvMesh emMesh
    (
        Foam::IOobject
        (
            word("emRegion"),
            runTime.timeName(),
            runTime,
            IOobject::MUST_READ
        )
    );

    fvMesh mesh //keep default variable name to avoid compilation errors with the default fluid solvers
    (
        Foam::IOobject
        (
            word("fluidRegion"),
            runTime.timeName(),
            runTime,
            IOobject::MUST_READ
        )
    );
This implies that the case folder has got roughly the following structure.

../caseName/
|
|-- 0/
| |-- emRegion/ --> initial conditions T, U, p,...
| |-- fluidRegion/ --> initial conditions T, U, p,...
|
|-- const/
| |-- emRegion/polyMesh/ --> emMesh
| |-- fluidRegion/polyMesh/ --> mesh (fluidMesh)
|-- system/
| |-- emRegion/ --> fvSolution, fvSchemes
| |-- fluidRegion/ --> fvSolution, fvSchemes

In your case, the "emMesh" is where you want to solve the temperature equation and the default "mesh" is where the fluid equations are solved.

In createFields.H of your solver, you should assign the fields to the corresponding meshes.
A field in the e.g. "emMesh" would be like

Code:
    volScalarField A
    (
        IOobject
        (
            "A",
            runTime.timeName(),
            emMesh,
            IOobject::MUST_READ,
            IOobject::AUTO_WRITE
        ),
        emMesh
    );
The standard fluid fields eventually may remain unchanged like

Code:
    volScalarField T
    (
        IOobject
        (
            "T",
            runTime.timeName(),
            mesh,
            IOobject::MUST_READ,
            IOobject::AUTO_WRITE
        ),
        mesh
    );
However, if a field happens to appear in both meshes and you want to map that field between the meshes, I recommend to do the following:
Code:
    volScalarField Tfluid
    (
        IOobject
        (
            "T",
            runTime.timeName(),
            mesh,
            IOobject::MUST_READ,
            IOobject::AUTO_WRITE
        ),
        mesh
    );

    volScalarField Tem
    (
        IOobject
        (
            "T",
            runTime.timeName(),
            emMesh,
            IOobject::MUST_READ,
            IOobject::AUTO_WRITE
        ),
        emMesh
    );
So it will be clear from the code, to which mesh the variable is assigned.


The last thing you need to know is how to properly map + interpolate the field between the meshes at run time.

This code can be executed after #include "createTime.H" as well:

Code:
// read the mapFieldsDict located in /case/system
IOdictionary mapFieldsDict
(
	IOobject
	(
		"mapFieldsDict",
		runTime.system(),
		runTime,
		IOobject::MUST_READ,
		IOobject::NO_WRITE,
		false
	)
);

HashTable<word> patchMap;
wordList cuttingPatches;
mapFieldsDict.lookup("patchMap") >> patchMap;
mapFieldsDict.lookup("cuttingPatches") >>  cuttingPatches;
Just create an empty mapFieldsDict file and put it into the system directory:

Code:
FoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    object      mapFieldsDict;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

// List of pairs of source/target patches for mapping
patchMap
(
);

// List of target patches cutting the source domain (these need to be
// handled specially e.g. interpolated from internal values)
cuttingPatches
(
);

And here is a code example of map + interpolation, which will be executed at runtime:

Code:
// specify source and target mesh for the mapping
const fvMesh& meshSource = emMesh; //meshToMeshInterp.fromMesh(); 
const fvMesh& meshTarget = mesh; //meshToMeshInterp.toMesh();     

// Create the interpolation scheme
meshToMesh meshToMeshInterp
(
    meshSource,
    meshTarget,
    patchMap,
    cuttingPatches
);

Info<< "Target mesh size: " << meshTarget.nCells() << endl;

Info<< nl
    << "Mapping fields for time " << meshSource.time().timeName()
    << nl << endl;

// Interpolate field
meshToMeshInterp.interpolate
(
    Tfluid, //target field
    Tem, //source field
    meshToMesh::INTERPOLATE
);
I guess that's everything you need to get started.
Note, that I encountered some "undesired extrapolation bug", which I mentioned previously here in this thread and which I could not resolve yet. But this problem should not occur in your case, because you mentioned that your geometry is consistent.

Best regards,
Christian
ch1 is offline   Reply With Quote

Old   October 4, 2019, 09:28
Default
  #3
Member
 
Fabien Robaux
Join Date: Oct 2016
Posts: 51
Rep Power: 10
frobaux is on a distinguished road
Wow,

Thank you for that complete explanation, I wish I had it when I started to write mine!
Everything is in there!

I just want to add two things.

First, I use

Code:
meshToMeshInterp.mapSrcToTgt(srcField,  plusEqOp<scalar>(), tgtField)
instead of "meshToMeshInterp.interpolate(..)" (Probably different version, I am on V17.12+)
Second, I am pretty sure that it is not what we call fully coupled.

Correct me if I am wrong, but fully coupled means that only one matrice is inverted and describe the entire problem. like Ax=B with x beeing ALL unknowns (both T in the coarse mesh and solid fraction in the finer). On the contrary, a normal coupling would have a loop solving for one then for the other, until convergence is reached.

But I would have no clue on how to implement a fully coupling in OpenFOAM.
frobaux is offline   Reply With Quote

Old   October 7, 2019, 05:25
Default
  #4
ch1
New Member
 
Christian
Join Date: Jun 2019
Posts: 14
Rep Power: 7
ch1 is on a distinguished road
Quote:
Originally Posted by frobaux View Post
Wow,

Thank you for that complete explanation, I wish I had it when I started to write mine!
Everything is in there!

I just want to add two things.

First, I use

Code:
meshToMeshInterp.mapSrcToTgt(srcField,  plusEqOp<scalar>(), tgtField)
instead of "meshToMeshInterp.interpolate(..)" (Probably different version, I am on V17.12+)
Second, I am pretty sure that it is not what we call fully coupled.

Correct me if I am wrong, but fully coupled means that only one matrice is inverted and describe the entire problem. like Ax=B with x beeing ALL unknowns (both T in the coarse mesh and solid fraction in the finer). On the contrary, a normal coupling would have a loop solving for one then for the other, until convergence is reached.

But I would have no clue on how to implement a fully coupling in OpenFOAM.

Yes, this command might be different due to different OF versions. I am using foam-extend 4.0 and if you want to implement fully-coupled solvers you can find it there too. The library is called fvBlockMatrix.H. See also pUCoupledFOAM in foam-extend to get started.

I don't know what itsme_kit is doing with the field variables or how the equations look like. So maybe what was meant with "fully coupled" is that the coupling is bidirectional but if the coupling is nonlinear you cannot solve it as a fully coupled matrix, so a segregated approach is needed.
ch1 is offline   Reply With Quote

Old   February 5, 2020, 12:40
Default
  #5
Senior Member
 
Join Date: Jan 2012
Posts: 197
Rep Power: 14
itsme_kit is on a distinguished road
Hi Christian

Thanks for your detailed explanation.

I'm back to CFD recently.

I will look into your suggestion immediately and let you know what I got.

Thanks a million again!



Quote:
Originally Posted by ch1 View Post
Yes, this command might be different due to different OF versions. I am using foam-extend 4.0 and if you want to implement fully-coupled solvers you can find it there too. The library is called fvBlockMatrix.H. See also pUCoupledFOAM in foam-extend to get started.

I don't know what itsme_kit is doing with the field variables or how the equations look like. So maybe what was meant with "fully coupled" is that the coupling is bidirectional but if the coupling is nonlinear you cannot solve it as a fully coupled matrix, so a segregated approach is needed.
itsme_kit is offline   Reply With Quote

Old   February 6, 2020, 12:38
Default
  #6
Senior Member
 
Join Date: Jan 2012
Posts: 197
Rep Power: 14
itsme_kit is on a distinguished road
Hi Christian

I completely followed your procedure, it is fully working after several trials.

Thanks very much.

There is one potential shortcoming in this procedure I wish to discuss:
Solver2 has to wait for execution until solver1 completes its job.

I wonder if we could execute these two solvers simultaneously, for example, solver1 solves and maps fields into solver2 and solver2 solves fields at each time step.

Looking forward to hearing from you.

Best Regards,

Ke



Quote:
Originally Posted by ch1 View Post
Yes, this command might be different due to different OF versions. I am using foam-extend 4.0 and if you want to implement fully-coupled solvers you can find it there too. The library is called fvBlockMatrix.H. See also pUCoupledFOAM in foam-extend to get started.

I don't know what itsme_kit is doing with the field variables or how the equations look like. So maybe what was meant with "fully coupled" is that the coupling is bidirectional but if the coupling is nonlinear you cannot solve it as a fully coupled matrix, so a segregated approach is needed.
itsme_kit is offline   Reply With Quote

Old   May 25, 2020, 17:15
Default
  #7
ch1
New Member
 
Christian
Join Date: Jun 2019
Posts: 14
Rep Power: 7
ch1 is on a distinguished road
Hey Ke,

sorry for my late response. So if I understand correctly you mean an implementation like this:

Code:
while (runTime.run())
{

eqnSolverA();

mapFieldsFromAtoB();

eqnSolverB();

}
Actually, this should work without a problem if you integrate the code of your two solvers, which at the moment are separate applications if I got you correctly, into a single application, which will be your new combined solver.

Maybe you can share your code one more time, so I can better understand the problem and what you are trying to achieve.

Best regards,
Christian
ch1 is offline   Reply With Quote

Old   January 24, 2021, 23:17
Default
  #8
Senior Member
 
Join Date: Jan 2012
Posts: 197
Rep Power: 14
itsme_kit is on a distinguished road
Hi Christian

I have completed the mapping between two solvers in one code as you suggested.

However, my case needs a little bit more complexity, for example, solverA runs at timestep T=0.0001, and solverB runs at timestep T=0.01, so the mapping should be implemented at every T=0.01.

I really appreciate any idea of doing mapping in this way.

Many thanks



Quote:
Originally Posted by ch1 View Post
Hey Ke,

sorry for my late response. So if I understand correctly you mean an implementation like this:

Code:
while (runTime.run())
{

eqnSolverA();

mapFieldsFromAtoB();

eqnSolverB();

}
Actually, this should work without a problem if you integrate the code of your two solvers, which at the moment are separate applications if I got you correctly, into a single application, which will be your new combined solver.

Maybe you can share your code one more time, so I can better understand the problem and what you are trying to achieve.

Best regards,
Christian
itsme_kit is offline   Reply With Quote

Old   July 30, 2023, 13:54
Default
  #9
Senior Member
 
Reviewer #2
Join Date: Jul 2015
Location: Knoxville, TN
Posts: 141
Rep Power: 11
randolph is on a distinguished road
Quote:
Originally Posted by ch1 View Post
Hello,
with "two identical meshes with different mesh density" you mean that they have the same geometry but different mesh refinement, right? Which version of OpenFOAM do you use?

In my thread, which you refer to, I used foam-extend 4.0. So for that I can give you some hints.

To use a second mesh I do the following (code can be inserted after e.g. #include "createTime.H" ):


Code:
        
    fvMesh emMesh
    (
        Foam::IOobject
        (
            word("emRegion"),
            runTime.timeName(),
            runTime,
            IOobject::MUST_READ
        )
    );

    fvMesh mesh //keep default variable name to avoid compilation errors with the default fluid solvers
    (
        Foam::IOobject
        (
            word("fluidRegion"),
            runTime.timeName(),
            runTime,
            IOobject::MUST_READ
        )
    );
This implies that the case folder has got roughly the following structure.

../caseName/
|
|-- 0/
| |-- emRegion/ --> initial conditions T, U, p,...
| |-- fluidRegion/ --> initial conditions T, U, p,...
|
|-- const/
| |-- emRegion/polyMesh/ --> emMesh
| |-- fluidRegion/polyMesh/ --> mesh (fluidMesh)
|-- system/
| |-- emRegion/ --> fvSolution, fvSchemes
| |-- fluidRegion/ --> fvSolution, fvSchemes

In your case, the "emMesh" is where you want to solve the temperature equation and the default "mesh" is where the fluid equations are solved.

In createFields.H of your solver, you should assign the fields to the corresponding meshes.
A field in the e.g. "emMesh" would be like

Code:
    volScalarField A
    (
        IOobject
        (
            "A",
            runTime.timeName(),
            emMesh,
            IOobject::MUST_READ,
            IOobject::AUTO_WRITE
        ),
        emMesh
    );
The standard fluid fields eventually may remain unchanged like

Code:
    volScalarField T
    (
        IOobject
        (
            "T",
            runTime.timeName(),
            mesh,
            IOobject::MUST_READ,
            IOobject::AUTO_WRITE
        ),
        mesh
    );
However, if a field happens to appear in both meshes and you want to map that field between the meshes, I recommend to do the following:
Code:
    volScalarField Tfluid
    (
        IOobject
        (
            "T",
            runTime.timeName(),
            mesh,
            IOobject::MUST_READ,
            IOobject::AUTO_WRITE
        ),
        mesh
    );

    volScalarField Tem
    (
        IOobject
        (
            "T",
            runTime.timeName(),
            emMesh,
            IOobject::MUST_READ,
            IOobject::AUTO_WRITE
        ),
        emMesh
    );
So it will be clear from the code, to which mesh the variable is assigned.


The last thing you need to know is how to properly map + interpolate the field between the meshes at run time.

This code can be executed after #include "createTime.H" as well:

Code:
// read the mapFieldsDict located in /case/system
IOdictionary mapFieldsDict
(
	IOobject
	(
		"mapFieldsDict",
		runTime.system(),
		runTime,
		IOobject::MUST_READ,
		IOobject::NO_WRITE,
		false
	)
);

HashTable<word> patchMap;
wordList cuttingPatches;
mapFieldsDict.lookup("patchMap") >> patchMap;
mapFieldsDict.lookup("cuttingPatches") >>  cuttingPatches;
Just create an empty mapFieldsDict file and put it into the system directory:

Code:
FoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    object      mapFieldsDict;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

// List of pairs of source/target patches for mapping
patchMap
(
);

// List of target patches cutting the source domain (these need to be
// handled specially e.g. interpolated from internal values)
cuttingPatches
(
);

And here is a code example of map + interpolation, which will be executed at runtime:

Code:
// specify source and target mesh for the mapping
const fvMesh& meshSource = emMesh; //meshToMeshInterp.fromMesh(); 
const fvMesh& meshTarget = mesh; //meshToMeshInterp.toMesh();     

// Create the interpolation scheme
meshToMesh meshToMeshInterp
(
    meshSource,
    meshTarget,
    patchMap,
    cuttingPatches
);

Info<< "Target mesh size: " << meshTarget.nCells() << endl;

Info<< nl
    << "Mapping fields for time " << meshSource.time().timeName()
    << nl << endl;

// Interpolate field
meshToMeshInterp.interpolate
(
    Tfluid, //target field
    Tem, //source field
    meshToMesh::INTERPOLATE
);
I guess that's everything you need to get started.
Note, that I encountered some "undesired extrapolation bug", which I mentioned previously here in this thread and which I could not resolve yet. But this problem should not occur in your case, because you mentioned that your geometry is consistent.

Best regards,
Christian
Christian,

I follow your approach (trying to define a volScalarField on the second mesh) but an error complains that the emMesh was not declared in this scope. Any suggestions?

Thanks,

volScalarField Tem
(
IOobject
(
"T",
runTime.timeName(),
emMesh,
IOobject::MUST_READ,
IOobject::AUTO_WRITE
),
emMesh
);
randolph is offline   Reply With Quote

Old   September 5, 2023, 07:10
Default
  #10
New Member
 
Luke Hirl
Join Date: Jul 2023
Location: Gießen
Posts: 16
Rep Power: 3
Luke99 is on a distinguished road
Quote:
Originally Posted by randolph View Post
Christian,

I follow your approach (trying to define a volScalarField on the second mesh) but an error complains that the emMesh was not declared in this scope. Any suggestions?

Thanks,

volScalarField Tem
(
IOobject
(
"T",
runTime.timeName(),
emMesh,
IOobject::MUST_READ,
IOobject::AUTO_WRITE
),
emMesh
);

I am currently facing the same issue. Did someone find a solution to this yet? It also happens if you just want to rename "mesh" to something else.


EDIT:
I might be wrong but from what I understand it would mean that I would have to declare emMesh (or whatever you call your 2nd mesh) outside of the main function. Yet the question is where and how does this happen for mesh, that might give insights into how to fix this issue. For the record I encountered this problem while trying to add a second dynamic mesh to interfoam, but even a static mesh throws the same error.

Last edited by Luke99; September 5, 2023 at 12:11.
Luke99 is offline   Reply With Quote

Old   September 11, 2023, 11:33
Default Solution?!
  #11
New Member
 
Luke Hirl
Join Date: Jul 2023
Location: Gießen
Posts: 16
Rep Power: 3
Luke99 is on a distinguished road
For everyone facing the same issue:
As we know it is possible to have multiple meshes in solvers like chtMultiRegionFoam i poked and modified it and came to following result:


if you create your multpile meshes in createMeshes.H(or wherever you create them) as given above or in the same manner as done in chtMultiRegionFoam just add the following line at the start of your main loop:
Code:
#define CREATE_MESH createMeshesPostProcess.H
inside createMeshesPostProcess.H or however you might call it, write:
Code:
#include "createMeshes.H"
I don`t know if this is a proper solution, but it works for my solver in OpenFOAM2212
This also works for one of the meshes being a dynamicFvMesh& insted of fvMesh&. I haven´t tested if it will work for more though, but i don't see a problem there.


Edit: I realised that runtime data transfer would be too time consuming so I will not be following up on it

Last edited by Luke99; September 12, 2023 at 13:17.
Luke99 is offline   Reply With Quote

Reply


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are Off
Pingbacks are On
Refbacks are On


Similar Threads
Thread Thread Starter Forum Replies Last Post
Transfering data from a Coarse mesh to a Fine mesh as initial values amin.z OpenFOAM Running, Solving & CFD 1 April 1, 2017 17:59
Cht tutorial in 15 braennstroem OpenFOAM Running, Solving & CFD 197 June 10, 2015 04:02
''unknown radialModelType type Gidaspow'' PROBLEM WITH THE BED TUTORIAL AndoniBM OpenFOAM Running, Solving & CFD 2 March 25, 2015 19:44
Mesh refinement with Field Functions - error Dan Chambers STAR-CCM+ 7 January 30, 2014 09:01
[Gmsh] 2D Mesh Generation Tutorial for GMSH aeroslacker OpenFOAM Meshing & Mesh Conversion 12 January 19, 2012 04:52


All times are GMT -4. The time now is 13:00.