15th OpenFOAM Workshop - June 24, 2020 - Training III-E

Multiphysics Modeling with the preCICE Coupling Library

Gerasimos Chourdakis, Technical University of Munich
chourdak@in.tum.de

Benjamin Uekermann, Eindhoven University of Technology
b.w.uekermann@tue.nl

Uncoupled simulation

Import preCICE


                        import numpy
                        
                        
                        # generate mesh
                        n = 20
                        y = numpy.linspace(0, 1, n + 1)

                        dt = 0.01
                        t = 0

                        while True:
                          print("Generating data")
                          u = 1 - 2 * numpy.random.rand(n)
                          
                          t = t + dt 
                          if(t > 0.1):
                            break
                    

Import preCICE


                        import numpy
                        import precice
                        
                        # generate mesh
                        n = 20
                        y = numpy.linspace(0, 1, n + 1)

                        dt = 0.01
                        t = 0

                        while True:
                          print("Generating data")
                          u = 1 - 2 * numpy.random.rand(n)
                          
                          t = t + dt 
                          if(t > 0.1):
                            break
                    

Configure preCICE


                        import numpy
                        import precice
                        
                        # generate mesh
                        n = 20
                        y = numpy.linspace(0, 1, n + 1)

                        # preCICE setup
                        participant_name     = "Generator"
                        config_file_name     = "precice-config.xml"
                        solver_process_index = 0
                        solver_process_size  = 1
                        interface = 
                            precice.Interface(  
                                               participant_name,
                                               config_file_name,
                                               solver_process_index,
                                               solver_process_size
                                             )

                        dt = 0.01
                        t = 0

                        while True:
                          print("Generating data")
                          u = 1 - 2 * numpy.random.rand(n)
                          
                          t = t + dt 
                          if(t > 0.1):
                            break
                    

Define the coupling mesh


                        import numpy
                        import precice
                        
                        # generate mesh
                        n = 20
                        y = numpy.linspace(0, 1, n + 1)

                        # preCICE setup
                        participant_name     = "Generator"
                        config_file_name     = "precice-config.xml"
                        solver_process_index = 0
                        solver_process_size  = 1
                        interface = 
                            precice.Interface(  
                                               participant_name,
                                               config_file_name,
                                               solver_process_index,
                                               solver_process_size
                                             )
                        
                        # Get the preCICE mesh id
                        mesh_name  = "Generator-Mesh"
                        mesh_id    = interface.get_mesh_id(mesh_name)
                        
                        # Define the coupling mesh
                        vertices   = [[1, y0] for y0 in y[:-1]]
                        vertex_ids = interface.set_mesh_vertices(mesh_id, vertices)
                        
                        dt = 0.01
                        t = 0

                        while True:
                          print("Generating data")
                          u = 1 - 2 * numpy.random.rand(n)
                          
                          t = t + dt 
                          if(t > 0.1):
                            break
                    

Initialize and finalize preCICE


                        import numpy
                        import precice
                        
                        # generate mesh
                        n = 20
                        y = numpy.linspace(0, 1, n + 1)

                        # preCICE setup
                        participant_name     = "Generator"
                        config_file_name     = "precice-config.xml"
                        solver_process_index = 0
                        solver_process_size  = 1
                        interface = 
                            precice.Interface(  
                                               participant_name,
                                               config_file_name,
                                               solver_process_index,
                                               solver_process_size
                                             )
                        
                        # Get the preCICE mesh id
                        mesh_name  = "Generator-Mesh"
                        mesh_id    = interface.get_mesh_id(mesh_name)
                        
                        # Define the coupling mesh
                        vertices   = [[1, y0] for y0 in y[:-1]]
                        vertex_ids = interface.set_mesh_vertices(mesh_id, vertices)
                        
                        interface.initialize()

                        dt = 0.01
                        t = 0

                        while True:
                          print("Generating data")
                          u = 1 - 2 * numpy.random.rand(n)
                          
                          t = t + dt 
                          if(t > 0.1):
                            break
                        
                        interface.finalize()
                    

Advance the coupling


                        import numpy
                        import precice
                        
                        # generate mesh
                        n = 20
                        y = numpy.linspace(0, 1, n + 1)

                        # preCICE setup
                        participant_name     = "Generator"
                        config_file_name     = "precice-config.xml"
                        solver_process_index = 0
                        solver_process_size  = 1
                        interface = 
                            precice.Interface(  
                                               participant_name,
                                               config_file_name,
                                               solver_process_index,
                                               solver_process_size
                                             )
                                             
                        # Get the preCICE mesh id
                        mesh_name  = "Generator-Mesh"
                        mesh_id    = interface.get_mesh_id(mesh_name)
                         
                        # Define the coupling mesh
                        vertices   = [[1, y0] for y0 in y[:-1]]
                        vertex_ids = interface.set_mesh_vertices(mesh_id, vertices)
                        
                        interface.initialize()

                        dt = 0.01
                        t = 0

                        while True:
                          print("Generating data")
                          u = 1 - 2 * numpy.random.rand(n)
                          
                          
                          
                          t = t + dt 
                          if(t > 0.1):
                            break
                        
                        interface.finalize()
                    

Advance the coupling


                        import numpy
                        import precice
                        
                        # generate mesh
                        n = 20
                        y = numpy.linspace(0, 1, n + 1)

                        # preCICE setup
                        participant_name     = "Generator"
                        config_file_name     = "precice-config.xml"
                        solver_process_index = 0
                        solver_process_size  = 1
                        interface = 
                            precice.Interface(  
                                               participant_name,
                                               config_file_name,
                                               solver_process_index,
                                               solver_process_size
                                             )
                        
                         # Get the preCICE mesh id
                         mesh_name  = "Generator-Mesh"
                         mesh_id    = interface.get_mesh_id(mesh_name)
                         
                         # Define the coupling mesh
                         vertices   = [[1, y0] for y0 in y[:-1]]
                         vertex_ids = interface.set_mesh_vertices(mesh_id, vertices)
                        
                        interface.initialize()

                        dt = 0.01
                        t = 0

                        while True:
                          print("Generating data")
                          u = 1 - 2 * numpy.random.rand(n)
                          
                          interface.advance(dt)
                          
                          t = t + dt 
                          if(t > 0.1):
                            break
                        
                        interface.finalize()
                    

Advance the coupling


                        import numpy
                        import precice
                        
                        # generate mesh
                        n = 20
                        y = numpy.linspace(0, 1, n + 1)

                        # preCICE setup
                        participant_name     = "Generator"
                        config_file_name     = "precice-config.xml"
                        solver_process_index = 0
                        solver_process_size  = 1
                        interface = 
                            precice.Interface(  
                                               participant_name,
                                               config_file_name,
                                               solver_process_index,
                                               solver_process_size
                                             )
                        
                        # Get the preCICE mesh id
                        mesh_name  = "Generator-Mesh"
                        mesh_id    = interface.get_mesh_id(mesh_name)
                        
                        # Define the coupling mesh
                        vertices   = [[1, y0] for y0 in y[:-1]]
                        vertex_ids = interface.set_mesh_vertices(mesh_id, vertices)
                        
                        interface.initialize()

                        dt = 0.01
                        t = 0

                        while interface.is_coupling_ongoing():
                          print("Generating data")
                          u = 1 - 2 * numpy.random.rand(n)
                          
                          interface.advance(dt)
                          
                          t = t + dt 
                        
                        
                        
                        interface.finalize()
                    

Advance the coupling


                        import numpy
                        import precice
                        
                        # generate mesh
                        n = 20
                        y = numpy.linspace(0, 1, n + 1)

                        # preCICE setup
                        participant_name     = "Generator"
                        config_file_name     = "precice-config.xml"
                        solver_process_index = 0
                        solver_process_size  = 1
                        interface = 
                            precice.Interface(  
                                               participant_name,
                                               config_file_name,
                                               solver_process_index,
                                               solver_process_size
                                             )
                        
                        # Get the preCICE mesh id
                        mesh_name  = "Generator-Mesh"
                        mesh_id    = interface.get_mesh_id(mesh_name)
                        
                        # Define the coupling mesh
                        vertices   = [[1, y0] for y0 in y[:-1]]
                        vertex_ids = interface.set_mesh_vertices(mesh_id, vertices)
                        
                        interface.initialize()

                        dt = 0.01
                        t = 0

                        while interface.is_coupling_ongoing():
                          print("Generating data")
                          u = 1 - 2 * numpy.random.rand(n)
                          
                          precice_dt = interface.advance(dt)
                          
                          t = t + dt 
                        
                        
                        
                        interface.finalize()
                    

