Issue 525 FE Basis refactor implementation#561
Conversation
updating fork repo with upstream head
update main to upstream branch
… unit tests for refactored basis functions
|
From the CI test cases we got the following:
The affected meshes are |
|
In the FSI fluid assembly,
That means:
Therefore the Hessian is not just participating in the tangent assembly and may not necessarily converge on the same solution as the current reference vtu. This is also why we do not see such CI failures for test cases that use Tet elements because |
|
Confirmed. The difference in accuracy for the FSI test cases when employing the new FE Basis functions is due to the non-zero Hessian values for the reference elements. We can see that when we zero-out the values for the Hessian on the
|
|
@zasexton Were Hessians also zeroed out in the Fortran svFSI? I think we should update reference solutions unless there is a good reason to use zero Hessians. @sujaldave, @hanzhao2020 Is there any reason (in VMS or something) to force |
|
@aabrown100-git yes, the Hessian was also effectively zero for the |
Regenerate affected FSI and FSI-ustruct HEX8 result_005.vtu references for the FE Basis path with nonzero HEX8 Hessian contributions. Update the pipe_3d PETSc and Trilinos references to match the base pipe_3d reference, preserving the existing shared-reference pattern across linear algebra variants.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #561 +/- ##
==========================================
+ Coverage 69.08% 71.73% +2.64%
==========================================
Files 181 237 +56
Lines 34256 37019 +2763
Branches 5931 6471 +540
==========================================
+ Hits 23665 26554 +2889
+ Misses 10454 10247 -207
- Partials 137 218 +81 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
… reference tables and related unit tests
…emoved associated unit tests for these changes
…two basis function infrastructures
aabrown100-git
left a comment
There was a problem hiding this comment.
Left some comments!
|
@zasexton Also, are you planning on adding an independent design document? Something I found helpful was asking Codex to explain the flow from mesh input to basis function generation to mapping to existing |
|
In general, I only assume that we derive the topology from the mesh information. Although often mesh structure can imply interpolation order, this does not always have to be true; I believe the solution basis order may be a separate discretization choice. The geometry mapping basis should always be interpolated from the mesh nodes provided, but the field solution basis functions need not be assumed the same. One practical example could be the Taylor-Hood elements where mixed fields expect different solution field orders (e.g. velocity P2, pressure P1). If we only allow for isoparametric order then these types of mixed order formulations require much more delicate flagging and checking (which I think might be the case currently). So for flow diagram type of illustration, any mesh information would be directly linked to the geometric basis while the field solution basis/orders rely on information from practical formulation choices. |
… types. replaced custom math vector/matrix implementations for Eigen-backed implementations
… and using `SVMP_HERE` for file, line, and function source location information
…es in doxygen are now reserved for c++ modules
|
@zasexton Thanks for explaining. I think a DESIGN.md in the |
|
I think I'll plan to add these notes to the |
|
@zasexton good idea! Will that be written in the comments of the code, or a separate file? |
| namespace { | ||
| using Vec3 = math::Vector<Real, 3>; | ||
|
|
||
| void store_gradient(const Gradient& gradient, Real* dst) { |
There was a problem hiding this comment.
@zasexton What is all of this Real type about ? Let's get rid of it unless there is a very very very good reason to use it.
There was a problem hiding this comment.
Right now Real is an alias for the scalar type and defaults to double. My thoughts were that as we refactored the FE machinery we would use Real which would serve as the solver-wide scalar alias policy. Specifically I implemented this in case there was ever a need to switch to float for a type of GPU support in the future. I can go ahead and replace these if this seems to be more confusion than it is worth.
| Real t, | ||
| Real* values, | ||
| Real* gradients, | ||
| Real* hessians) { |
There was a problem hiding this comment.
I know, I know....my thoughts here: these calls allow for genuine zero-allocation evaluation paths for solver production runs and compiler restriction optimizations. The evaluate_values along with their gradient and hessian analogues offer the safe, sized, allocated API while the *_to methods offer these allocation-free methods. I could replace the raw pointers with std::span and remove the restrict to avoid directly using raw pointers while maintaining the flat, non-owning, no-allocation buffer model. We just wouldn't be able to use the compiler restrict anymore which is probably fine if we are always caching these values anyway.
| dst[2] = gradient[2]; | ||
| } | ||
|
|
||
| void evaluate_hex8_reference(Real r, |
There was a problem hiding this comment.
@zasexton What exactly is this function doing and what are the parameters ?
Isn't the Hessian of a hex8 element 0 ?
There was a problem hiding this comment.
The pure second derivative terms are 0 but the mixed terms are not.
There was a problem hiding this comment.
@zasexton What I meant was why did the original code set the Hessian to 0 and why is that being changed ?
There was a problem hiding this comment.
I'm not sure why the original code set the Hessian to zero...in the svFSI the Hessian is zero initialized and never populated. So the svFSI and svMultiPhysics codes are doing the same thing I'm just not sure why
There was a problem hiding this comment.
as for why it's being changed: I don't think it makes sense that a change in the element type should affect the formulated residual that is being solved. Either we chose to zero out the Hessian completely (effectively eliminating these terms in the formulation) or we consider reference solutions that appropriately include these terms (as seems to be the case in the fsi formulation intent). For linear tet elements this doesn't matter since the Hessian is completely zero, but this is a special case.
There was a problem hiding this comment.
I think we should include non-zero Hessians unless there is a real numerical reason not to, but I don't think that's the case (although I haven't looked closely into this)
There was a problem hiding this comment.
If I remember correctly, the convergence of residual-based stabilization (SUPG, VMS-LES) rests on the assumption that the residual appearing in the stabilization terms is zero if evaluated on the exact solution (that is, the method must be consistent, or weakly consistent at least). If that residual does not include the laplacian term, then there is an inconsistency, and the method is not convergent. Optimistically, the error (numerical vs. exact solution) will stagnate as the mesh is refined. In light of this, I think that neglecting the Hessian is wrong, and including it is correct.
I do not know how this will affect the convergence properties of the non-linear and linear solvers and the system's conditioning - they might be better or they might be worse. Most likely they will vary between scenarios. But I think this is a secondary issue, and should not affect the decision of whether to include the Hessian or not.
| values[i] = Real(0.125) * ar * bs * ct; | ||
| } | ||
| if (gradients) { | ||
| Real* g = gradients + i * 3u; |
There was a problem hiding this comment.
@zasexton Offsetting from a raw pointer ? Yuck, like C !
There was a problem hiding this comment.
mainly I'm trying to avoid having to create temporary Gradient or Hessian objects within functions that are internal. since the output buffer shape is pretty simple for gradients and hessians I opted for the offsets. I can change these if to have an accessor if you think this would be better long-term.
|
@aabrown100-git I'm intending for it to live in the |
michelebucelli
left a comment
There was a problem hiding this comment.
Thanks @zasexton, I left a few comments mostly related to housekeeping - I haven't yet dug into the more logic-heavy part of the PR.
| # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
|
|
||
| set(CMAKE_CXX_STANDARD 17) | ||
| set(CMAKE_CXX_STANDARD 20) |
There was a problem hiding this comment.
What features from C++ 20 are required now?
I don't know about recent updates, but until last year or so compiler support for C++ 20 was still a bit sketchy, and requiring developers to update to the latest compiler versions might be a big ask, especially in HPC environments.
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20") |
There was a problem hiding this comment.
Similar to the above, but also: would it make sense to take this version number from the CMake variable CMAKE_CXX_STANDARD?
| MATHJAX_VERSION = MathJax_2 | ||
| MATHJAX_FORMAT = HTML-CSS | ||
| MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 | ||
| MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols |
There was a problem hiding this comment.
Just out of curiosity, what prompted this downgrading to version 2? Are there new features that are currently unsupported?
| #include <atomic> | ||
| #ifdef EIGEN_USE_GPU | ||
| #include <chrono> | ||
| #endif |
There was a problem hiding this comment.
Do I understand correctly that these lines are changing code inside the Eigen code? If so, this seems potentially dangerous to me, as we risk branching into a customized version of Eigen that might be hard to update with new Eigen versions if necessary.
What issue prompted introducing this change? Maybe we can figure out a workaround.



Current situation
Tracks #525.
The solver currently relies on legacy table-driven shape-function paths in
nn.cpp, which makes basis evaluation, Hessian support, node-ordering validation, and parity testing difficult to extend. This PR introduces a self-contained FE Basis layer while preserving the existing solver-facing storage contracts.Release Notes
nn::get_gnnandnn::get_gn_nxxvolume/face evaluations through the new FE Basis adapter.SVMP_BASIS_MODE=auto|legacy|feto compare legacy and FE Basis evaluation paths at runtime.Documentation
SVMP_BASIS_MODE.Testing
tests/unitTests/FE/Basisfor Lagrange/Serendipity basis evaluation, Hessians, cache/factory behavior, error paths, node ordering, solver adapter parity, and supported mapped element coverage.tests/unitTests/FE/Mathfor matrix/vector operations, expression helpers, math constants, and dense linear algebra.Code of Conduct & Contributing Guidelines