Skip to content

Add quadratic manufactured solution for 1D bar validation#10

Open
Fimache wants to merge 3 commits intomain-freefem-sofafrom
feature/1d-quadratic-mms
Open

Add quadratic manufactured solution for 1D bar validation#10
Fimache wants to merge 3 commits intomain-freefem-sofafrom
feature/1d-quadratic-mms

Conversation

@Fimache
Copy link
Copy Markdown

@Fimache Fimache commented Apr 13, 2026

This PR adds a quadratic manufactured solution for 1D bar elasticity to validate SOFA.

Copy link
Copy Markdown
Collaborator

@th-skam th-skam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few things to be careful of. We'll merge this PR after we make sure the results are correct.

Quick Points:

  1. This is an equilibrium problem, so we should use the static solver, not a time integrator.
  2. For SOFA, we must use components from the Elasticity plugin. The SpringForceField in the scene should not be there.
  3. The mesh is finer in FreeFEM than in SOFA.
  4. The errors are not consistently computed between FreeFEM and SOFA. To see this, look at the following images, generated by these scripts:
Image Image

What is obvious from the 1st image, is that although the solutions are very close, the error is orders of magnitude off for SOFA. Which indicates that the way we compute errors is wrong.

The second image also reveals something is wrong. In a previous PR, we simulated the distributed load over a bar. We saw super-convergence of the nodal error with quadratic polynomial solutions. For the continuous L2 norm, we saw the expected convergence. However, what I see here is:

  1. For FreeFEM: Super convergence of the nodal error is observed. Also, we see the expected convergence of the L2 norm (integrated over the 1-dimensional domain). That's also correct.
  2. For SOFA:
    • We are computing the RMS in the code which should also exhibit super-convergence. It doesn't because the meshes are not identical and require interpolation within elements. This converges by refining but at a wrong rate, caused by using the wrong h to compute it.

Let me know if anything is not clear.


rootNode.gravity = [0, 0, 0]

rootNode.dt = 1e6
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
rootNode.dt = 1e6
rootNode.dt = 1

Time shouldn't matter for solving this equilibrium problem.

Comment on lines +34 to +37
rootNode.addObject("EulerImplicitSolver",
name="odeSolver",
rayleighStiffness=0.0,
rayleighMass=0.0)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not use time marching to solve a static equilibrium problem.
Look at https://github.com/InfinyTech3D/Elasticity/blob/main-freefem-sofa/examples/Freefem/solution_man/cas_1D/test_sofa/scenario2.py to see how to set up the static solver. You've already done it.

Comment on lines +58 to +59
rootNode.addObject("StiffSpringForceField", name="springs",
spring=springs_str)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of this is testing the wrong thing. You should use the LinearStrainFEMForceField with Vec1d from the Elasticity plugin. The component you used only mimics what linear FEM does using a spring model.

Comment on lines +61 to +62
L = length
f_const = 2.0 * young_modulus / (L * L)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
L = length
f_const = 2.0 * young_modulus / (L * L)
f_const = 2.0 * young_modulus / (length * length)

No reason to redefine this, just use length. Substitute this further down.

raise ValueError(
f"Dimensions incompatibles : u_num={len(u_num)}, u_ref={len(u_ref)}."
)
return np.linalg.norm(u_num - u_ref) / np.sqrt(len(u_ref))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is RMS not just the L2 norm, right? Or something mixed. It's not resulting from integration over the domain as FreeFEM does. So the comparison is meaningless.

Comment on lines +50 to +56
def convergence_rate(h_arr, err_arr, floor=1e-13):
h_arr = np.asarray(h_arr)
err_arr = np.asarray(err_arr)
mask = err_arr > floor
if mask.sum() < 2:
return np.nan
return np.polyfit(np.log(h_arr[mask]), np.log(err_arr[mask]), 1)[0]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a fitting line on the log-log scatter plot to derive a slope value?

real E = $youngModulus;
int N = $nx;

meshL Th = segment(N, [x*L, 0, 0]);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
meshL Th = segment(N, [x*L, 0, 0]);
meshL Th = segment(N-1, [x*L, 0, 0]);

Be careful here. Look at this:
https://github.com/InfinyTech3D/Elasticity/pull/10/changes#diff-0597ab6b63a9f899e584c61ab4c1aaa3b98264f9ecbce0eae4c7ec166d2bc0c2R98-R119
You are passing the same nx to SOFA and FreeFEM.

  1. SOFA creates nx nodes
  2. FreeFEM creates nx elements -> nx + 1 nodes.
    So the meshes used are not the same and they don't have the same h. However, the same h is used in the convergence rate analysis.

Let's set it now: N will always refer to the number of nodes (never the elements).

Comment on lines +121 to +123
u_sofa_interp = np.interp(x_ff, x_sofa, u_sofa)
u_ex_ff = u_exact(x_ff, L)
err_sofa = l2_error_discrete(u_sofa_interp, u_ex_ff)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why interpolate on a simple 1D example ? If you look at the work we did in previous PRs, we don't need to interpolate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants