Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test coverage for PointEval with nonlinear geometries #820

Merged
merged 2 commits into from
Aug 6, 2024

Conversation

termi-official
Copy link
Member

@termi-official termi-official commented Oct 13, 2023

Point evaluation on master just does not work on nonlinear geometries. Here is an attempt to make it work. Fixes #763 but might not completely fix the point eval.

TODO

  • docs
  • flexibility for inner solver not sure how. Should be a separate PR. How is now included during review.
  • expose inner solver parameters
  • fix point eval for mixed-dimensional and embedded grids

@termi-official termi-official changed the title Try to fix PointEval for nonlinear geometris Try to fix PointEval for nonlinear geometries Oct 13, 2023
@codecov-commenter
Copy link

codecov-commenter commented Oct 18, 2023

Codecov Report

Attention: Patch coverage is 96.00000% with 3 lines in your changes missing coverage. Please review.

Project coverage is 93.67%. Comparing base (1dad1fe) to head (4ee7c78).
Report is 2 commits behind head on master.

Files Patch % Lines
src/PointEvalHandler.jl 94.23% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #820      +/-   ##
==========================================
+ Coverage   93.58%   93.67%   +0.08%     
==========================================
  Files          39       39              
  Lines        5895     5990      +95     
==========================================
+ Hits         5517     5611      +94     
- Misses        378      379       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@termi-official termi-official marked this pull request as ready for review October 18, 2023 14:25
src/Grid/grid.jl Outdated Show resolved Hide resolved
@termi-official termi-official added the awaiting review PR is finished from the authors POV, waiting for feedback label Nov 15, 2023
src/PointEval/PointEvalHandler.jl Outdated Show resolved Hide resolved
src/Grid/utils.jl Outdated Show resolved Hide resolved
src/Grid/utils.jl Outdated Show resolved Hide resolved
src/Grid/utils.jl Outdated Show resolved Hide resolved
src/PointEval/PointEvalHandler.jl Outdated Show resolved Hide resolved
@termi-official termi-official changed the title Try to fix PointEval for nonlinear geometries Test coverage for PointEval with nonlinear geometries Jan 22, 2024
@termi-official
Copy link
Member Author

I moved the computation of J and x without the caches (i.e. GeometryMapping) into GeometryMapping.jl and common_values.jl and changed the signature for the PointEvalHandler to support also mixed-dimensional grids (which are e.g. obtained by mixing shells, beams and solid elements in one grid). These functions are up to further optimization in subsequent PRs (i.e. the stuff which I do in the GPU support PR).

I also fixed an oopsie in the line searcher (which we need to identify the cell boundary for nonlinear cells).

Docs have been added as suggested.

src/Grid/utils.jl Outdated Show resolved Hide resolved
Copy link
Member

@KnutAM KnutAM left a comment

Choose a reason for hiding this comment

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

Nice work - really cool with the ever-increasing support for embedded elements!

  • I would suggest leaving the inclusion of sample_random_point into src to a separate pr for now.

  • I like the renaming of x to cell_coordinates, but on the other hand such a renaming is unrelated? If renamed it should probably also be renamed in the reinit! methods? Should this renaming take place in a separate pr then?

I'm unsure regarding iterations for embedded elements. I would have thought we should find the value of $\xi$ which gives zero distance to the sought coordinate, $\boldsymbol{x}_0$, projected onto the element. And then potentially say didn't find if the distance to the element surface is too large.

$$\begin{align} \boldsymbol{x}(\boldsymbol{\xi}) &= \boldsymbol{x}_i N_i(\boldsymbol{\xi})\\\ \boldsymbol{r}(\boldsymbol{\xi}) &= [\boldsymbol{x}(\boldsymbol{\xi}) - \boldsymbol{x}_0] \cdot \frac{\partial\boldsymbol{x}}{\partial \boldsymbol{\xi}}, \quad \text{Potentially normalizing each column in }\frac{\partial\boldsymbol{x}}{\partial \boldsymbol{\xi}} \ \\\ \frac{\partial\boldsymbol{r}}{\partial \boldsymbol{\xi}} &= [\boldsymbol{x}(\boldsymbol{\xi}) - \boldsymbol{x}_0] \cdot \frac{\partial^2\boldsymbol{x}}{\partial \boldsymbol{\xi}^2} + \left[\frac{\partial\boldsymbol{x}}{\partial \boldsymbol{\xi}}\right]^T \cdot \left[ \frac{\partial\boldsymbol{x}}{\partial \boldsymbol{\xi}} \right] \end{align}$$

I suppose the pinv updates are then doing this in some quasi-newton fashion? (But last time I checked, this is quite slow, so probably the above algorithm is faster even with the higher order derivative calculations?)

But since we are using autodiff anyway to compute the gradients of hessians of the base functions, perhaps it would be equally efficient to just define the residual function and just iterate on this? I.e.

function residual(ξ::Vec{dim}, x0::Vec{dim}, ip, cell_coordinates::AbstractVector{<:Vec{dim}) where dim
    return spatial_coordinate(ip, ξ, cell_coordinates) - x0
end
function residual(ξ::Vec{dim}, x0::Vec{dim}, ip, cell_coordinates::AbstractVector{<:Vec{dim}) where dim
    dxdξ, x = gradient(ξ_ -> spatial_coordinate(ip, ξ_, cell_coordinates), :all)
    # do scaling if desired or needed?
    return (x - x0) \cdot dxdξ
end

(And even if not used this modified residual, I suppose the first definition of residual can be used to remove the need for the calculate_mapping_and_spatial_coordinate functions?

src/PointEvalHandler.jl Outdated Show resolved Hide resolved
src/FEValues/GeometryMapping.jl Outdated Show resolved Hide resolved
src/FEValues/common_values.jl Outdated Show resolved Hide resolved
src/FEValues/common_values.jl Show resolved Hide resolved
src/PointEvalHandler.jl Outdated Show resolved Hide resolved
@termi-official
Copy link
Member Author

Thanks for taking the time @KnutAM and @fredrikekre !

* I would suggest leaving the inclusion of `sample_random_point` into `src` to a separate pr for now.

* I like the renaming of `x` to `cell_coordinates`, but on the other hand such a renaming is unrelated? If renamed it should probably also be renamed in the `reinit!` methods? Should this renaming take place in a separate pr then?

I am not a big fan of making many small 1-5 line PRs (i.e. add docs, move function with same name from one file to another, ..) due to the associated overhead and dependencies, but I can make many tiny PRs if it is preferred.

I'm unsure regarding iterations for embedded elements. I would have thought we should find the value of ξ which gives zero distance to the sought coordinate, x0, projected onto the element. And then potentially say didn't find if the distance to the element surface is too large.

I suppose the pinv updates are then doing this in some quasi-newton fashion? (But last time I checked, this is quite slow, so probably the above algorithm is faster even with the higher order derivative calculations?)

My initial idea was also to have better modularity for the inner solver, but I could not find a good way how to do it. The rationale behind the current Newton solver for embedded (nonlinear) stuff is that it does not converge to a root if the coordinate is not in the element. You can interpret the solution of the linear system in a least square sense, as pinv returns by default the Moore-Penrose pseudoinverse. The problem with the projection is that you need additional infrastructure to handle the projections properly. Both approaches are valid tho.

But since we are using autodiff anyway to compute the gradients of hessians of the base functions, perhaps it would be equally efficient to just define the residual function and just iterate on this? I.e.

function residual(ξ::Vec{dim}, x0::Vec{dim}, ip, cell_coordinates::AbstractVector{<:Vec{dim}) where dim
    return spatial_coordinate(ip, ξ, cell_coordinates) - x0
end
function residual(ξ::Vec{dim}, x0::Vec{dim}, ip, cell_coordinates::AbstractVector{<:Vec{dim}) where dim
    dxdξ, x = gradient(ξ_ -> spatial_coordinate(ip, ξ_, cell_coordinates), :all)
    # do scaling if desired or needed?
    return (x - x0) \cdot dxdξ
end

(And even if not used this modified residual, I suppose the first definition of residual can be used to remove the need for the calculate_mapping_and_spatial_coordinate functions?

I am not sure if I follow the idea here. So you are proposing to use a gradient descend over a Newton with line search as default? I think this will end up with the same issues as master for points near (or even on) element boundaries.

@KnutAM
Copy link
Member

KnutAM commented Jan 23, 2024

I am not a big fan of making many small 1-5 line PRs (i.e. add docs, move function with same name from one file to another, ..) due to the associated overhead and dependencies, but I can make many tiny PRs if it is preferred.

I rather prefer the opposite, when one PR does one thing (of course - introducing a new feature should also include updated docs and test) and refactoring is left as a standalone pr as this makes the review easier. Furthermore, some changes such as the change from x to cell_coordinates in the function signature are easy and non-controversial and I feel confident review + merge on those. But algorithmic changes / new features etc. might require more discussions.

The reason for suggesting to wait with the inclusion of the testing functions was simply because I think a utils package or including these functions in core is worthwhile discussing, and would be bad to have that discussion stopping this pr.

I am not sure if I follow the idea here. So you are proposing to use a gradient descend over a Newton with line search as default? I think this will end up with the same issues as master for points near (or even on) element boundaries.

No, rather the opposite - the current implementation is a quasi-newton for embedded elements due to the pseudo-inverse. With that code the regular elements would have no change in algorithm, but with easy extension to the embedded elements (but there, it is not guaranteed that the problem is convex / has a solution, not sure if the pseudo inverse approach can circumvent this). I'm struggling with makie-issues, so to take a quick break I'll add a PR to this branch showing what I meant (and check if it actually works :)) (Edit: #876)

Copy link
Member

@KnutAM KnutAM left a comment

Choose a reason for hiding this comment

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

Long time since I looked at this (sorry!)
I think it would be good to move some unrelated changes to a separate PR, that will also keep the git blames easier to follow.

src/FEValues/GeometryMapping.jl Outdated Show resolved Hide resolved
src/FEValues/GeometryMapping.jl Outdated Show resolved Hide resolved
src/FEValues/GeometryMapping.jl Outdated Show resolved Hide resolved
src/FEValues/common_values.jl Outdated Show resolved Hide resolved
src/FEValues/common_values.jl Outdated Show resolved Hide resolved
src/PointEvalHandler.jl Outdated Show resolved Hide resolved
src/PointEvalHandler.jl Outdated Show resolved Hide resolved
src/FEValues/GeometryMapping.jl Outdated Show resolved Hide resolved
src/FEValues/GeometryMapping.jl Outdated Show resolved Hide resolved
src/FEValues/common_values.jl Show resolved Hide resolved
Copy link
Member

@KnutAM KnutAM 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 more comments, but otherwise lgtm!

src/FEValues/common_values.jl Outdated Show resolved Hide resolved
src/PointEvalHandler.jl Outdated Show resolved Hide resolved
test/test_pointevaluation.jl Outdated Show resolved Hide resolved
# set up tree structure for finding nearest nodes to points
kdtree = KDTree(reinterpret(Vec{dim,T}, getnodes(grid)))
nearest_nodes, _ = knn(kdtree, points, search_nneighbors, true)

cells = Vector{Union{Nothing, Int}}(nothing, length(points))
local_coords = Vector{Union{Nothing, Vec{dim, T}}}(nothing, length(points))
local_coords = Vector{Union{Nothing, Vec{1, T},Vec{2, T},Vec{3, T}}}(nothing, length(points))
Copy link
Member

Choose a reason for hiding this comment

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

What is the reason for this change?

Copy link
Member Author

Choose a reason for hiding this comment

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

Support for mixed-dimensional meshes. E.g. shells with beams.

Copy link
Member

Choose a reason for hiding this comment

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

Okay, and if there is a field that lives on both domains then?

Copy link
Member Author

Choose a reason for hiding this comment

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

I am not sure if I understand the question correctly. The point finder just gives you the reference coordinate and cell index for some spatial coordinate. If you have some spatial coordinate which lives on the boundary of two elements, then it is always up to the prayers of convergence which element is returned for now. Technically we need to distinguish this case from the case when the point is strictly in the interior of some element. But note that resolving this issue is orthogonal to the goal of this PR, which is about improving the handling of nonlinear geometries. Before this PR using either mixed-dimensional or embedded elements resulted in a cryptic error for users and it looks like changing this (and the solver to some pseudo-inverse instead of the actual inverse below) is sufficient to handle these cases. I can separate these 2 lines and the tests into a separate PR if you think it convolutes this PR too much

@fredrikekre fredrikekre removed the awaiting review PR is finished from the authors POV, waiting for feedback label Aug 6, 2024
@fredrikekre fredrikekre merged commit 064269d into master Aug 6, 2024
11 checks passed
@fredrikekre fredrikekre deleted the do/fix-pointeval-geovalues branch August 6, 2024 16:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bug in PointValues
5 participants