Seminar at NTUA Chemical Engineering, Athens, Greece - September 29, 2025

Black-box coupling of simulation codes using preCICE

Gerasimos Chourdakis

Institute for Parallel and Distributed Systems
University of Stuttgart
gerasimos.chourdakis@ipvs.uni-stuttgart.de

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
                        participant = 
                            precice.Participant(  
                                               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
                        participant =
                            precice.Participant(
                                               participant_name,
                                               config_file_name,
                                               solver_process_index,
                                               solver_process_size
                                             )
                        
                        # Define the coupling mesh name
                        mesh_name  = "Generator-Mesh"
                        
                        # Define the coupling mesh
                        positions = [[1, y0] for y0 in y[:-1]]
                        vertex_ids = participant.set_mesh_vertices(mesh_name, positions)
                        
                        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
                        participant = 
                            precice.Participant(  
                                               participant_name,
                                               config_file_name,
                                               solver_process_index,
                                               solver_process_size
                                             )
                        
                        # Define the coupling mesh name
                        mesh_name = "Generator-Mesh"
                        
                        # Define the coupling mesh
                        positions  = [[1, y0] for y0 in y[:-1]]
                        vertex_ids = participant.set_mesh_vertices(mesh_name, positions)
                        
                        participant.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
                        
                        participant.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
                        participant =
                            precice.Participant(
                                              participant_name,
                                              config_file_name,
                                              solver_process_index,
                                              solver_process_size
                                            )
                    
                        # Define the coupling mesh name
                        mesh_name = "Generator-Mesh"
                        
                        # Define the coupling mesh
                        positions = [[1, y0] for y0 in y[:-1]]
                        vertex_ids = participant.set_mesh_vertices(mesh_name, positions)
                        
                        participant.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
                        
                        participant.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
                        participant =
                            precice.Participant(
                                              participant_name,
                                              config_file_name,
                                              solver_process_index,
                                              solver_process_size
                                            )
                    
                        # Define the coupling mesh name
                        mesh_name = "Generator-Mesh"
                        
                        # Define the coupling mesh
                        positions = [[1, y0] for y0 in y[:-1]]
                        vertex_ids = participant.set_mesh_vertices(mesh_name, positions)
                        
                        participant.initialize()

                        dt = 0.01
                        t = 0

                        while True:
                          print("Generating data")
                          u = 1 - 2 * numpy.random.rand(n)
                          
                          participant.advance(dt)
                          
                          t = t + dt 
                          if(t > 0.1):
                            break
                        
                        participant.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
                        participant =
                            precice.Participant(
                                              participant_name,
                                              config_file_name,
                                              solver_process_index,
                                              solver_process_size
                                            )
                    
                        # Define the coupling mesh name
                        mesh_name = "Generator-Mesh"
                        
                        # Define the coupling mesh
                        positions = [[1, y0] for y0 in y[:-1]]
                        vertex_ids = participant.set_mesh_vertices(mesh_name, positions)
                        
                        participant.initialize()

                        dt = 0.01
                        t = 0

                        while participant.is_coupling_ongoing():
                          print("Generating data")
                          u = 1 - 2 * numpy.random.rand(n)
                          
                          participant.advance(dt)
                          
                          t = t + dt 
                        
                        
                        
                        participant.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
                        participant =
                            precice.Participant(
                                              participant_name,
                                              config_file_name,
                                              solver_process_index,
                                              solver_process_size
                                            )
                    
                        # Define the coupling mesh name
                        mesh_name = "Generator-Mesh"
                        
                        # Define the coupling mesh
                        positions = [[1, y0] for y0 in y[:-1]]
                        vertex_ids = participant.set_mesh_vertices(mesh_name, positions)
                        
                        participant.initialize()

                        dt = 0.01
                        t = 0

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

Nothing happening?

Steering works. But 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
                        participant = 
                            precice.Participant(  
                                               participant_name,
                                               config_file_name,
                                               solver_process_index,
                                               solver_process_size
                                             )
                        
                        # Get the preCICE mesh name
                        mesh_name  = "Generator-Mesh"
                         
                        # Define the coupling mesh
                        positions = [[1, y0] for y0 in y[:-1]]
                        vertex_ids = participant.set_mesh_vertices(mesh_name, positions)
                        



                        
                        participant.initialize()
                        precice_dt = participant.get_max_time_step_size()

                        dt = 0.01
                        t = 0

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

                          participant.advance(dt)
                          precice_dt = participant.get_max_time_step_size()

                          
                          t = t + dt 
                        
                        participant.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
                        participant = 
                            precice.Participant(  
                                               participant_name,
                                               config_file_name,
                                               solver_process_index,
                                               solver_process_size
                                             )
                        
                        # Get the preCICE mesh name
                        mesh_name  = "Generator-Mesh"
                         
                        # Define the coupling mesh
                        positions = [[1, y0] for y0 in y[:-1]]
                        vertex_ids = participant.set_mesh_vertices(mesh_id, positions)
                        
                        # Get the exchanged data name
                        data_name = "Color"
                        
                        participant.initialize()

                        dt = 0.01
                        t = 0

                        while participant.is_coupling_ongoing():
                          dt = np.minimum(dt, precice_dt)
                          
                          print("Generating data")
                          u = 1 - 2 * numpy.random.rand(n)
                          
                          participant.write_data(mesh_name, data_name, vertex_ids, u)
                          precice_dt = participant.advance(dt)
                          # u[:, -1] = participant.read_data(mesh_name, data_name, vertex_ids, dt)
                          
                          t = t + dt 
                        
                        participant.finalize()
                    
It should work now!

Data is being transfered!

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

preCICE configuration

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