Advance the coupling


                        import numpy
                        import precice
                        
                        # generate mesh
                        n = 20
                        y = numpy.linspace(0, 1, n + 1)

                        # preCICE setup
                        participant_name     = "Generator"
                        config_file_name     = "precice-config.xml"
                        solver_process_index = 0
                        solver_process_size  = 1
                        interface = 
                            precice.Interface(  
                                               participant_name,
                                               config_file_name,
                                               solver_process_index,
                                               solver_process_size
                                             )
                        
                        # Get the preCICE mesh id
                        mesh_name  = "Generator-Mesh"
                        mesh_id    = interface.get_mesh_id(mesh_name)
                        
                        # Define the coupling mesh
                        vertices   = [[1, y0] for y0 in y[:-1]]
                        vertex_ids = interface.set_mesh_vertices(mesh_id, vertices)
                        
                        precice_dt = interface.initialize()

                        dt = 0.01
                        t = 0

                        while interface.is_coupling_ongoing():
                          dt = np.minimum(dt, precice_dt)
                          
                          print("Generating data")
                          u = 1 - 2 * numpy.random.rand(n)
                          
                          precice_dt = interface.advance(dt)
                          
                          t = t + dt 
                        
                        interface.finalize()
                    
Not there yet, but let's run it
(similar changes in propagator.py - try it at home)

Nothing happening?

Did we forget something?

Write & read data


                        import numpy
                        import precice
                        
                        # generate mesh
                        n = 20
                        y = numpy.linspace(0, 1, n + 1)

                        # preCICE setup
                        participant_name     = "Generator"
                        config_file_name     = "precice-config.xml"
                        solver_process_index = 0
                        solver_process_size  = 1
                        interface = 
                            precice.Interface(  
                                               participant_name,
                                               config_file_name,
                                               solver_process_index,
                                               solver_process_size
                                             )
                        
                        # Get the preCICE mesh id
                        mesh_name  = "Generator-Mesh"
                        mesh_id    = interface.get_mesh_id(mesh_name)
                         
                        # Define the coupling mesh
                        vertices   = [[1, y0] for y0 in y[:-1]]
                        vertex_ids = interface.set_mesh_vertices(mesh_id, vertices)
                        



                        
                        precice_dt = interface.initialize()

                        dt = 0.01
                        t = 0

                        while interface.is_coupling_ongoing():
                          dt = np.minimum(dt, precice_dt)
                          
                          print("Generating data")
                          u = 1 - 2 * numpy.random.rand(n)
                          

                          precice_dt = interface.advance(dt)

                          
                          t = t + dt 
                        
                        interface.finalize()
                    

Write & read data


                        import numpy
                        import precice
                        
                        # generate mesh
                        n = 20
                        y = numpy.linspace(0, 1, n + 1)

                        # preCICE setup
                        participant_name     = "Generator"
                        config_file_name     = "precice-config.xml"
                        solver_process_index = 0
                        solver_process_size  = 1
                        interface = 
                            precice.Interface(  
                                               participant_name,
                                               config_file_name,
                                               solver_process_index,
                                               solver_process_size
                                             )
                        
                        # Get the preCICE mesh id
                        mesh_name  = "Generator-Mesh"
                        mesh_id    = interface.get_mesh_id(mesh_name)
                         
                        # Define the coupling mesh
                        vertices   = [[1, y0] for y0 in y[:-1]]
                        vertex_ids = interface.set_mesh_vertices(mesh_id, vertices)
                        
                        # Get the exchanged data id
                        data_name = "Data"
                        data_id = interface.get_data_id(data_name, mesh_id)
                        
                        precice_dt = interface.initialize()

                        dt = 0.01
                        t = 0

                        while interface.is_coupling_ongoing():
                          dt = np.minimum(dt, precice_dt)
                          
                          print("Generating data")
                          u = 1 - 2 * numpy.random.rand(n)
                          
                          interface.write_block_scalar_data(data_id, vertex_ids, u)
                          precice_dt = interface.advance(dt)
                          # interface.read_block_scalar_data(data_id, vertex_ids, u)
                          
                          t = t + dt 
                        
                        interface.finalize()
                    
