-
Notifications
You must be signed in to change notification settings - Fork 108
Implement Hessians and Identity Mapping for embedded elements #1257
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
base: master
Are you sure you want to change the base?
Changes from 6 commits
0eeecd9
06ac744
53e0939
7edb56b
8737dd3
831fd7f
158cc2b
8011c2f
6f7fbbd
7260a95
d3214ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,3 +24,4 @@ docs/src/changelog.md | |
| LocalPreferences.toml | ||
| docs/LocalPreferences.toml | ||
| benchmark/tune.json | ||
| .vscode/ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,7 @@ end | |
|
|
||
| @inline getjacobian(mv::MappingValues{<:Union{AbstractTensor, SMatrix}}) = mv.J | ||
| @inline gethessian(mv::MappingValues{<:Any, <:AbstractTensor}) = mv.H | ||
| @inline gethessian(mv::MappingValues{<:SMatrix, <:SArray}) = mv.H | ||
|
|
||
|
|
||
| """ | ||
|
|
@@ -112,6 +113,12 @@ geometric_interpolation(geo_mapping::GeometryMapping) = geo_mapping.ip | |
| @inline function otimes_helper(x::Vec{sdim}, dMdξ::Vec{rdim}) where {sdim, rdim} | ||
| return SMatrix{sdim, rdim}((x[i] * dMdξ[j] for i in 1:sdim, j in 1:rdim)...) | ||
| end | ||
|
|
||
| @inline otimes_helper(x::Vec{dim}, d2Mdξ2::Tensor{2, dim}) where {dim} = x ⊗ d2Mdξ2 | ||
| @inline function otimes_helper(x::Vec{sdim}, d2Mdξ2::Tensor{2, rdim}) where {sdim, rdim} | ||
| return SArray{Tuple{sdim, rdim, rdim}}((x[i] * d2Mdξ2[j, k] for i in 1:sdim, j in 1:rdim, k in 1:rdim)...) | ||
| end | ||
|
|
||
| # End of embedded hot-fixes | ||
|
|
||
| # For creating initial value | ||
|
|
@@ -124,6 +131,10 @@ end | |
| function otimes_returntype(#=typeof(x)=# ::Type{<:Vec{dim, Tx}}, #=typeof(d2Mdξ2)=# ::Type{<:Tensor{2, dim, TM}}) where {dim, Tx, TM} | ||
| return Tensor{3, dim, promote_type(Tx, TM)} | ||
| end | ||
| function otimes_returntype(::Type{<:Vec{sdim, Tx}}, #=typeof(d2Mdξ2)=# ::Type{<:Tensor{2, rdim, TM}}) where {sdim, rdim, Tx, TM} | ||
| return typeof(StaticArrays.@SArray zeros(promote_type(Tx, TM), sdim, rdim, rdim)) | ||
| end | ||
|
|
||
|
|
||
| @inline function calculate_mapping(::GeometryMapping{0}, q_point::Int, x::AbstractVector{<:Vec}) | ||
| return MappingValues(nothing, nothing) | ||
|
|
@@ -140,12 +151,13 @@ end | |
|
|
||
| @inline function calculate_mapping(geo_mapping::GeometryMapping{2}, q_point::Int, x::AbstractVector{<:Vec}) | ||
| J = zero(otimes_returntype(eltype(x), eltype(geo_mapping.dMdξ))) | ||
| sdim, rdim = size(J) | ||
| (rdim != sdim) && error("hessian for embedded elements not implemented (rdim=$rdim, sdim=$sdim)") | ||
| H = zero(otimes_returntype(eltype(x), eltype(geo_mapping.d2Mdξ2))) | ||
| @inbounds for j in 1:getngeobasefunctions(geo_mapping) | ||
| J += x[j] ⊗ geo_mapping.dMdξ[j, q_point] | ||
| H += x[j] ⊗ geo_mapping.d2Mdξ2[j, q_point] | ||
| J += otimes_helper(x[j], geo_mapping.dMdξ[j, q_point]) | ||
| H += otimes_helper(x[j], geo_mapping.d2Mdξ2[j, q_point]) | ||
|
|
||
| # J += x[j] ⊗ geo_mapping.dMdξ[j, q_point] | ||
| # H += x[j] ⊗ geo_mapping.d2Mdξ2[j, q_point] | ||
| end | ||
| return MappingValues(J, H) | ||
| end | ||
|
|
@@ -170,13 +182,16 @@ end | |
| @inline function calculate_mapping(gip::ScalarInterpolation, ξ::Vec{rdim, T}, x::AbstractVector{<:Vec{sdim}}, ::Val{2}) where {T, rdim, sdim} | ||
| n_basefuncs = getnbasefunctions(gip) | ||
| @boundscheck checkbounds(x, Base.OneTo(n_basefuncs)) | ||
| (rdim != sdim) && error("hessian for embedded elements not implemented (rdim=$rdim, sdim=$sdim)") | ||
| # (rdim != sdim) && error("hessian for embedded elements not implemented (rdim=$rdim, sdim=$sdim)") | ||
|
henrij22 marked this conversation as resolved.
Outdated
|
||
| J = zero(otimes_returntype(Vec{sdim, T}, Vec{rdim, T})) | ||
| H = zero(otimes_returntype(eltype(x), typeof(J))) | ||
| @inbounds for j in 1:n_basefuncs | ||
| d2Mdξ2, dMdξ, _ = reference_shape_hessian_gradient_and_value(gip, ξ, j) | ||
| J += x[j] ⊗ dMdξ | ||
| H += x[j] ⊗ d2Mdξ2 | ||
|
|
||
| J += otimes_helper(x[j], dMdξ) | ||
|
|
||
| # J += x[j] ⊗ dMdξ | ||
| # H += x[j] ⊗ d2Mdξ2 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. where did the computation of the hessian go?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aha, I only added it in above. This code then might not be tested atm. Also, what is the difference between |
||
| end | ||
| return MappingValues(J, H) | ||
| end | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -441,9 +441,83 @@ | |
| cv_vector = CellValues(qr, ip^2, ip^3; update_hessians = true) | ||
| cv_scalar = CellValues(qr, ip, ip^3; update_hessians = true) | ||
|
|
||
| coords = [Vec{3}((x[1], x[2], 0.0)) for x in Ferrite.reference_coordinates(ip)] | ||
| @test_throws ErrorException reinit!(cv_vector, coords) # Not implemented for embedded elements | ||
| @test_throws ErrorException reinit!(cv_scalar, coords) | ||
| coords = [Vec{3}((2x[1], 0.5x[2], rand())) for x in Ferrite.reference_coordinates(ip)] | ||
|
|
||
| # Test Scalar H | ||
| reinit!(cv_scalar, coords) | ||
|
|
||
| qp = 1 | ||
| H = Ferrite.calculate_mapping(cv_scalar.geo_mapping, qp, coords).H | ||
|
|
||
| d2Ndξ2 = cv_scalar.fun_values.d2Ndξ2 | ||
| ∂A₁∂₁ = Vec{3}(i -> getindex.(d2Ndξ2[:, qp], 1, 1) ⋅ getindex.(coords, i)) | ||
| ∂A₂∂₂ = Vec{3}(i -> getindex.(d2Ndξ2[:, qp], 2, 2) ⋅ getindex.(coords, i)) | ||
| ∂A₁∂₂ = Vec{3}(i -> getindex.(d2Ndξ2[:, qp], 1, 2) ⋅ getindex.(coords, i)) # = ∂A₂∂₁ | ||
| ∂A₂∂₁ = Vec{3}(i -> getindex.(d2Ndξ2[:, qp], 2, 1) ⋅ getindex.(coords, i)) | ||
|
|
||
| @test H[:, 1, 1] ≈ ∂A₁∂₁ | ||
| @test H[:, 2, 2] ≈ ∂A₂∂₂ | ||
| @test H[:, 1, 2] ≈ ∂A₁∂₂ | ||
| @test H[:, 2, 1] ≈ ∂A₂∂₁ | ||
|
|
||
|
|
||
| # Test Vector H | ||
| reinit!(cv_vector, coords) | ||
|
|
||
| H = Ferrite.calculate_mapping(cv_vector.geo_mapping, qp, coords).H | ||
|
|
||
| d2Ndξ2 = cv_vector.fun_values.d2Ndξ2 | ||
| ∂A₁∂₁ = Vec{3}(i -> getindex.(d2Ndξ2[1:2:18, qp], 1, 1, 1) ⋅ getindex.(coords, i)) | ||
| ∂A₂∂₂ = Vec{3}(i -> getindex.(d2Ndξ2[1:2:18, qp], 1, 2, 2) ⋅ getindex.(coords, i)) | ||
| ∂A₁∂₂ = Vec{3}(i -> getindex.(d2Ndξ2[1:2:18, qp], 1, 1, 2) ⋅ getindex.(coords, i)) # = ∂A₂∂₁ | ||
| ∂A₂∂₁ = Vec{3}(i -> getindex.(d2Ndξ2[1:2:18, qp], 1, 2, 1) ⋅ getindex.(coords, i)) | ||
|
|
||
|
|
||
| @test H[:, 1, 1] ≈ ∂A₁∂₁ | ||
| @test H[:, 2, 2] ≈ ∂A₂∂₂ | ||
| @test H[:, 1, 2] ≈ ∂A₁∂₂ | ||
| @test H[:, 2, 1] ≈ ∂A₂∂₁ | ||
|
|
||
| # Test Mapping Scalar | ||
| coords_scaled = [Vec{3}((2x[1], 0.5x[2], 0.0)) for x in Ferrite.reference_coordinates(ip)] | ||
| reinit!(cv_scalar, coords_scaled) | ||
|
|
||
| scale_x = 2.0 | ||
| scale_y = 0.5 | ||
|
|
||
| coords_ref = [Vec{3}((x[1], x[2], 0.0)) for x in Ferrite.reference_coordinates(ip)] | ||
| cv_ref = CellValues(qr, ip, ip^3; update_hessians = true) | ||
| reinit!(cv_ref, coords_ref) | ||
|
|
||
| # Test scaling embedded vs embedded | ||
| @test shape_hessian(cv_scalar, qp, 1)[1, 1] * scale_x^2 ≈ shape_hessian(cv_ref, qp, 1)[1, 1] | ||
| @test shape_hessian(cv_scalar, qp, 1)[2, 2] * scale_y^2 ≈ shape_hessian(cv_ref, qp, 1)[2, 2] | ||
| @test shape_hessian(cv_scalar, qp, 1)[3, 3] ≈ shape_hessian(cv_ref, qp, 1)[3, 3] | ||
| @test shape_hessian(cv_scalar, qp, 1)[1, 2] * scale_x * scale_y ≈ shape_hessian(cv_ref, qp, 1)[1, 2] | ||
| @test shape_hessian(cv_scalar, qp, 1)[2, 1] * scale_x * scale_y ≈ shape_hessian(cv_ref, qp, 1)[2, 1] | ||
|
|
||
| # Test Mapping Vector | ||
| cv_ref2 = CellValues(qr, ip^2, ip^3; update_hessians = true) | ||
| reinit!(cv_vector, coords_scaled) | ||
| reinit!(cv_ref2, coords_ref) | ||
|
|
||
| # Test scaling embedded vs embedded | ||
| @test shape_hessian(cv_vector, qp, 1)[1, 1, 1] * scale_x^2 ≈ shape_hessian(cv_ref2, qp, 1)[1, 1, 1] | ||
| @test shape_hessian(cv_vector, qp, 1)[1, 2, 2] * scale_y^2 ≈ shape_hessian(cv_ref2, qp, 1)[1, 2, 2] | ||
| @test shape_hessian(cv_vector, qp, 1)[1, 3, 3] ≈ shape_hessian(cv_ref2, qp, 1)[1, 3, 3] | ||
| @test shape_hessian(cv_vector, qp, 1)[1, 1, 2] * scale_x * scale_y ≈ shape_hessian(cv_ref2, qp, 1)[1, 1, 2] | ||
| @test shape_hessian(cv_vector, qp, 1)[1, 2, 1] * scale_x * scale_y ≈ shape_hessian(cv_ref2, qp, 1)[1, 2, 1] | ||
|
|
||
|
|
||
| # Test embedded vs non-embedded | ||
| cv_ref2n = CellValues(qr, ip^2, ip^2; update_hessians = true) | ||
| coords_refn = [Vec{2}((x[1], x[2])) for x in Ferrite.reference_coordinates(ip)] | ||
| reinit!(cv_ref2n, coords_refn) | ||
|
|
||
| @test shape_hessian(cv_ref2, qp, 1)[1, 1, 1] ≈ shape_hessian(cv_ref2n, qp, 1)[1, 1, 1] | ||
| @test shape_hessian(cv_ref2, qp, 1)[1, 2, 1] ≈ shape_hessian(cv_ref2n, qp, 1)[1, 2, 1] | ||
| @test shape_hessian(cv_ref2, qp, 1)[2, 1, 1] ≈ shape_hessian(cv_ref2n, qp, 1)[2, 1, 1] | ||
| @test shape_hessian(cv_ref2, qp, 1)[2, 2, 1] ≈ shape_hessian(cv_ref2n, qp, 1)[2, 2, 1] | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can add tests for the function_hessian also, as we do here: Can you add a similar test in this @testset for hessians? (I am not sure if function_value_from_physical_coord works with embedded elements)
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This might be more difficult than i first thought. For the embedded case, the function_value_from_physical_coord would need some signed distance search to find the parametric coord on the surface |
||
| end | ||
| end | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this type of construction of SArrays is fine, but I dont know how efficiently the compiler can optimize it, maybe someone else can review this part :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You were right, there are a lot of calls in there.
This implementation takes 24 ns on my system, I found a better version which only takes about 2 ns