Skip to content

Commit 2326360

Browse files
committed
new solution metrics added
1 parent 7c9e2cf commit 2326360

3 files changed

Lines changed: 46 additions & 23 deletions

File tree

docs/benchmarks/linear elasticity/plate-with-hole.md

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -96,31 +96,40 @@ $$
9696

9797
In order to solve the weak formulation, the finite-element method (FEM) can be used. This method discretizes the domain $\Omega$ into so called finite elements that can for example be triangles or quadrilaterals in 2D. On these elements, ansatz functions are defined such that they are continous on the boundaries between elements. These functions form a basis for the solution space for an approximate solution $\boldsymbol{u}_h$ of the problem.
9898

99-
## Comparison of approximate solution with analytical solution
99+
## Solution metrics
100100

101-
The approxiamte solution and the analytical solution can be compared with the $L_2$ norm which is defined as
101+
The approximate solution is compared with the analytical solution using the $L_2$ norm which is defined as
102102

103103
$$
104-
\Vert \boldsymbol{u}\Vert_{L_2} = \sqrt{\int_\Omega \Vert\boldsymbol{u}(\boldsymbol{x})\Vert_2^2 \mathrm d \boldsymbol x}
104+
\Vert \boldsymbol{f}\Vert_{L_2} = \sqrt{\int_\Omega \left|\boldsymbol{f}(\boldsymbol{x})\right|^2 \mathrm d \boldsymbol x}
105105
$$
106106

107-
and the error in the $L_2$ norm
107+
This norm is computed for the error between the finite element solution and the analytical solution of displacements i.e., $\Vert \boldsymbol{u}-\boldsymbol{u}_h\Vert_{L_2}$.
108+
109+
The maximum displacement error is computed at the nodes of the finite element mesh with respect to the analytical solution.
108110

109111
$$
110-
e_{L_2} = \Vert \boldsymbol{u}-\boldsymbol{u}_h\Vert_{L_2}.
112+
e_{\max}
113+
=
114+
\max_{i \in \mathcal{N}}
115+
\left\|
116+
\mathbf{u}(\mathbf{x}_i)
117+
-
118+
\mathbf{u}_h(\mathbf{x}_i)
119+
\right\|
111120
$$
112121

113-
Alternatively, the sup norm can be used which is defined as
122+
where $\mathcal{N}$ denotes the set of nodes of the finite element mesh.
114123

115-
$$
116-
\Vert \boldsymbol{u}\Vert_{\inf} = \sup_{\boldsymbol x} \Vert \boldsymbol{u}(\boldsymbol{x}) \Vert
117-
$$
124+
Max Von-Mises stress
125+
126+
Displacement at the top-right corner of the plate.
127+
128+
The reaction force at the left boundary.
129+
130+
Number of Degrees of Freedom which supports in comparing the outputs between different meshes.
118131

119-
and the error in the sup norm
120132

121-
$$
122-
e_{\inf} = \Vert \boldsymbol{u}-\boldsymbol{u}_h\Vert_{\inf}.
123-
$$
124133

125134

126135
With these metrices, we can perform a convergence analysis for different approximations $\boldsymbol{u}_h$ which differ in the element size $h$. Plotting the error over the used element-size in a log-log plot lets us determine the convergence order of the approximation.

examples/linear-elastic-plate-with-hole/fenics/run_simulation.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ def traction_top_expr(x: np.ndarray) -> np.ndarray:
170170
reaction_left_y_local = df.fem.assemble_scalar(df.fem.form(traction[1] * ds(1)))
171171
reaction_left_x = MPI.COMM_WORLD.allreduce(reaction_left_x_local, op=MPI.SUM)
172172
reaction_left_y = MPI.COMM_WORLD.allreduce(reaction_left_y_local, op=MPI.SUM)
173+
num_dofs = V.dofmap.index_map.size_global * V.dofmap.index_map_bs
173174

174175

175176
# Compute L2 error norm between FE displacement and analytical displacement.
@@ -187,6 +188,14 @@ def analytical_displacement_expr(x: np.ndarray) -> np.ndarray:
187188
l2_error_sq_global = MPI.COMM_WORLD.allreduce(l2_error_sq_local, op=MPI.SUM)
188189
l2_error_displacement = np.sqrt(l2_error_sq_global)
189190

