Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 7 additions & 11 deletions .github/workflows/run-benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,22 @@ jobs:
snakemake --use-conda --force --cores 'all'
snakemake --use-conda --force --cores all \
--reporter metadata4ing \
--report-metadata4ing-paramscript parameter_extractor.py \
--report-metadata4ing-filename metadata4ing_provenance
--report-metadata4ing-paramscript ../common/parameter_extractor.py \
--report-metadata4ing-filename snakemake_provenance
unzip snakemake_provenance -d snakemake_provenance

- name: run_linear-elastic-plate-with-hole-benchmarks_nextflow
shell: bash -l {0}
run: |
cd $GITHUB_WORKSPACE/benchmarks/linear-elastic-plate-with-hole/
nextflow run main.nf -params-file workflow_config.json -plugins [email protected]
nextflow run main.nf -params-file workflow_config.json -c ../common/nextflow.config -plugins [email protected]

- name: Archive Linear Elastic plate with a hole benchmark data for snakemake
uses: actions/upload-artifact@v4
with:
name: snakemake_results_linear-elastic-plate-with-hole
path: |
benchmarks/linear-elastic-plate-with-hole/metadata4ing_provenance.zip
benchmarks/linear-elastic-plate-with-hole/snakemake_provenance/

- name: Archive Linear Elastic plate with a hole benchmark data for nextflow
uses: actions/upload-artifact@v4
Expand All @@ -85,12 +86,7 @@ jobs:
uses: actions/download-artifact@v4
with:
name: snakemake_results_linear-elastic-plate-with-hole
path: ./artifact_files

- name: Unzip metadata4ing_provenance.zip
run: |
mkdir -p ./metadata4ing_provenance
unzip -o ./artifact_files/metadata4ing_provenance.zip -d ./metadata4ing_provenance
path: ./snakemake_provenance

- name: Setup Mambaforge with postprocessing env
uses: conda-incubator/setup-miniconda@v3
Expand All @@ -103,7 +99,7 @@ jobs:
- name: Run plotting script
shell: bash -l {0}
run: |
python benchmarks/linear-elastic-plate-with-hole/plot_provenance.py ./metadata4ing_provenance
python benchmarks/linear-elastic-plate-with-hole/plot_metrics.py ./snakemake_provenance

- name: Upload PDF plot as artifact
uses: actions/upload-artifact@v4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
ParameterExtractorInterface,
)

"""
Parses the parameter configuration files and their corresponding output files. Returns a dictionary.
https://github.com/izus-fokus/snakemake-report-plugin-metadata4ing

"""

