Skip to content

Commit 4208737

Browse files
div-tygdtyagijoergfungersrosenbu
authored
Restructure the Snakefile for the linear elastic plate with a hole test
* provenance creation for snakemake and nextflow workflows * edited run-benchmark.yml * update snakemake workflow for linear elastic benchmark * formating * updated the ci action file, fix typos there * fix env * fix bug for file patterns * first call snakemake and then the reporter afterwards * fix typo * fix typo * make the output depending on the tool as a wildcard, only the simulation is then performed with a different script, but all tools can be integrated into the same workflow * revert hdf5 storage to just store a zipped version of all simulation outputs and no longer store the summary.h5 * Update benchmarks/linear-elastic-plate-with-hole/FEniCS/plateWithHoleSolution.py update type hints Co-authored-by: Sjard Mathis Rosenbusch <69848361+srosenbu@users.noreply.github.com> * Update benchmarks/linear-elastic-plate-with-hole/FEniCS/plateWithHoleSolution.py update type hints Co-authored-by: Sjard Mathis Rosenbusch <69848361+srosenbu@users.noreply.github.com> * Update benchmarks/linear-elastic-plate-with-hole/FEniCS/plateWithHoleSolution.py update type hints Co-authored-by: Sjard Mathis Rosenbusch <69848361+srosenbu@users.noreply.github.com> * Update benchmarks/linear-elastic-plate-with-hole/FEniCS/plateWithHoleSolution.py update type hints Co-authored-by: Sjard Mathis Rosenbusch <69848361+srosenbu@users.noreply.github.com> * move directories * fix typo * add a check for duplicate configurations in different parameter files * add some comments * upload artefact location * Update benchmarks/linear-elastic-plate-with-hole/FEniCS/environment_mesh.yml Co-authored-by: Sjard Mathis Rosenbusch <69848361+srosenbu@users.noreply.github.com> --------- Co-authored-by: dtyagi <divyansh.tyagi@bam.de> Co-authored-by: Jörg F. Unger <joerg.unger@bam.de> Co-authored-by: Jörg F. Unger <joergfunger@users.noreply.github.com> Co-authored-by: Sjard Mathis Rosenbusch <69848361+srosenbu@users.noreply.github.com>
1 parent 5b3136c commit 4208737

22 files changed

+601
-419
lines changed

.github/workflows/run-benchmark.yml

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
name: CI
22
on:
33
push:
4-
branches-ignore:
54

65
pull_request:
76
branches: [ main ]
@@ -35,49 +34,58 @@ jobs:
3534

3635
- name: Update environment
3736
run: mamba env update -n model-validation -f environment_benchmarks.yml
38-
if: steps.cache.outputs.cache-hit != 'true'
3937

4038
- name: run-fenics-benchmarks
4139
shell: bash -l {0}
4240
run: |
4341
cd $GITHUB_WORKSPACE/benchmarks/linear-elastic-plate-with-hole/FEniCS/
4442
snakemake --use-conda --force --cores 'all'
43+
snakemake --use-conda --force --cores 'all' --reporter metadata4ing
4544
4645
- name: run-fenics-nextflow-benchmark
4746
shell: bash -l {0}
4847
run: |
4948
cd $GITHUB_WORKSPACE/benchmarks/linear-elastic-plate-with-hole/FEniCS_nextflow/
50-
nextflow run main.nf
49+
nextflow run main.nf -plugins nf-prov@1.4.0
5150
5251
- name: run-Kratos-benchmarks
5352
shell: bash -l {0}
5453
run: |
5554
cd $GITHUB_WORKSPACE/benchmarks/linear-elastic-plate-with-hole/Kratos/
5655
snakemake --use-conda --force --cores 'all'
5756
58-
#- name: run-optimization-workflow
59-
# shell: bash -l {0}
60-
# run: |
61-
# cd $GITHUB_WORKSPACE/usecases/optimization_paper/optimization_workflow/
62-
# snakemake -c 1
63-
64-
#- name: run-optimization-paper
65-
# shell: bash -l {0}
66-
# run: |
67-
# cd $GITHUB_WORKSPACE/usecases/optimization_paper/
68-
# doit
69-
7057
- name: Archive fenics data
7158
uses: actions/upload-artifact@v4
7259
with:
7360
name: fenics-output
7461
path: |
75-
benchmarks/linear-elastic-plate-with-hole/FEniCS/data
76-
62+
benchmarks/linear-elastic-plate-with-hole/fenics/snakemake_results/
63+
64+
- name: Archive fenics snakemake provenance
65+
uses: actions/upload-artifact@v4
66+
with:
67+
name: fenics-snakemake-provenance
68+
path: |
69+
benchmarks/linear-elastic-plate-with-hole/FEniCS/*.zip
70+
7771
- name: Archive kratos data
7872
uses: actions/upload-artifact@v4
7973
with:
8074
name: kratos-output
8175
path: |
8276
benchmarks/linear-elastic-plate-with-hole/Kratos/data
83-
77+
78+
- name: Archive fenics nextflow data
79+
uses: actions/upload-artifact@v4
80+
with:
81+
name: fenics-nextflow-output
82+
path: |
83+
benchmarks/linear-elastic-plate-with-hole/FEniCS_nextflow/work/**
84+
!benchmarks/linear-elastic-plate-with-hole/FEniCS_nextflow/work/conda/**
85+
86+
- name: Archive fenics nextflow provenance
87+
uses: actions/upload-artifact@v4
88+
with:
89+
name: fenics-nextflow-provenance
90+
path: |
91+
benchmarks/linear-elastic-plate-with-hole/FEniCS_nextflow/provenance.json
Lines changed: 92 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,111 @@
11
from pathlib import Path
2+
from os.path import join
23

3-
files = list(Path(".").rglob("parameters_*.json"))
4-
names = [f.stem.split("_")[1] for f in files]
4+
files = list(Path(".").glob("parameters_*.json"))
5+
6+
# extract the configuration from the parameter files
7+
# by reading in the json files and extracting the "configuration" value
8+
# configuration stores the appendix in the output files)"
9+
# in theory, you could make that identical so parameters_1.json with configuration "1"
10+
# would produce summary_1.json
11+
import json
12+
def get_configuration(file):
13+
with open(file, 'r') as f:
14+
data = json.load(f)
15+
# Check if "configuration" key exists, otherwise use the file name
16+
if "configuration" in data:
17+
return data["configuration"]
18+
# Fallback to using the file name if "configuration" is not present
19+
# Assuming the file name is in the format "parameters_<configuration>.json"
20+
if file.stem.startswith("parameters_"):
21+
return file.stem.split("_")[1]
22+
# If no configuration is found, raise an error
23+
raise ValueError(f"Configuration key not found for file: {file}")
24+
25+
# Create a dictionary of configurations (key is the name of the parameter file)
26+
# configurations: {Path("parameters_1.json"): "1", ...}
27+
configurations = {file: get_configuration(file) for file in files if file.is_file()}
28+
29+
# Check for duplicate configuration values (the configurations should be unique)
30+
config_values = list(configurations.values())
31+
duplicates = set([x for x in config_values if config_values.count(x) > 1])
32+
if duplicates:
33+
raise ValueError(f"Duplicate configuration values found in parameter files: {', '.join(duplicates)}")
34+
35+
# Reverse mapping for easy lookup by configuration name
36+
configuration_to_parameter_file = {v: str(k) for k, v in configurations.items()}
37+
38+
tools = ["fenics"]
39+
benchmark = "linear-elastic-plate-with-hole"
40+
# results are stored in snakemake_results/linear-elastic-plate-with-hole/fenics
41+
result_dir = join("snakemake_results", benchmark)
542

643
rule all:
744
input:
8-
expand("summary_{name}.json", name=names),
9-
#expand("output_{name}.h5", name=names)
45+
expand(f"{result_dir}/{{tool}}/summary.json", tool=tools),
1046

11-
rule generate_input_files:
47+
rule create_mesh:
1248
input:
13-
"experiment.json",
14-
"parameters_{name}.json",
49+
script = "create_mesh.py",
50+
# the parameters file for the current configuration, this has to be a lambda function since
51+
# the wildcard (configuration) has to be evaluated (the dictionary)
52+
# otherwise, you could just write configuration_to_parameter_file(configuration)
53+
parameters = lambda wildcards: configuration_to_parameter_file[wildcards.configuration],
1554
output:
16-
"data/input_{name}.json",
17-
"data/mesh_{name}.msh",
18-
conda: "environment.yml"
19-
shell: "python3 create_input_files.py {wildcards.name} {input}"
55+
mesh = f"{result_dir}/mesh/mesh_{{configuration}}.msh",
56+
conda: "environment_mesh.yml"
57+
shell:
58+
"""
59+
python3 {input.script} --input_parameter_file {input.parameters} --output_mesh_file {output.mesh}
60+
"""
2061

2162
rule run_simulation:
2263
input:
23-
"data/input_{name}.json",
24-
"data/mesh_{name}.msh",
64+
script = "{tool}/run_simulation.py",
65+
parameters = lambda wildcards: configuration_to_parameter_file[wildcards.configuration],
66+
mesh = f"{result_dir}/mesh/mesh_{{configuration}}.msh",
2567
output:
26-
"data/output_{name}.vtk",
68+
zip = f"{result_dir}/{{tool}}/solution_field_data_{{configuration}}.zip",
69+
metrics = f"{result_dir}/{{tool}}/solution_metrics_{{configuration}}.json",
2770
conda:
28-
"environment.yml",
29-
shell: "python3 run_simulation.py {wildcards.name} {input}"
71+
"{tool}/environment_simulation.yml",
72+
shell:
73+
"""
74+
python3 {input.script} --input_parameter_file {input.parameters} --input_mesh_file {input.mesh} --output_solution_file_zip {output.zip} --output_metrics_file {output.metrics}
75+
"""
3076

3177
rule summary:
3278
input:
33-
"data/output_{name}.vtk",
34-
"data/input_{name}.json",
35-
"data/mesh_{name}.msh",
36-
"parameters_{name}.json",
79+
# the summary is performed for all configurations saved into a single file
80+
# (snakemake_results/linear-elastic-plate-with-hole/fenics/summary.json)
81+
parameters = expand("{param}", param=[configuration_to_parameter_file[c] for c in configurations.values()]),
82+
mesh = expand(f"{result_dir}/mesh/mesh_{{configuration}}.msh", configuration=configurations.values()),
83+
metrics = lambda wildcards: expand(
84+
f"{result_dir}/{{tool}}/solution_metrics_{{configuration}}.json",
85+
tool=[wildcards.tool], configuration=configurations.values()
86+
),
87+
solution_field_data = lambda wildcards: expand(
88+
f"{result_dir}/{{tool}}/solution_field_data_{{configuration}}.zip",
89+
tool=[wildcards.tool], configuration=configurations.values()
90+
),
3791
output:
38-
"summary_{name}.json",
92+
summary_json = f"{result_dir}/{{tool}}/summary.json",
93+
conda: "environment_postprocessing.yml",
3994
run:
4095
import json
41-
import pyvista
42-
summary = {}
43-
summary["name"] = wildcards.name
44-
summary["parameters"] = input[3]
45-
summary["input"] = input[1]
46-
summary["mesh"] = input[2]
47-
summary["output"] = input[0]
48-
# Load the mesh and output data
49-
max_mises_stress = 42.0
50-
from xml.etree import ElementTree as ET
51-
tree = ET.parse(input[0])
52-
root = tree.getroot()
53-
pvtu_filenames = []
54-
path = Path(input[0]).parent
55-
for dataset in root.findall(".//DataSet"):
56-
pvtu_filenames.append(path / dataset.get("file"))
57-
meshes = [pyvista.read(pvtu_filename) for pvtu_filename in pvtu_filenames]
58-
print(pvtu_filenames)
59-
for mesh in meshes:
60-
# Assuming the mesh has a 'von_mises_stress' array
61-
try:
62-
max_mises_stress = float(mesh["von_mises_stress"].max())
63-
except KeyError:
64-
print("von_mises_stress not found in mesh.")
65-
summary["max_mises_stress"] = max_mises_stress # Replace with actual computation
66-
with open(output[0], "w") as f:
67-
json.dump(summary, f, indent=4)
96+
from pathlib import Path
97+
98+
all_summaries = []
99+
for idx, config in enumerate(configurations.values()):
100+
summary = {}
101+
summary["benchmark"] = benchmark
102+
with open(input.parameters[idx], "r") as param_file:
103+
summary["parameters"] = json.load(param_file)
104+
summary["mesh"] = f"{config}/mesh"
105+
with open(input.metrics[idx], "r") as metrics_file:
106+
summary["metrics"] = json.load(metrics_file)
107+
summary["configuration"] = config
108+
all_summaries.append(summary)
109+
110+
with open(output.summary_json, "w") as f:
111+
json.dump(all_summaries, f, indent=4)

benchmarks/linear-elastic-plate-with-hole/FEniCS/create_input_files.py

Lines changed: 0 additions & 95 deletions
This file was deleted.

0 commit comments

Comments
 (0)