191+
# Compute max nodal displacement error magnitude (global across MPI)
192+
block_size = V.dofmap.index_map_bs
193+
nodal_error = (u.x.array - u_analytical.x.array).reshape(-1, block_size)
194+
max_displacement_error_nodes_local = np.max(np.linalg.norm(nodal_error, axis=1))
195+
max_displacement_error_nodes = MPI.COMM_WORLD.allreduce(
196+
max_displacement_error_nodes_local, op=MPI.MAX
197+
)
198+
190199
# Evaluate displacement at the specified evaluation point
191200
displacement_eval_point = np.array(
192201
[[displacement_evaluation_x, displacement_evaluation_y, 0.0]],
@@ -281,8 +290,6 @@ def mises_stress(u):
281290
) as vtk:
282291
vtk.write_function(mises_stress_nodes, 0.0)
283292

284-
# extract maximum von Mises stress
285-
max_mises_stress_nodes = np.max(mises_stress_nodes.x.array)
286293

287294
# Compute von Mises stress at quadrature (Gauss) points and extract maximum (global across MPI)
288295
quad_element = basix.ufl.quadrature_element(
@@ -319,9 +326,10 @@ def mises_stress(u):
319326

320327
# Save metrics
321328
metrics = {
322-
"max_von_mises_stress_nodes[Pa]": max_mises_stress_nodes,
323-
"max_von_mises_stress_gauss_points[Pa]": max_mises_stress_gauss_points,
329+
"number_of_dofs[-]": num_dofs,
330+
"max_von_mises_stress[Pa]": max_mises_stress_gauss_points,
324331
"l2_error_displacement[m]": l2_error_displacement,
332+
"max_displacement_error[m]": max_displacement_error_nodes,
325333
"reaction_force_left_boundary_x[N]": reaction_left_x,
326334
"reaction_force_left_boundary_y[N]": reaction_left_y,
327335
f"displacement_x_at_evaluation_point (x={displacement_evaluation_x}, y={displacement_evaluation_y})[m]": displacement_x_at_evaluation_point,

examples/linear-elastic-plate-with-hole/kratos/postprocess_results.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,8 @@ def postprocess_results(input_parameter_file, input_result_vtk, output_metrics_f
7777
L=L,
7878
load=load,
7979
)
80-
# Compute maximum von Mises stress at nodes and Gauss points.
81-
max_von_mises_stress_nodes = float(np.max(mesh.point_data["VON_MISES_STRESS"]))
82-
83-
max_von_mises_stress_gauss_points = max_von_mises_stress_nodes
80+
# Compute maximum von Mises stress at Gauss points.
81+
max_von_mises_stress_gauss_points = 0
8482
for key, values in mesh.cell_data.items():
8583
if "VON_MISES_STRESS" in key:
8684
max_von_mises_stress_gauss_points = float(np.max(values))
@@ -123,10 +121,18 @@ def postprocess_results(input_parameter_file, input_result_vtk, output_metrics_f
123121
else:
124122
displacement_x_at_evaluation_point = float(displacement_sampled[0, 0])
125123

124+
# Compute nodal displacement error (Euclidean norm of error vector at each node)
125+
nodal_displacement_error = np.linalg.norm(displacement - u_ref, axis=1)
126+
max_displacement_error_nodes = float(np.max(nodal_displacement_error))
127+
128+
# Compute the number of dofs
129+
num_dofs = int(mesh.n_points * 2)
130+
126131
metrics = {
127-
"max_von_mises_stress_nodes[Pa]": max_von_mises_stress_nodes,
128-
"max_von_mises_stress_gauss_points[Pa]": max_von_mises_stress_gauss_points,
132+
"number_of_dofs[-]": num_dofs,
133+
"max_von_mises_stress[Pa]": max_von_mises_stress_gauss_points,
129134
"l2_error_displacement[m]": l2_error_displacement,
135+
"max_displacement_error[m]": max_displacement_error_nodes,
130136
"reaction_force_left_boundary_x[N]": reaction_force_left_boundary_x,
131137
"reaction_force_left_boundary_y[N]": reaction_force_left_boundary_y,
132138
f"displacement_x_at_evaluation_point (x={displacement_evaluation_x}, y={displacement_evaluation_y})[m]": displacement_x_at_evaluation_point,

0 commit comments

Comments
 (0)