class ParameterExtractor(ParameterExtractorInterface):
def extract_params(self, rule_name: str, file_path: str) -> dict:
results = {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,29 @@ def create_summary(configurations: list[str],
solution_metrics: list[str],
solution_field_data: list[str],
benchmark: str,
benchmark_uri: str,
summary_json: str) -> None:

"""
Create a summary JSON file containing simulation results and metadata.

Args:
configurations: List of configuration names
parameter_files: List of paths to parameter JSON files
mesh_files: List of paths to mesh files
solution_metrics: List of paths to metrics JSON files
solution_field_data: List of paths to solution field data files
benchmark: Name of the benchmark
benchmark_uri: URI of the benchmark
summary_json: Output path for the summary JSON file
"""


all_summaries = []
for idx, config in enumerate(configurations):
print(idx, config)
summary = {}
summary["benchmark"] = benchmark
print(solution_metrics[idx])
summary["benchmark_uri"] = benchmark_uri
with open(parameter_files[idx], "r") as param_file:
summary["parameters"] = json.load(param_file)
summary["mesh"] = f"{config}/mesh"
Expand All @@ -38,6 +53,7 @@ def create_summary(configurations: list[str],
parser.add_argument("--input_solution_metrics", nargs="+", type=str, required=True, help="Path to the metrics JSON file (input)")
parser.add_argument("--input_solution_field_data", nargs="+", type=str, required=True, help="Path to the zipped solution files (input)")
parser.add_argument("--input_benchmark", required=True, type=str, help="Name of the benchmark (input)")
parser.add_argument("--input_benchmark_uri", required=True, type=str, help="URI of the benchmark (input)")
parser.add_argument("--output_summary_json", required=True, type=str, help="Path to the summary JSON file (output)")
args = parser.parse_args()
create_summary(
Expand All @@ -47,5 +63,6 @@ def create_summary(configurations: list[str],
args.input_solution_metrics,
args.input_solution_field_data,
args.input_benchmark,
args.input_benchmark_uri,
args.output_summary_json
)
22 changes: 20 additions & 2 deletions benchmarks/linear-elastic-plate-with-hole/Snakefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ configuration_to_parameter_file = config["configuration_to_parameter_file"]
configurations = config["configurations"]
tools = config["tools"]
benchmark = config["benchmark"]
benchmark_uri = config["benchmark_uri"]


rule all:
Expand Down Expand Up @@ -41,7 +42,7 @@ rule summary:
input:
# the summary is performed for all configurations saved into a single file
# (snakemake_results/linear-elastic-plate-with-hole/fenics/summary.json)
script = "summarise_results.py",
script = "../common/summarize_results.py",
parameters = expand("{param}", param=[configuration_to_parameter_file[c] for c in configurations]),
mesh = expand(f"{result_dir}/mesh/mesh_{{configuration}}.msh", configuration=configurations),
metrics = lambda wildcards: expand(
Expand All @@ -64,5 +65,22 @@ rule summary:
--input_solution_metrics {input.metrics} \
--input_solution_field_data {input.solution_field_data} \
--input_benchmark {benchmark} \
--input_benchmark_uri {benchmark_uri} \
--output_summary_json {output.summary_json}
"""
"""

"""
Steps to add a new simulation tool to the workflow:

1. Write the tool-specific workflow, scripts, environment file and store them in the benchmarks/linear-elastic-plate-with-hole/tool_name/.
2. Add the tool name to "tools" workflow_config.json (generated here using generate_config.py)

------------------------------------------------------------------------------------------------------------------------
"rule all" defines the final target of the workflow. Knowing the final target, the snakemake determines
the dependency chain automatically.

Wildcards in the rule definitions allow to generalize the rules for multiple configurations and tools.
They act like placeholders (variables) in filenames or paths that get automatically filled in by Snakemake.

Information on snakemake rules: https://snakemake.readthedocs.io/en/stable/snakefiles/rules.html
"""
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import numpy as np
import sympy as sp

class PlateWithHoleSolution:
class AnalyticalSolution:
def __init__(self, E: float, nu: float, radius: float, L:float, load:float) -> None:
self.radius = radius
self.L = L
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
name: mesh-generation
# Environment file for create_mesh.py script. Called by the main workflow.

channels:
- conda-forge

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
name: postprocessing
# Environment file for summarize_results.py script. Called by the main workflow.

channels:
- conda-forge
- defaults
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
name: fenics_simulation
# Environment file for fenics simulation scripts. Called by fenics tool workflow.

channels:
- conda-forge

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

# Add parent directory to sys.path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from plateWithHoleSolution import PlateWithHoleSolution
from analytical_solution import AnalyticalSolution


def run_fenics_simulation(
Expand Down Expand Up @@ -76,7 +76,7 @@ def run_fenics_simulation(
.magnitude
)

analytical_solution = PlateWithHoleSolution(
analytical_solution = AnalyticalSolution(
E=E,
nu=nu,
radius=radius,
Expand Down
7 changes: 5 additions & 2 deletions benchmarks/linear-elastic-plate-with-hole/generate_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,17 @@ def get_configuration(file):
# Reverse mapping for easy lookup by configuration name
configuration_to_parameter_file = {v: str(k) for k, v in configurations.items()}

tools = ["fenics", "kratos"]
benchmark = "linear-elastic-plate-with-hole"
benchmark_uri = "https://portal.mardi4nfdi.de/wiki/Model:6775296"

# Template for workflow config
workflow_config = {
"configuration_to_parameter_file": configuration_to_parameter_file,
"configurations": list(configurations.values()),
"tools": ["fenics", "kratos"],
"benchmark": benchmark
"tools": tools,
"benchmark": benchmark,
"benchmark_uri": benchmark_uri
}

# Write workflow configuration file
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
from argparse import ArgumentParser
from pathlib import Path
import sys
# Ensure the parent directory is in the path to import PlateWithHoleSolution
# Ensure the parent directory is in the path to import AnalyticalSolution
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from plateWithHoleSolution import PlateWithHoleSolution
from analytical_solution import AnalyticalSolution

def create_kratos_input(
parameter_file: str,
Expand Down Expand Up @@ -50,7 +50,7 @@ def create_kratos_input(
.magnitude
)

analytical_solution = PlateWithHoleSolution(
analytical_solution = AnalyticalSolution(
E=E,
nu=nu,
radius=radius,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
name: kratos_simulation
# Environment file for kratos simulation scripts. Called by kratos tool workflow.

channels:
- conda-forge
dependencies:
Expand Down
32 changes: 24 additions & 8 deletions benchmarks/linear-elastic-plate-with-hole/main.nf
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ process summary{
val solution_metrics
val solution_field_data
val benchmark
val benchmark_uri
val tool

output:
Expand All @@ -48,6 +49,7 @@ process summary{
--input_solution_metrics ${solution_metrics.join(' ')} \
--input_solution_field_data ${solution_field_data.join(' ')} \
--input_benchmark ${benchmark} \
--input_benchmark_uri ${benchmark_uri} \
--output_summary_json "summary.json"

"""
Expand Down Expand Up @@ -137,21 +139,35 @@ workflow {

//Summarizing results
def ch_benchmark = Channel.value(params.benchmark)
def ch_summarise_python_script = Channel.value(file('summarise_results.py'))
summary(ch_summarise_python_script, \
def ch_benchmark_uri = Channel.value(params.benchmark_uri)
def ch_summarize_python_script = Channel.value(file('../common/summarize_results.py'))
summary(ch_summarize_python_script, \
input_summary_configuration, \
input_summary_parameter_file, \
input_summary_mesh, \
input_summary_metrics, \
input_summary_solution_field, \
ch_benchmark, \
ch_benchmark_uri, \
ch_tools)

}
/*
Steps to add a new simulation tool to the workflow:

// Steps to perform to add a new simulation tool to the workflow:
// 1. Add the tool name to "tools" workflow_config.json (generated here using generate_config.py)
// 2. Include the tool-specific workflow script at the top of this file.
// 3. Create an input channel for the new tool (e.g. see the definition of input_fenics_workflow)
// 4. Invoke the new tool-specific workflow (similar to fenics_workflow) & using its output, prepare inputs for the summary process.
// 5. Concatenate the prepared inputs to form the final input channels for the summary process.
1. Write the tool-specific workflow, scripts, environment file and store them in the benchmarks/linear-elastic-plate-with-hole/tool_name/.
2. Add the tool name to "tools" workflow_config.json (generated here using generate_config.py)
3. Include the tool-specific workflow script at the top of this file.
4. Create an input channel for the new tool (e.g. see the definition of input_fenics_workflow)
5. Invoke the new tool-specific workflow (similar to fenics_workflow) & using its output, prepare inputs for the summary process.
6. Concatenate the prepared inputs to form the final input channels for the summary process.

---------------------------------------------------------------------------------------------------------------------------------

Remark: Care should be taken to track the entries in the I/O channels, as the process output for a given configuration
may not arrive in the same order as the inputs were sent. When reusing channel entries after process execution, outputs should
be matched with their corresponding inputs using a common key.

Information on channel operations: https://www.nextflow.io/docs/latest/reference/operator.html
Information on channels: https://training.nextflow.io/2.2/basic_training/channels/
*/
6 changes: 3 additions & 3 deletions docs/benchmarks/linear elasticity/plate_with_hole.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
Expand All @@ -26,7 +26,7 @@
"\n",
"ureg = pint.UnitRegistry()\n",
"\n",
"class PlateWithHoleSolution:\n",
"class AnalyticalSolution:\n",
" def __init__(self, E, nu, radius, L, load):\n",
" self.radius = radius\n",
" self.L = L\n",
Expand Down Expand Up @@ -168,7 +168,7 @@
" except TypeError:\n",
" pass\n",
"\n",
"analytical_solution = PlateWithHoleSolution(without_unit[\"young-modulus\"], without_unit[\"poisson-ratio\"], without_unit[\"radius\"], without_unit[\"length\"], without_unit[\"load\"])"
"analytical_solution = AnalyticalSolution(without_unit[\"young-modulus\"], without_unit[\"poisson-ratio\"], without_unit[\"radius\"], without_unit[\"length\"], without_unit[\"load\"])"
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
Expand All @@ -27,7 +27,7 @@
"\n",
"ureg = pint.UnitRegistry()\n",
"\n",
"class PlateWithHoleSolution:\n",
"class AnalyticalSolution:\n",
" def __init__(self, E, nu, radius, L, load):\n",
" self.radius = radius\n",
" self.L = L\n",
Expand Down Expand Up @@ -160,7 +160,7 @@
" except TypeError:\n",
" pass\n",
"\n",
"analytical_solution = PlateWithHoleSolution(without_unit[\"young-modulus\"], without_unit[\"poisson-ratio\"], without_unit[\"radius\"], without_unit[\"length\"], without_unit[\"load\"])"
"analytical_solution = AnalyticalSolution(without_unit[\"young-modulus\"], without_unit[\"poisson-ratio\"], without_unit[\"radius\"], without_unit[\"length\"], without_unit[\"load\"])"
]
},
{
Expand Down
Loading
Loading