Add three-argument reinit! for InterfaceCellValues - #48
MelanieInky wants to merge 10 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #48 +/- ##
==========================================
+ Coverage 91.66% 92.57% +0.90%
==========================================
Files 6 6
Lines 348 350 +2
==========================================
+ Hits 319 324 +5
+ Misses 29 26 -3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…th superparametric elements)
|
Edit: as of 45a0ce4 my comment is no longer necessary as the issue was fixed upstream. n_coords_per_side = length(x) ÷ 2seems to work fine but it leaves me confused as to the distinction when the element is not isoparametric. We use (from the constructor) base_indices_here = collect( get_interface_index(ip, :here, i) for i in 1:getnbasefunctions(ip.base) )But the length of x is the number of geometric nodes * 2 (ip_geo), not the shape values (ip). I am not super well versed in non isoparametric elements so I left it as is. |
KnutAM
left a comment
There was a problem hiding this comment.
Looks good! Just a few small things to fix and test.
| return Ferrite.reinit_needs_cell(cv.here) || Ferrite.reinit_needs_cell(cv.there) | ||
| end | ||
|
|
||
| Ferrite.reinit!(cv::InterfaceCellValues, cc::CellCache) = reinit!(cv, getcells(cc.grid, Ferrite.cellid(cc)), cc.coords) |
There was a problem hiding this comment.
| Ferrite.reinit!(cv::InterfaceCellValues, cc::CellCache) = reinit!(cv, getcells(cc.grid, Ferrite.cellid(cc)), cc.coords) |
This can be deleted right since cv::AbstractCellValues and Ferrite.reinit_needs_cell defined above?
There was a problem hiding this comment.
You're right it can branch on Ferrite.jl version.
| end | ||
| end | ||
|
|
||
| @testset "reinit! with superparametric field (ip order 2, geometry order 1)" begin |
There was a problem hiding this comment.
Are these tests relevant for this PR? Seems like a different change (adding tests)? Isn't this already tested somewhere else?
There was a problem hiding this comment.
There was a typo that passed through tests before #47 that was fixed in commit 2ec45e7 in this PR.
The commit was a one line change:
- x_there = @view x[cv.base_indices_there]
+ x_there = @view x[cv.base_indices_there[1:n_coords_per_side]]
The tests didn't catch it because there was no appropriate covering when include_R==true with superparametric elements. Now there is so this is partially redundant.
Now the code has been changed upstream so the typo fix is no longer relevant, but the fact that no test caught it is still relevant. I will check again if this is tested since I just appended both this PR and #47 tests.
There was a problem hiding this comment.
Trimmed in 416431a, so there should not be any redundant part now
With your comments in mind I am not a fan of this testset I think what it tests should be included in other testsets, or maybe it should be expanded to handle rotations for a variety of geometric order? Something like (Current code passes this)
@testset "midplane_rotation under rigid rotation" begin
for (shape, cells, dim) in ((RefLine, (Line, QuadraticLine), 2),
(RefTriangle, (Triangle, QuadraticTriangle), 3),
(RefQuadrilateral, (Quadrilateral, QuadraticQuadrilateral), 3))
Q = dim == 2 ? rotation_tensor(0.7) :
rotation_tensor(Vec{3}((1.0, 2.0, 3.0)) / sqrt(14.0), 0.7)
for forder in (1, 2), gorder in (1, 2)
base_fip = Lagrange{shape, forder}()
base_gip = Lagrange{shape, gorder}()
fip = InterfaceCellInterpolation(base_fip)
gip = InterfaceCellInterpolation(base_gip)
qr = QuadratureRule{shape}(2)
cv = InterfaceCellValues(qr, fip, gip; include_R = true)
xh = [Vec{dim}(i -> i < dim ? ξ[i] : 0.0) for ξ in Ferrite.reference_coordinates(base_gip)]
xt = [x + Vec{dim}(i -> i == dim ? 1.0 : 0.0) for x in xh]
nn = length(xh)
C = cells[gorder]
cell = InterfaceCell(C(Tuple(1:nn)), C(Tuple(nn+1:2nn)))
x = vcat(xh, xt)[collect(cell.nodes)]
reinit!(cv, x)
R0 = [midplane_rotation(cv, qp) for qp in 1:getnquadpoints(cv)]
reinit!(cv, [Q ⋅ xi for xi in x])
for qp in 1:getnquadpoints(cv)
R = midplane_rotation(cv, qp)
@test tdot(R) ≈ one(R)
@test det(R) ≈ 1.0
@test R ≈ Q ⋅ R0[qp] # equivariance: rotating the cell rotates the frame
end
end
end
endThere was a problem hiding this comment.
Looks like a good test! Some suggestions to simplify and use the querry functions we have (not tested code so probably some bugs)
@testset "midplane_rotation under rigid rotation" begin
for base_cell in (Line, QuadraticLine, Triangle, QuadraticTriangle, Quadrilateral, QuadraticQuadrilateral)
dim = Ferrite.getrefdim(base_cell) + 1
base_gip = geometric_interpolation(base_cell)
gip = InterfaceCellInterpolation(base_gip)
shape = Ferrite.getrefshape(base_cell)
Q = dim == 2 ? rotation_tensor(0.7) :
rotation_tensor(Vec{3}((1.0, 2.0, 3.0)) / sqrt(14.0), 0.7)
for forder in (1, 2)
base_fip = Lagrange{shape, forder}()
fip = InterfaceCellInterpolation(base_fip)
qr = QuadratureRule{shape}(2)
cv = InterfaceCellValues(qr, fip, gip; include_R = true)
# Needs a comment to motivate what xh and xt are.
xh = [Vec{dim}(i -> i < dim ? ξ[i] : 0.0) for ξ in Ferrite.reference_coordinates(base_gip)]
xt = [x + Vec{dim}(i -> i == dim ? 1.0 : 0.0) for x in xh]
nn = length(xh)
cell = InterfaceCell(base_cell(Tuple(1:nn)), base_cell(Tuple(nn+1:2nn)))
x = vcat(xh, xt)[collect(cell.nodes)]
reinit!(cv, x)
R0 = [midplane_rotation(cv, qp) for qp in 1:getnquadpoints(cv)]
reinit!(cv, [Q ⋅ x_i for x_i in x])
for qp in 1:getnquadpoints(cv)
R = midplane_rotation(cv, qp)
@test tdot(R) ≈ one(R)
@test det(R) ≈ 1.0
@test R ≈ Q ⋅ R0[qp] # equivariance: rotating the cell rotates the frame
end
end
end
endFor nonlinear geometry, we could potentially ensure that also the geometry is nonlinear by doing e.g.
- xt = [x + Vec{dim}(i -> i == dim ? 1.0 : 0.0) for x in xh]
+ xt = [x + (1 + norm(x)^2)/2) * Vec{dim}(i -> i == dim ? 1.0 : 0.0) for x in xh]There was a problem hiding this comment.
Yes, that could be better! Then remove the full test and add in another PR!
| qr = QuadratureRule{RefTriangle}(1) | ||
| ip = InterfaceCellInterpolation(Lagrange{RefTriangle, 1}()) | ||
| cell = InterfaceCell(Triangle((1, 2, 3)), Triangle((4, 5, 6))) | ||
| x = repeat([rand(Vec{3}), rand(Vec{3}), rand(Vec{3})], 2) |
There was a problem hiding this comment.
Should also add a test with CellCache to include the Ferrite.reinit_needs_cell overload.
There was a problem hiding this comment.
Done. I am not sure how to handle the case where reinit_needs_cell == true because I don't think we support non identity mapping in this package yet.
There was a problem hiding this comment.
It would be possible to create a dummy interpolation to test with, but I think it is enough as it is now.
Add three-arguments reinit! for InterfaceCellValues. I am currently working on a cohesive zone model using FerriteAssembly and it uses the three argument versions of reinit!. Doing so leads to the error
"
ERROR: LoadError: MethodError: no method matching get_geo_mapping(::InterfaceCellValues{CellValues{Ferrite.FunctionValues{…}, Ferrite.GeometryMapping{…}, QuadratureRule{…}, Vector{…}}})
The function
get_geo_mappingexists, but no method is defined for this combination of argument types.Closest candidates are:
get_geo_mapping(::CellValues)
@ Ferrite ~/.julia/packages/Ferrite/e5O5M/src/FEValues/CellValues.jl:90
get_geo_mapping(::MultiFieldCellValues)
@ Ferrite ~/.julia/packages/Ferrite/e5O5M/src/FEValues/CellValues.jl:265
get_geo_mapping(::FacetValues)
@ Ferrite ~/.julia/packages/Ferrite/e5O5M/src/FEValues/FacetValues.jl:91
"
Implementing a three-argument version solves the issue. Current behavior:
I have added some AI generated tests. Also below an AI generated minimum working example using FerriteAssembly involving a simple cohesive element. I tested with and without the patch. It assembles fine with it and throws a MethodError without.