It should work now!

Data is being transfered!

Most basic case: uni-directional, serial-explicit, nearest-neighbor mapping, ...

Side note: Subcycling

preCICE configuration file

Visual representation of precice-config.xml using the config visualizer: Visual representation of the precice-config.xml
precice-config.xml:

                        <precice-configuration>
                          <solver-interface dimensions="2">

                          <data:scalar name="Data"/>

                          <mesh name="Generator-Mesh">
                            <use-data name="Data"/>
                          </mesh>

                          <mesh name="Propagator-Mesh">
                            <use-data name="Data" />
                          </mesh>
                          
                          <participant name="Generator">
                            <use-mesh name="Generator-Mesh" provide="yes"/>
                            <write-data name="Data" mesh="Generator-Mesh"/>
                          </participant>

                          <participant name="Propagator">
                            <use-mesh name="Generator-Mesh" from="Generator"/>
                            <use-mesh name="Propagator-Mesh" provide="yes"/>
                            <mapping:nearest-neighbor direction="read"
                              from="Generator-Mesh" to="Propagator-Mesh"
                              constraint="consistent"/>
                            <read-data name="Data" mesh="Propagator-Mesh" />
                          </participant>    

                          <m2n:sockets from="Generator" to="Propagator"/>

                          <coupling-scheme:serial-explicit>
                            <participants first="Generator" second="Propagator"/>
                            <time-window-size value="0.01"/>
                            <max-time value="0.1"/>
                            <exchange data="Data" mesh="Generator-Mesh"
                              from="Generator" to="Propagator"/>
                          </coupling-scheme:serial-explicit>
                          
                          </solver-interface>
                        </precice-configuration>
                    

Enabling the adapter


                        // Fluid/system/controlDict
                        
                        functions
                        {
                        preCICE_Adapter
                        {
                            
                        type preciceAdapterFunctionObject;
                        libs ("libpreciceAdapterFunctionObject.so");
                        
                        }
                        }
                    

                        // Solid/system/controlDict
                        
                        functions
                        {
                        preCICE_Adapter
                        {
                            
                        type preciceAdapterFunctionObject;
                        libs ("libpreciceAdapterFunctionObject.so");
                        
                        }
                        }
                    

OpenFOAM adapter config


                // Fluid/system/preciceDict
                    
                preciceConfig "precice-config.xml";

                participant Fluid;

                modules (CHT);

                interfaces
                {
                  Interface1
                  {
                    mesh      Fluid-Mesh;
                    patches   (interface);
                    
                    readData
                    (
                      Heat-Flux
                    );
                    
                    writeData
                    (
                      Temperature
                    );
                  };
                };
                

                // Solid/system/preciceDict
                    
                preciceConfig "precice-config.xml";

                participant Solid;

                modules (CHT);

                interfaces
                {
                  Interface1
                  {
                    mesh      Solid-Mesh;
                    patches   (interface);
                    
                    readData
                    (
                      Temperature
                    );
                    
                    writeData
                    (
                      Heat-Flux
                    );
                  };
                };

                CHT
                {
                   k   [ 1  1 -3 -1 0 0 0 ] 100;
                   solverType "basic";
                };
                

The interface patch


                // Fluid/system/blockMeshDict
                
                boundary
                (
                    interface
                    {
                        type wall;
                        faces
                        (
                            (4 0 1 5)
                        );
                    }

                    // ...
                );
                

                // Solid/system/blockMeshDict
                
                boundary
                (
                    interface
                    {
                        type wall;
                        faces
                        (
                            (7 6 2 3)
                        );
                    }
                    
                    // ...
                );
                

OpenFOAM boundary conditions


                // Fluid/0/T
                    
                internalField uniform 300;

                boundaryField
                {
                  interface
                  {
                    type      fixedGradient;
                    gradient  uniform 0;
                  }
                  inlet
                  {
                    type      fixedValue;
                    value     $internalField;
                  }
                  outlet
                  {
                    type      zeroGradient;
                  }
                  // ...
                }
                

                // Solid/0/T
                    
                internalField uniform 310;

                boundaryField
                {
                  interface
                  {
                    type      fixedValue;
                    value     $internalField;
                  }
                  left
                  {
                    type      zeroGradient;
                  }
                  // ...
                }
                

