Skip to content

Commit fc65893

Browse files
uekermanLeonard WillekeLeonardWillekeMakisH
authored
Add FMU participants to oscillator tutorial (precice#466)
Co-authored-by: Leonard Willeke <[email protected]> Co-authored-by: LeonardWilleke <[email protected]> Co-authored-by: Gerasimos Chourdakis <[email protected]>
1 parent d00a8f7 commit fc65893

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+10063
-26
lines changed

Diff for: oscillator/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fmi/Oscillator.fmu

Diff for: oscillator/README.md

+13-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Oscillator
33
permalink: tutorials-oscillator.html
4-
keywords: Python, ODE
4+
keywords: Python, ODE, FMI
55
summary: We solve an oscillator with two masses in a partitioned fashion. Each mass is solved by an independent ODE.
66
---
77

@@ -17,19 +17,16 @@ This tutorial solves a simple mass-spring oscillator with two masses and three s
1717

1818
Note that this case applies a Schwarz-type coupling method and not (like most other tutorials in this repository) a Dirichlet-Neumann coupling. This results in a symmetric setup of the solvers. We will refer to the solver computing the trajectory of $m_1$ as `Mass-Left` and to the solver computing the trajectory of $m_2$ as `Mass-Right`. For more information, please refer to [1].
1919

20-
This tutorial is useful to study different time stepping and coupling schemes. It uses subcycling and time interpolation: Each solver performs 4 time steps in each time window. The data of these 4 substeps is used to create a third order B-spline interpolation (`waveform-degree="3"` in `precice-config.xml`).
21-
2220
## Available solvers
2321

24-
This tutorial is only available in Python. You need to have preCICE and the Python bindings installed on your system.
25-
26-
- *Python*: An example solver using the preCICE [Python bindings](https://www.precice.org/installation-bindings-python.html). This solver also depends on the Python libraries `numpy`, which you can get from your system package manager or with `pip3 install --user <package>`. Using the option `-ts` allows to pick the time stepping scheme being used. Available choices are Newmark beta, generalized alpha, explicit Runge Kutta 4, and implicit RadauIIA.
22+
There are two different implementations:
2723

28-
## Running the Simulation
24+
- *Python*: A solver using the preCICE [Python bindings](https://www.precice.org/installation-bindings-python.html). This solver also depends on the Python libraries `numpy`, which you can get from your system package manager or with `pip3 install --user <package>`. Using the option `-ts` allows you to pick the time stepping scheme being used. Available choices are Newmark beta, generalized alpha, explicit Runge Kutta 4, and implicit RadauIIA. The solver uses subcycling: Each participant performs 4 time steps in each time window. The data of these 4 substeps is then used by preCICE to create a third order B-spline interpolation (`waveform-degree="3"` in `precice-config.xml`).
25+
- *FMI*: A solver using the [preCICE-FMI runner](https://github.com/precice/fmi-runner) (requires at least v0.2). The Runner executes the FMU model `Oscillator.fmu` for computation. The provided run script (see below) builds this model if not already there. If you want to change the model parameters or the initial conditions of the simulation, have a look inside the setting files for [MassLeft](https://github.com/precice/tutorials/tree/master/oscillator/fmi/MassLeft) and [MassRight](https://github.com/precice/tutorials/tree/master/oscillator/fmi/MassRight). For more information, please refer to [2].
2926

30-
### Python
27+
## Running the simulation
3128

32-
Open two separate terminals and start both participants by calling:
29+
Open two separate terminals and start both participants. For example, you can run a simulation where the left participant is computed in Python and the right participant is computed with FMI with these commands:
3330

3431
```bash
3532
cd python
@@ -39,19 +36,21 @@ cd python
3936
and
4037

4138
```bash
42-
cd python
39+
cd fmi
4340
./run.sh -r
4441
```
4542

43+
Of course, you can also use the same solver for both sides.
44+
4645
## Post-processing
4746

48-
Each simulation run creates two files containing position and velocity of the two masses over time. These files are called `trajectory-Mass-Left.csv` and `trajectory-Mass-Right.csv`. You can use the script `plot-trajectory.py` for post-processing. Type `python3 plot-trajectory --help` to see available options. You can, for example plot the trajectory by running
47+
Each simulation run creates two files containing position and velocity of the two masses over time. These files are called `trajectory-Mass-Left.csv` and `trajectory-Mass-Right.csv`. You can use the script `plot-trajectory.py` for post-processing. Type `python3 plot-trajectory --help` to see available options. You can, for example, plot the trajectory of the Python solver by running
4948

5049
```bash
5150
python3 plot-trajectory.py python/output/trajectory-Mass-Left.csv TRAJECTORY
5251
```
5352

54-
This allows you to study the effect of different time stepping schemes on energy conservation. Newmark beta conserves energy:
53+
The solvers allow you to study the effect of different time stepping schemes on energy conservation. Newmark beta conserves energy:
5554

5655
![Trajectory for Newmark beta scheme](images/tutorials-oscillator-trajectory-newmark-beta.png)
5756

@@ -64,3 +63,5 @@ For details, refer to [1].
6463
## References
6564

6665
[1] V. Schüller, B. Rodenberg, B. Uekermann and H. Bungartz, A Simple Test Case for Convergence Order in Time and Energy Conservation of Black-Box Coupling Schemes, in: WCCM-APCOM2022. [URL](https://www.scipedia.com/public/Rodenberg_2022a)
66+
67+
[2] L. Willeke, D. Schneider and B. Uekermann, A preCICE-FMI Runner to Couple FMUs to PDE-Based Simulations, Proceedings 15th Intern. Modelica Conference, 2023. [DOI](https://doi.org/10.3384/ecp204)

Diff for: oscillator/fmi/MassLeft/fmi-settings.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"model_params": {
3+
"mass.m": 1,
4+
"spring_fixed.c": 39.4784176044,
5+
"spring_middle.c": 157.913670417
6+
},
7+
"initial_conditions": {
8+
"mass.u": 1,
9+
"mass.v": 0,
10+
"mass.a": -197.392088022
11+
},
12+
"simulation_params": {
13+
"fmu_file_name": "./Oscillator.fmu",
14+
"output_file_name": "./output/trajectory-Mass-Left.csv",
15+
"output": ["mass.u", "mass.v"],
16+
"fmu_read_data_names": ["force_in"],
17+
"fmu_write_data_names": ["force_out"],
18+
"fmu_instance_name": "instance_left"
19+
}
20+
}

Diff for: oscillator/fmi/MassLeft/precice-settings.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"coupling_params": {
3+
"participant_name": "Mass-Left",
4+
"config_file_name": "../precice-config.xml",
5+
"mesh_name": "Mass-Left-Mesh",
6+
"write_data_name": "Force-Left",
7+
"read_data_name": "Force-Right"
8+
}
9+
}

Diff for: oscillator/fmi/MassRight/fmi-settings.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"model_params": {
3+
"mass.m": 1,
4+
"spring_fixed.c": 39.4784176044,
5+
"spring_middle.c": 157.913670417
6+
},
7+
"initial_conditions": {
8+
"mass.u": 0,
9+
"mass.v": 0,
10+
"mass.a": 157.913670417
11+
},
12+
"simulation_params": {
13+
"fmu_file_name": "./Oscillator.fmu",
14+
"output_file_name": "./output/trajectory-Mass-Right.csv",
15+
"output": ["mass.u", "mass.v"],
16+
"fmu_read_data_names": ["force_in"],
17+
"fmu_write_data_names": ["force_out"],
18+
"fmu_instance_name": "instance_right"
19+
}
20+
}

Diff for: oscillator/fmi/MassRight/precice-settings.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"coupling_params": {
3+
"participant_name": "Mass-Right",
4+
"config_file_name": "../precice-config.xml",
5+
"mesh_name": "Mass-Right-Mesh",
6+
"write_data_name": "Force-Right",
7+
"read_data_name": "Force-Left"
8+
}
9+
}

Diff for: oscillator/fmi/calculate-error.py

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import numpy as np
2+
import pandas as pd
3+
import json
4+
import os
5+
import argparse
6+
from enum import Enum
7+
import sys
8+
9+
10+
class Participant(Enum):
11+
MASS_LEFT = "Mass-Left"
12+
MASS_RIGHT = "Mass-Right"
13+
14+
15+
parser = argparse.ArgumentParser()
16+
parser.add_argument("fmi_setting_file_left", help="Path to the fmi setting file for MassLeft.", type=str)
17+
parser.add_argument("precice_setting_file_left", help="Path to the precice setting file for MassLeft.", type=str)
18+
parser.add_argument("fmi_setting_file_right", help="Path to the fmi setting file for MassRight.", type=str)
19+
parser.add_argument("precice_setting_file_right", help="Path to the precice setting file for MassRight.", type=str)
20+
parser.add_argument("participant_name", help="Participant for which the error should be calculated", type=str,
21+
choices=[p.value for p in Participant])
22+
args = parser.parse_args()
23+
24+
# Get input files
25+
fmi_file_left = args.fmi_setting_file_left
26+
precice_file_left = args.precice_setting_file_left
27+
fmi_file_right = args.fmi_setting_file_right
28+
precice_file_right = args.precice_setting_file_right
29+
participant_name = args.participant_name
30+
31+
# Read json files
32+
folder = os.path.dirname(os.path.join(os.getcwd(), os.path.dirname(sys.argv[0]), fmi_file_left))
33+
path = os.path.join(folder, os.path.basename(fmi_file_left))
34+
read_file = open(path, "r")
35+
fmi_data_left = json.load(read_file)
36+
37+
folder = os.path.dirname(os.path.join(os.getcwd(), os.path.dirname(sys.argv[0]), precice_file_left))
38+
path = os.path.join(folder, os.path.basename(precice_file_left))
39+
read_file = open(path, "r")
40+
precice_data_left = json.load(read_file)
41+
42+
folder = os.path.dirname(os.path.join(os.getcwd(), os.path.dirname(sys.argv[0]), fmi_file_right))
43+
path = os.path.join(folder, os.path.basename(fmi_file_right))
44+
read_file = open(path, "r")
45+
fmi_data_right = json.load(read_file)
46+
47+
folder = os.path.dirname(os.path.join(os.getcwd(), os.path.dirname(sys.argv[0]), precice_file_right))
48+
path = os.path.join(folder, os.path.basename(precice_file_right))
49+
read_file = open(path, "r")
50+
precice_data_right = json.load(read_file)
51+
52+
53+
# Define variables
54+
k_1 = fmi_data_left["model_params"]["spring_fixed.c"]
55+
k_2 = fmi_data_right["model_params"]["spring_fixed.c"]
56+
u0_1 = fmi_data_left["initial_conditions"]["mass.u"]
57+
u0_2 = fmi_data_right["initial_conditions"]["mass.u"]
58+
k_12_left = fmi_data_left["model_params"]["spring_middle.c"]
59+
k_12_right = fmi_data_right["model_params"]["spring_middle.c"]
60+
61+
if k_12_left == k_12_right:
62+
k_12 = k_12_left
63+
else:
64+
raise Exception("k_12 has to be equal in both participants. Please adjust input values.")
65+
66+
67+
# Define analytical solution and read computed results
68+
K = np.array([[k_1 + k_12, -k_12], [-k_12, k_2 + k_12]])
69+
eigenvalues, eigenvectors = np.linalg.eig(K)
70+
omega = np.sqrt(eigenvalues)
71+
A, B = eigenvectors
72+
c = np.linalg.solve(eigenvectors, [u0_1, u0_2])
73+
74+
if participant_name == Participant.MASS_LEFT.value:
75+
filename = fmi_data_left["simulation_params"]["output_file_name"]
76+
df = pd.read_csv(filename, delimiter=',')
77+
78+
def u_analytical(t): return c[0] * A[0] * np.cos(omega[0] * t) + c[1] * A[1] * np.cos(omega[1] * t)
79+
80+
elif participant_name == Participant.MASS_RIGHT.value:
81+
filename = fmi_data_right["simulation_params"]["output_file_name"]
82+
df = pd.read_csv(filename, delimiter=',')
83+
84+
def u_analytical(t): return c[0] * B[0] * np.cos(omega[0] * t) + c[1] * B[1] * np.cos(omega[1] * t)
85+
86+
times = df.iloc[:, 0]
87+
positions = df.iloc[:, 1]
88+
89+
90+
# Calculate error
91+
error = np.max(abs(u_analytical(np.array(times)) - np.array(positions)))
92+
print("Error w.r.t analytical solution:")
93+
print(f"{error}")

Diff for: oscillator/fmi/clean.sh

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/bin/sh
2+
set -e -u
3+
4+
. ../../tools/cleaning-tools.sh
5+
6+
rm -rfv ./output/
7+
8+
clean_precice_logs .

0 commit comments

Comments
 (0)