From d6c605a2f709d620dbc61b25c9004c0b8c37068c Mon Sep 17 00:00:00 2001 From: Benjamin Uekermann Date: Wed, 21 Feb 2024 17:36:53 +0100 Subject: [PATCH] Port runner to preCICE v3 (#9) * Start adapting runner to v3 * First working coupling with v3 for one special case * Implement coupling for scalar data only * Implement exchagne of preCICE vector data * Test v3 runner with oscillator tutorial * Add detection of preCICE version * Revert duplication of configurations * Merge cleaning scripts of test * Make run scripts executable * Update preCICE configs * Remove support of v2 * Remove explicit data type from configuration * Format code --------- Co-authored-by: Leonard Willeke --- fmiprecice/runner.py | 122 +++++++++----------------- tests/SolverOne/clean.sh | 3 - tests/SolverOne/precice-settings.json | 4 +- tests/SolverOne/run.sh | 0 tests/SolverTwo/clean.sh | 4 - tests/SolverTwo/precice-settings.json | 4 +- tests/SolverTwo/run.sh | 0 tests/clean.sh | 7 ++ tests/precice-config.xml | 86 +++++++++--------- 9 files changed, 96 insertions(+), 134 deletions(-) delete mode 100644 tests/SolverOne/clean.sh mode change 100644 => 100755 tests/SolverOne/run.sh delete mode 100644 tests/SolverTwo/clean.sh mode change 100644 => 100755 tests/SolverTwo/run.sh create mode 100755 tests/clean.sh diff --git a/fmiprecice/runner.py b/fmiprecice/runner.py index cf2e174..454fc0c 100644 --- a/fmiprecice/runner.py +++ b/fmiprecice/runner.py @@ -13,43 +13,13 @@ import json -# Define functions - -def precice_read_data(interface, data_type, read_data_id, vertex_id): - """ - Reads data from preCICE. The preCICE API call depends on the data type, scalar or vector. - """ - - if data_type == "scalar": - read_data = interface.read_scalar_data(read_data_id, vertex_id) - elif data_type == "vector": - read_data = interface.read_vector_data(read_data_id, vertex_id) - else: - raise Exception("Please choose data type from: scalar, vector.") - - return read_data - - -def precice_write_data(interface, data_type, write_data_id, vertex_id, write_data): - """ - Writes data to preCICE. The preCICE API call depends on the data type, scalar or vector. - """ - - if data_type == "scalar": - write_data = interface.write_scalar_data(write_data_id, vertex_id, write_data) - elif data_type == "vector": - write_data = interface.write_vector_data(write_data_id, vertex_id, write_data) - else: - raise Exception("Please choose data type from: scalar, vector.") - - def main(): """ Executes the Runner - Load settings from json files - Load FMU model and prepare the simulation - - Initialize the preCICE interface + - Create an object of the preCICE participant - Run the coupled simulation - Store the results - Terminate FMU model and preCICE @@ -167,48 +137,37 @@ def main(): solver_process_size = 1 num_vertices = 1 - # Initialize interface - interface = precice.Interface( + if precice.get_version_information().decode()[0] != "3": + raise Exception("This version of the FMI Runner is only compatible with preCICE v3.") + + # Create the participant + participant = precice.Participant( precice_data["coupling_params"]["participant_name"], precice_data["coupling_params"]["config_file_name"], solver_process_index, solver_process_size ) - mesh_id = interface.get_mesh_id(precice_data["coupling_params"]["mesh_name"]) - dimensions = interface.get_dimensions() + mesh_name = precice_data["coupling_params"]["mesh_name"] + read_data_name = precice_data["coupling_params"]["read_data_name"] + write_data_name = precice_data["coupling_params"]["write_data_name"] + + dimensions = participant.get_mesh_dimensions(mesh_name) vertices = np.zeros((num_vertices, dimensions)) read_data = np.zeros((num_vertices, dimensions)) write_data = np.zeros((num_vertices, dimensions)) - vertex_id = interface.set_mesh_vertices(mesh_id, vertices) - read_data_id = interface.get_data_id(precice_data["coupling_params"]["read_data"]["name"], mesh_id) - write_data_id = interface.get_data_id(precice_data["coupling_params"]["write_data"]["name"], mesh_id) - read_data_type = precice_data["coupling_params"]["read_data"]["type"] - write_data_type = precice_data["coupling_params"]["write_data"]["type"] - - # check entries for data types - if read_data_type not in ["scalar", "vector"]: - raise Exception("Wrong data type for read data in the precice settings file. Please choose from: scalar, vector") - if write_data_type not in ["scalar", "vector"]: - raise Exception("Wrong data type for write data in the precice settings file. Please choose from: scalar, vector") - - # initial value for write data - if write_data_type == "scalar": - write_data = fmu_write_data_init[0] - elif write_data_type == "vector": - write_data = np.array(fmu_write_data_init) + # Is it possible to have different data types for read and write? Eg read a scalar and write a vector. This should be possible from preCICE, but I have to implement it. - precice_dt = interface.initialize() - my_dt = precice_dt # use my_dt < precice_dt for subcycling + vertex_id = participant.set_mesh_vertices(mesh_name, vertices) # write initial data - if interface.is_action_required(precice.action_write_initial_data()): - precice_write_data(interface, write_data_type, write_data_id, vertex_id, write_data) - interface.mark_action_fulfilled(precice.action_write_initial_data()) + if participant.requires_initial_data(): + write_data = np.array(fmu_write_data_init) + participant.write_data(mesh_name, write_data_name, vertex_id, write_data) - interface.initialize_data() + participant.initialize() recorder = Recorder(fmu=fmu, modelDescription=model_description, variableNames=output_names) @@ -216,10 +175,11 @@ def main(): recorder.sample(t, force=False) - while interface.is_coupling_ongoing(): - if interface.is_action_required(precice.action_write_iteration_checkpoint()): + while participant.is_coupling_ongoing(): - # Check if model has the appropiate functionalities + if participant.requires_writing_checkpoint(): + + # Check if model has the appropriate functionalities if is_fmi1: raise Exception("Implicit coupling not possible because FMU model with FMI1 can't reset state. " "Please update model to FMI2 or FMI3. " @@ -234,19 +194,20 @@ def main(): state_cp = fmu.getFMUState() t_cp = t - interface.mark_action_fulfilled(precice.action_write_iteration_checkpoint()) - # Compute current time step size - dt = np.min([precice_dt, my_dt]) + precice_dt = participant.get_max_time_step_size() + dt = precice_dt # FMU always does the max possible dt - # Read data from other participant - read_data = precice_read_data(interface, read_data_type, read_data_id, vertex_id) + read_data = participant.read_data(mesh_name, read_data_name, vertex_id, precice_dt) # Convert data to list for FMU - if read_data_type == "scalar": - read_data = [read_data] - elif read_data_type == "vector": - read_data = list(read_data) + if participant.get_data_dimensions(mesh_name, read_data_name) > 1: + # why does this work with one-entry vectors? A (1,2) vector is written on a single scalar FMU variable. + # This is not correct + # The program should abort if data_type = vector and the number of entries + # in vr_read / vr_write do not match the number of elements in read_data / write_data + # preCICE aborts for write_data() with the wrong dimensions, that is ok for now + read_data = read_data[0] # Set signals in FMU input.apply(t) @@ -261,31 +222,28 @@ def main(): fmu.doStep(t, dt) result = fmu.getFloat64(vr_write) - # Convert result to double or array for preCICE - if write_data_type == "scalar": - write_data = result[0] - elif write_data_type == "vector": + # Convert result for preCICE + # Convert to array + if participant.get_data_dimensions(mesh_name, write_data_name) == 1: write_data = np.array(result) + else: + write_data = np.array([result]) - # Write data to other participant - precice_write_data(interface, write_data_type, write_data_id, vertex_id, write_data) + participant.write_data(mesh_name, write_data_name, vertex_id, write_data) t = t + dt - precice_dt = interface.advance(dt) - - if interface.is_action_required(precice.action_read_iteration_checkpoint()): + participant.advance(dt) + if participant.requires_reading_checkpoint(): fmu.setFMUState(state_cp) t = t_cp - interface.mark_action_fulfilled(precice.action_read_iteration_checkpoint()) - else: # Save output data for completed timestep recorder.sample(t, force=False) - interface.finalize() + participant.finalize() # store final results try: diff --git a/tests/SolverOne/clean.sh b/tests/SolverOne/clean.sh deleted file mode 100644 index 8f64071..0000000 --- a/tests/SolverOne/clean.sh +++ /dev/null @@ -1,3 +0,0 @@ -rm -rv output -rm -rv *events.json -rm -rv *.log diff --git a/tests/SolverOne/precice-settings.json b/tests/SolverOne/precice-settings.json index 89a5903..e873a56 100644 --- a/tests/SolverOne/precice-settings.json +++ b/tests/SolverOne/precice-settings.json @@ -3,7 +3,7 @@ "participant_name": "SolverOne", "config_file_name": "../precice-config.xml", "mesh_name": "MeshOne", - "write_data": {"name": "dataOne", "type": "scalar"}, - "read_data": {"name": "dataTwo", "type": "scalar"} + "write_data_name": "dataOne", + "read_data_name": "dataTwo" } } diff --git a/tests/SolverOne/run.sh b/tests/SolverOne/run.sh old mode 100644 new mode 100755 diff --git a/tests/SolverTwo/clean.sh b/tests/SolverTwo/clean.sh deleted file mode 100644 index c2b2b87..0000000 --- a/tests/SolverTwo/clean.sh +++ /dev/null @@ -1,4 +0,0 @@ -rm -rv ../precice-run -rm -rv output -rm -rv *events.json -rm -rv *.log diff --git a/tests/SolverTwo/precice-settings.json b/tests/SolverTwo/precice-settings.json index fd2bf43..743d03f 100644 --- a/tests/SolverTwo/precice-settings.json +++ b/tests/SolverTwo/precice-settings.json @@ -3,7 +3,7 @@ "participant_name": "SolverTwo", "config_file_name": "../precice-config.xml", "mesh_name": "MeshTwo", - "write_data": {"name": "dataTwo", "type": "scalar"}, - "read_data": {"name": "dataOne", "type": "scalar"} + "write_data_name": "dataTwo", + "read_data_name": "dataOne" } } diff --git a/tests/SolverTwo/run.sh b/tests/SolverTwo/run.sh old mode 100644 new mode 100755 diff --git a/tests/clean.sh b/tests/clean.sh new file mode 100755 index 0000000..d8545ed --- /dev/null +++ b/tests/clean.sh @@ -0,0 +1,7 @@ +rm -rfv SolverOne/precice-profiling +rm -rfv SolverTwo/precice-profiling +rm -rfv ./precice-run +rm -rfv SolverOne/output +rm -rfv SolverTwo/output +rm -rfv SolverOne/*.log +rm -rfv SolverTwo/*.log diff --git a/tests/precice-config.xml b/tests/precice-config.xml index 6af1859..3830295 100644 --- a/tests/precice-config.xml +++ b/tests/precice-config.xml @@ -9,45 +9,49 @@ enabled="true" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +