Skip to content

Commit cd083ea

Browse files
committed
Add ExtendableFEM.jl benchmark implementation
1 parent 8a03d3b commit cd083ea

File tree

3 files changed

+219
-0
lines changed

3 files changed

+219
-0
lines changed
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

0 commit comments

Comments
 (0)