Skip to content

Commit e7dfa3e

Browse files
committed
Merge remote-tracking branch 'origin/55-reporter-plugin-update-rohub-upload-issue' into 62-rocrate-merge
2 parents 2336ec0 + a93a7be commit e7dfa3e

File tree

14 files changed

+1741
-155
lines changed

14 files changed

+1741
-155
lines changed

.github/workflows/run-benchmark.yml

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,12 @@ on:
1414

1515
env:
1616
CACHE_NUMBER: 1 # increase to reset cache manually
17+
SNAKEMAKE_RESULT_FILE: metadata4ing_provenance
18+
PROVENANACE_FILE_NAME: element_size_vs_max_mises_stress.pdf
1719

1820
jobs:
19-
tests:
21+
run-simulation:
2022
runs-on: ubuntu-latest
21-
22-
23-
2423
steps:
2524
- name: checkout repo content
2625
uses: actions/checkout@v2
@@ -84,7 +83,7 @@ jobs:
8483
8584
process-artifacts:
8685
runs-on: ubuntu-latest
87-
needs: tests
86+
needs: run-simulation
8887
steps:
8988
- name: Checkout repo content
9089
uses: actions/checkout@v2
@@ -108,14 +107,60 @@ jobs:
108107
activate-environment: postprocessing
109108
use-mamba: true
110109
environment-file: benchmarks/linear-elastic-plate-with-hole/environment_postprocessing.yml
110+
111+
- name: Validate Snakemake Result File
112+
shell: bash -l {0}
113+
run: |
114+
python benchmarks/common/validate_provenance.py \
115+
--provenance_folderpath "./$SNAKEMAKE_RESULT_FILE"
111116
112117
- name: Run plotting script
113118
shell: bash -l {0}
114119
run: |
115120
python benchmarks/linear-elastic-plate-with-hole/plot_metrics.py ./snakemake_fenics_provenance ./snakemake_kratos_provenance
116121
117-
- name: Upload PDF plot as artifact
122+
- name: Upload snakemake results file as artifact
118123
uses: actions/upload-artifact@v4
119124
with:
120125
name: element-size-vs-stress-plot
121-
path: element_size_vs_stress.pdf
126+
path: ${{ env.PROVENANACE_FILE_NAME }}
127+
128+
- name: Re-zip snakemake result folder
129+
run: |
130+
cd "./${SNAKEMAKE_RESULT_FILE}"
131+
zip -r "../${SNAKEMAKE_RESULT_FILE}.zip" .
132+
133+
- name: Upload RoCrate Zip file onto RoHub
134+
id: rohub_upload
135+
shell: bash -l {0}
136+
continue-on-error: true
137+
run: |
138+
LOG_FILE="${GITHUB_WORKSPACE}/rohub_upload_error.log"
139+
140+
# Run Python and capture stdout+stderr
141+
OUTPUT="$(
142+
python benchmarks/common/upload_provenance.py \
143+
--provenance_folderpath "./${SNAKEMAKE_RESULT_FILE}.zip" \
144+
--benchmark_name "linear-elastic-plate-with-hole" \
145+
--username "${{ secrets.ROHUB_USERNAME }}" \
146+
--password "${{ secrets.ROHUB_PASSWORD }}" \
147+
2>&1
148+
)"
149+
PYTHON_EXIT_CODE=$?
150+
151+
# Standard convention: 0 = success, non-zero = failure
152+
if [ "$PYTHON_EXIT_CODE" -eq 0 ]; then
153+
echo "RoHub upload succeeded"
154+
rm -f "$LOG_FILE" || true
155+
else
156+
echo "=== RoHub upload failed — writing log to $LOG_FILE ==="
157+
printf '%s\n' "$OUTPUT" | tee "$LOG_FILE"
158+
fi
159+
160+
- name: Upload RoHub error log
161+
if: always()
162+
uses: actions/upload-artifact@v4
163+
with:
164+
name: rohub-upload-error-log
165+
path: ${{ github.workspace }}/rohub_upload_error.log
166+
if-no-files-found: ignore
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Julia-CI
2+
on:
3+
push:
4+
5+
pull_request:
6+
branches: [ main ]
7+
8+
# Allows you to run this workflow manually from the Actions tab
9+
workflow_dispatch:
10+
11+
# Runs the workflow once per day at 3:15am
12+
schedule:
13+
- cron: '3 16 * * *'
14+
15+
env:
16+
CACHE_NUMBER: 1 # increase to reset cache manually
17+
18+
jobs:
19+
tests:
20+
runs-on: ubuntu-latest
21+
22+
23+
24+
steps:
25+
- name: checkout repo content
26+
uses: actions/checkout@v2
27+
28+
- name: Setup Mambaforge
29+
uses: conda-incubator/setup-miniconda@v3
30+
with:
31+
miniforge-version: latest
32+
activate-environment: model-validation
33+
use-mamba: true
34+
- name: Setup Apptainer
35+
uses: eWaterCycle/setup-apptainer@v2
36+
with:
37+
apptainer-version: 1.4.5
38+
- name: Set strict channel priority
39+
run: conda config --set channel_priority strict
40+
41+
- name: Update environment
42+
run: mamba env update -n model-validation -f environment_benchmarks.yml
43+
44+
- name: generate-config-files
45+
shell: bash -l {0}
46+
run: |
47+
cd $GITHUB_WORKSPACE/benchmarks/linear-elastic-plate-with-hole/
48+
python generate_config.py
49+
50+
- name: run_linear-elastic-plate-with-hole-benchmarks_snakemake
51+
shell: bash -l {0}
52+
run: |
53+
cd $GITHUB_WORKSPACE/benchmarks/linear-elastic-plate-with-hole/
54+
snakemake --use-conda --use-apptainer --cores=all --config tools=[extendablefem] configurations=["1","05","025","0125","00625","003125"]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[deps]
2+
ExtendableFEM = "a722555e-65e0-4074-a036-ca7ce79a4aed"
3+
ExtendableGrids = "cfc395e8-590f-11e8-1f13-43a2532b2fa8"
4+
Fire = "652a1917-b8e5-5d9b-be38-bbf27e56fe44"
5+
Gmsh = "705231aa-382f-11e9-3f0c-b7cb4346fdeb"
6+
GridVisualize = "5eed8a63-0fb0-45eb-886d-8d5a387d12b8"
7+
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
8+
LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae"
9+
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
10+
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
11+
ZipArchives = "49080126-0e18-4c2a-b176-c102e4b3760c"
12+
13+
[compat]
14+
LinearSolve = "~3.50.0"
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
import json
3+
import os
4+
5+
tool = "extendablefem"
6+
result_dir = "snakemake_results/" + config["benchmark"]
7+
configuration_to_parameter_file = config["configuration_to_parameter_file"]
8+
configurations = config["configurations"]
9+
10+
rule setup_julia_environment:
11+
input: f"{tool}/Project.toml"
12+
output: manifest=f"{tool}/Manifest.toml"
13+
singularity: "docker://julia:1.12.2-bookworm"
14+
shell:
15+
"""
16+
julia --project={tool} -e 'using Pkg;Pkg.instantiate()'
17+
"""
18+
19+
rule run_extendablefem_simulation:
20+
input:
21+
parameters = lambda wildcards: configuration_to_parameter_file[wildcards.configuration],
22+
mesh = f"{result_dir}/mesh/mesh_{{configuration}}.msh",
23+
manifest = f"{tool}/Manifest.toml",
24+
output:
25+
zip = f"{result_dir}/{{tool}}/solution_field_data_{{configuration}}.zip",
26+
metrics = f"{result_dir}/{{tool}}/solution_metrics_{{configuration}}.json",
27+
singularity: "docker://julia:1.12.2-bookworm"
28+
shell:
29+
"""
30+
julia --project={tool} {tool}/run_extendablefem_simulation.jl --configfile {input.parameters} --meshfile {input.mesh} --outputzip {output.zip} --outputmetrics {output.metrics}
31+
"""
32+
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
using JSON
2+
using Fire
3+
using Gmsh
4+
using ExtendableGrids
5+
using ExtendableFEM
6+
using StaticArrays: @SArray
7+
using Unitful
8+
using LinearAlgebra
9+
using ZipArchives: ZipWriter, zip_newfile
10+
11+
struct PlateConfig
12+
id::String
13+
F::Float64
14+
E::Float64
15+
ν::Float64
16+
radius::Float64
17+
length::Float64
18+
element_order::Int64
19+
end
20+
21+
struct Metrics
22+
max_von_mises_stress_nodes::Float64
23+
end
24+
25+
function value_with_unit(json::JSON.Object{String,Any})
26+
res = uparse(string(json["value"])*json["unit"])
27+
return res
28+
end
29+
30+
31+
function parse_config(configfile::String)
32+
config = JSON.parsefile(configfile)
33+
id = config["configuration"]
34+
F = ustrip(u"Pa",value_with_unit(config["load"]))
35+
E = ustrip(u"Pa",value_with_unit(config["young-modulus"]))
36+
ν = config["poisson-ratio"]["value"]
37+
radius = ustrip(u"m",value_with_unit(config["radius"]))
38+
length = ustrip(u"m",value_with_unit(config["length"]))
39+
element_order = config["element-order"]
40+
return PlateConfig(id,F,E,ν,radius,length,element_order)
41+
end
42+
43+
44+
function plane_stress_elasticity_tensor(E::Float64, ν::Float64)
45+
G = (1 / (1 + ν)) * E * 0.5
46+
λ =/ (1 - 2ν)) * 2 * G
47+
return @SArray [
48+
+ 2G) λ 0
49+
λ (λ + 2G) 0
50+
0 0 G
51+
]
52+
end
53+
54+
55+
function make_kernel(𝐂)
56+
function LE_kernel_sym!(σ,εv,qpinfo)
57+
mul!(σ,𝐂,εv)
58+
end
59+
return LE_kernel_sym!
60+
end
61+
62+
function u_ex_kernel!(result,qpinfo)
63+
x = qpinfo.x[1]
64+
y = qpinfo.x[2]
65+
a = qpinfo.params[1]
66+
T = qpinfo.params[2]
67+
E = qpinfo.params[3]
68+
ν = qpinfo.params[4]
69+
r = sqrt(x^2+y^2)
70+
θ = atan(y,x)
71+
k = (3.0-ν)/(1.0+ν)
72+
Ta_8mu = T*a*(1.0+ν)/(4.0*E)
73+
ct = cos(θ)
74+
c3t = cos(3.0*θ)
75+
st = sin(θ)
76+
s3t = sin(3.0*θ)
77+
fac = 2.0 * (a/r)^3
78+
79+
80+
result[1] = Ta_8mu * (
81+
(r/a) * (k + 1.0) * ct
82+
+ 2.0*(a/r)*((1.0 + k) * ct + c3t)
83+
- fac * c3t
84+
)
85+
result[2] = Ta_8mu * (
86+
(r/a) * (k - 3.0) * st
87+
+ 2.0*(a/r)*((1.0 - k) * st + s3t)
88+
- fac * s3t
89+
)
90+
end
91+
92+
function exact_error!(result,u, qpinfo)
93+
u_ex_kernel!(result,qpinfo)
94+
result .-= u
95+
result .= result .^2
96+
return nothing
97+
end
98+
99+
function solve_plate_with_hole(config::PlateConfig,grid::ExtendableGrid,outputzip::String,outputmetrics::String)
100+
101+
bfacemask!(grid,[0.,0.],[config.radius,config.radius],50)
102+
103+
PD = ProblemDescription("Linear elastic 2D Plate with hole, configuration "*config.id)
104+
u = Unknown("u"; name= "displacement")
105+
assign_unknown!(PD, u)
106+
𝐂 = plane_stress_elasticity_tensor(config.E,config.ν)
107+
LE_kernel_sym! = make_kernel(𝐂)
108+
assign_operator!(PD, BilinearOperator(LE_kernel_sym!, [εV(u,1.0)]))
109+
assign_operator!(PD, InterpolateBoundaryData(u, u_ex_kernel!; regions = [3,4], params = [config.radius,config.F,config.E,config.ν]))
110+
assign_operator!(PD, HomogeneousBoundaryData(u; regions = [1], mask = [1,0]))
111+
assign_operator!(PD, HomogeneousBoundaryData(u; regions = [2], mask = [0,1]))
112+
113+
FEType = H1Pk{2,2, config.element_order}
114+
FES = FESpace{FEType}(grid)
115+
sol = solve(PD,FES; timeroutputs = :hide)
116+
117+
u_ex = FEVector(FES; name="exact solution")
118+
interpolate!(u_ex.FEVectorBlocks[1],ON_CELLS,u_ex_kernel!;params = [config.radius,config.F,config.E,config.ν])
119+
u_exx = nodevalues(u_ex.FEVectorBlocks[1])[1,:]
120+
u_exy = nodevalues(u_ex.FEVectorBlocks[1])[2,:]
121+
122+
u_x = nodevalues(sol[u])[1,:]
123+
u_y = nodevalues(sol[u])[2,:]
124+
u_mag = sqrt.(u_x.*u_x.+u_y.*u_y)
125+
uex_mag = sqrt.(u_exx.*u_exx.+u_exy.*u_exy)
126+
127+
# TODO: Stress calculations
128+
129+
metrics = Metrics(0.)
130+
# TODO: L2 error, see Example301
131+
ErrorIntegrationExact = ItemIntegrator(exact_error!, [id(u)]; quadorder = 8,params = [config.radius,config.F,config.E,config.ν])
132+
133+
error = evaluate(ErrorIntegrationExact, sol)
134+
135+
L2error = sqrt(sum(error))
136+
137+
outputvtk = splitdir(outputzip)[1]*"/results_"*config.id*".vtu";
138+
writeVTK(outputvtk,grid;compress=false, u_x=u_x,u_y=u_y,u_mag=u_mag,uexx=u_exx,uexy=u_exy,uex=uex_mag)
139+
f = open(outputvtk,"r")
140+
vtkcontent = read(f,String)
141+
ZipWriter(outputzip) do w
142+
zip_newfile(w, "result_"*config.id*".vtu";compress=true)
143+
write(w,vtkcontent)
144+
end
145+
JSON.json(outputmetrics,metrics;pretty=true)
146+
147+
end
148+
149+
"run linear elastic plate with a hole using ExtendableFEM.jl"
150+
Fire.@main function run_simulation(;
151+
configfile::String="",
152+
meshfile::String="",
153+
outputzip::String="",
154+
outputmetrics::String=""
155+
)
156+
if(isempty(configfile))
157+
@error "No configuration file given"
158+
end
159+
config = parse_config(configfile)
160+
if(isempty(meshfile))
161+
@error "No mesh file given"
162+
end
163+
if(isempty(outputzip))
164+
@error "No output zip file given"
165+
end
166+
if(isempty(outputmetrics))
167+
@error "No output metrics file given"
168+
end
169+
grid = simplexgrid_from_gmsh(meshfile)
170+
solve_plate_with_hole(config,grid,outputzip,outputmetrics)
171+
172+
return
173+
end
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"researchProblem": {
3+
"description": "This benchmark describes a linear-elastic plate with a hole test case used in the NFDI-4Ing Model Validation Platform for comparing and validating computational modelling methods."
4+
},
5+
"rocrate": {
6+
"name": "NFDI4Ing Provenance",
7+
"description": "Benchmark for linear-elastic plate with a hole",
8+
"license": "https://opensource.org/licenses/MIT"
9+
}
10+
}

0 commit comments

Comments
 (0)