precice-config.xml


                        <precice-configuration>

                            <solver-interface dimensions="3">

                            <data:scalar name="Temperature"/>
                            <data:scalar name="Heat-Flux"/>

                            <mesh name="Fluid-Mesh">
                                <use-data name="Temperature"/>
                                <use-data name="Heat-Flux"/>
                            </mesh>

                            <mesh name="Solid-Mesh">
                                <use-data name="Temperature"/>
                                <use-data name="Heat-Flux"/>
                            </mesh>

                            <participant name="Fluid">
                                <use-mesh name="Fluid-Mesh" provide="yes"/>
                                <use-mesh name="Solid-Mesh" from="Solid"/>
                                <read-data name="Heat-Flux" mesh="Fluid-Mesh"/>
                                <write-data name="Temperature" mesh="Fluid-Mesh"/>
                                <mapping:nearest-neighbor direction="read"
                                  from="Solid-Mesh" to="Fluid-Mesh"
                                  constraint="consistent"/>
                            </participant>

                            <participant name="Solid">
                                <use-mesh name="Fluid-Mesh" from="Fluid"/>
                                <use-mesh name="Solid-Mesh" provide="yes"/>
                                <read-data name="Temperature" mesh="Solid-Mesh"/>
                                <write-data name="Heat-Flux" mesh="Solid-Mesh"/>
                                <mapping:nearest-neighbor direction="read"
                                  from="Fluid-Mesh" to="Solid-Mesh"
                                  constraint="consistent"/>
                            </participant>

                            <m2n:sockets from="Fluid" to="Solid"/>

                            <coupling-scheme:serial-implicit>
                                <time-window-size value="0.01"/>
                                <max-time value="1"/>
                                <participants first="Fluid" second="Solid"/>
                                <exchange data="Temperature" mesh="Fluid-Mesh"
                                  from="Fluid" to="Solid"/>
                                <exchange data="Heat-Flux" mesh="Solid-Mesh"
                                  from="Solid" to="Fluid"/>
                                <max-iterations value="200"/>
                                <relative-convergence-measure limit="1.0e-6"
                                  data="Temperature" mesh="Fluid-Mesh"/>
                                <acceleration:IQN-ILS>
                                    <data mesh="Solid-Mesh" name="Heat-Flux" />
                                    <initial-relaxation value="0.01" />
                                    <max-used-iterations value="80" />
                                    <time-windows-reused value="10" />
                                    <filter type="QR1" limit="1e-8" />
                                </acceleration:IQN-ILS>
                            </coupling-scheme:serial-implicit>

                            </solver-interface>

                        </precice-configuration>
                    

precice-config.xml

Visual representation of precice-config.xml using the config visualizer

"Flickering"? Run ./removeObsoleteFolders.sh first.

Configure OpenFOAM


                // Fluid/0.orig/U
                    
                flap
                {
                    type            movingWallVelocity;
                    value           uniform (0 0 0);
                }
                

                // Fluid/0.orig/pointDisplacement
                    
                flap
                {
                    type            fixedValue;
                    value           $internalField;
                }
                

                // Fluid/constant/dynamicMeshDict
                    
                solver      displacementLaplacian;
                

Configure the OpenFOAM adapter


                // Fluid/system/preciceDict
                    
                preciceConfig "precice-config.xml";

                participant Fluid;

                modules (FSI);

                interfaces
                {
                  Interface1
                  {
                    mesh              Fluid-Mesh-Centers;
                    patches           (flap);
                    locations         faceCenters;
                    
                    readData
                    (
                    );
                    
                    writeData
                    (
                      Stress
                    );
                  };
                  
                  Interface2
                  {
                    mesh              Fluid-Mesh-Nodes;
                    patches           (flap);
                    locations         faceNodes;
                    
                    readData
                    (
                      Displacement
                    );
                    
                    writeData
                    (
                    );
                  };
                };

                FSI
                {
                  rho rho [1 -3 0 0 0 0 0] 1;
                }
                

Configure the deal.ii adapter


                // Solid/linear_elasticity.prm
                    
                subsection precice configuration
                  # Cases: FSI3 or PF for perpendicular flap
                  set Scenario            = PF

                  # Name of the precice configuration file
                  set precice config-file = precice-config.xml

                  # Name of the participant in the precice-config.xml file
                  set Participant name    = Solid

                  # Name of the coupling mesh in the precice-config.xml file
                  set Mesh name           = Solid_mesh

                  # Name of the read data in the precice-config.xml file
                  set Read data name      = Stress

                  # Name of the write data in the precice-config.xml file
                  set Write data name     = Displacement
                end
                

Configure preCICE


                    <precice-configuration>
                        <solver-interface dimensions="2">

                        <data:vector name="Stress"/>
                        <data:vector name="Displacement"/>

                        <mesh name="Fluid-Mesh-Centers">
                            <use-data name="Stress"/>
                       </mesh>
                        <mesh name="Fluid-Mesh-Nodes">
                            <use-data name="Displacement"/>
                        </mesh>

                        <mesh name="Solid_mesh">
                            <use-data name="Displacement"/>
                            <use-data name="Stress"/>
                        </mesh>

                        <participant name="Fluid">
                            <use-mesh name="Fluid-Mesh-Nodes" provide="yes"/>
                            <use-mesh name="Fluid-Mesh-Centers" provide="yes"/>
                            <use-mesh name="Solid_mesh" from="Solid"/>
                            
                            <read-data name="Displacement" mesh="Fluid-Mesh-Nodes"/>
                            <write-data name="Stress" mesh="Fluid-Mesh-Centers"/>
                            
                            <mapping:rbf-thin-plate-splines direction="read"
                                from="Solid_mesh" to="Fluid-Mesh-Nodes"
                                constraint="consistent"/>
                        </participant>

                        <participant name="Solid">
                            <use-mesh name="Solid_mesh" provide="yes"/>
                            <use-mesh name="Fluid-Mesh-Centers" from="Fluid"/>
                            <use-mesh name="Fluid-Mesh-Nodes" from="Fluid"/>
                            
                            <read-data name="Stress"  mesh="Solid_mesh"/>
                            <write-data name="Displacement" mesh="Solid_mesh"/>
                            
                            <mapping:rbf-thin-plate-splines direction="read"
                                from="Fluid-Mesh-Centers" to="Solid_mesh"
                                constraint="consistent" />
                                
                            <watch-point mesh="Solid_mesh" name="flap_tip"
                                coordinate="-0.05;1;0" />
                        </participant>

                        <m2n:sockets from="Fluid" to="Solid" />

                        <coupling-scheme:serial-implicit>
                            <time-window-size value="0.01"/>
                            <max-time value="5"/>
                            <participants first="Fluid" second="Solid"/>
                            <exchange data="Stress" mesh="Fluid-Mesh-Centers"
                                from="Fluid" to="Solid"/>
                            <exchange data="Displacement" mesh="Solid_mesh"
                                from="Solid" to="Fluid" initialize="0"/>

                            <max-iterations value="50"/>
                            <relative-convergence-measure limit="1e-4"
                                data="Stress" mesh="Fluid-Mesh-Centers"/>
                            <relative-convergence-measure limit="1e-4"
                                data="Displacement" mesh="Solid_mesh"/>

                            <acceleration:IQN-ILS>
                                <data name="Displacement" mesh="Solid_mesh"/>
                                <preconditioner type="residual-sum"/>
                                <filter type="QR1" limit="1e-6"/>
                                <initial-relaxation value="0.1"/>
                                <max-used-iterations value="50"/>
                                <time-windows-reused value="10"/>
                            </acceleration:IQN-ILS>

                        </coupling-scheme:serial-implicit>
                        </solver-interface>
                    </precice-configuration>
                

Linear solid model

How is the tip moving?


                                #! /bin/bash
                                gnuplot -p << EOF
                                set grid
                                set title 'Displacement of the Flap Tip'
                                set xlabel 'Time [s]'
                                set ylabel 'X-Displacement [m]'
                                set linestyle  1 lt 2 lc 1 # red-dashed
                                plot "precice-Solid-watchpoint-flap_tip.log"
                                    using 1:4 
                                    title 'Top displacemement'
                                    with lines
                                EOF
                            

See also: OpenFOAM + CalculiX