Skip to content

Commit ea403aa

Browse files
authored
Merge pull request #1071 from aerappa/tangent_vector
Implementation of `get_tangent_vector` with similar behavior as `get_normal_vector`
2 parents b568864 + e7726f4 commit ea403aa

9 files changed

+83
-3
lines changed

NEWS.md

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.18.10] - 2025-01-13
9+
10+
### Added
11+
12+
- Added corresponding function `get_tangent_vector` to `get_normal_vector`. This method calculates the (unique up to sign) tangential unit vector to edges in 2D meshes, by rotating the normal (nx, ny) -> (ny, -nx). Since PR[#1071](https://github.com/gridap/Gridap.jl/pull/1071).
13+
814
## [0.18.9] - 2025-01-13
915

1016

src/CellData/CellData.jl

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export Integrand
6161
export
6262
export CellDof
6363
export get_normal_vector
64+
export get_tangent_vector
6465
export get_cell_measure
6566
export Interpolable
6667
export KDTreeSearch

src/CellData/CellFields.jl

+18-3
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,20 @@ end
113113

114114
function get_normal_vector(trian::Triangulation)
115115
cell_normal = get_facet_normal(trian)
116-
get_normal_vector(trian,cell_normal)
116+
get_normal_vector(trian, cell_normal)
117117
end
118118

119-
function get_normal_vector(trian::Triangulation,cell_normal::AbstractArray)
120-
GenericCellField(cell_normal,trian,ReferenceDomain())
119+
function get_tangent_vector(trian::Triangulation)
120+
cell_tangent = get_edge_tangent(trian)
121+
get_tangent_vector(trian, cell_tangent)
122+
end
123+
124+
function get_normal_vector(trian::Triangulation,cell_vectors::AbstractArray)
125+
GenericCellField(cell_vectors,trian,ReferenceDomain())
126+
end
127+
128+
function get_tangent_vector(trian::Triangulation,cell_vectors::AbstractArray)
129+
GenericCellField(cell_vectors,trian,ReferenceDomain())
121130
end
122131

123132
evaluate!(cache,f::Function,x::CellPoint) = CellField(f,get_triangulation(x))(x)
@@ -712,6 +721,12 @@ function get_normal_vector(trian::Triangulation,cell_normal::SkeletonPair)
712721
SkeletonPair(plus,minus)
713722
end
714723

724+
function get_tangent_vector(trian::Triangulation,cell_tangent::SkeletonPair)
725+
plus = get_normal_vector(trian,cell_tangent.plus)
726+
minus = get_normal_vector(trian,cell_tangent.minus)
727+
SkeletonPair(plus,minus)
728+
end
729+
715730
for op in (:outer,:*,:dot)
716731
@eval begin
717732
($op)(a::CellField,b::SkeletonPair{<:CellField}) = Operation($op)(a,b)

src/Exports.jl

+1
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ using Gridap.TensorValues: ⊗; export ⊗
141141
@publish CellData mean
142142
@publish CellData update_state!
143143
@publish CellData get_normal_vector
144+
@publish CellData get_tangent_vector
144145
using Gridap.CellData: ∫; export
145146
@publish CellData get_cell_measure
146147
@publish CellData get_physical_coordinate

src/Geometry/BoundaryTriangulations.jl

+18
Original file line numberDiff line numberDiff line change
@@ -238,10 +238,28 @@ function get_facet_normal(trian::BoundaryTriangulation, boundary_trian_glue::Fac
238238
Fields.MemoArray(face_s_n)
239239
end
240240

241+
function get_edge_tangent(trian::BoundaryTriangulation, boundary_trian_glue::FaceToCellGlue)
242+
243+
face_s_n = get_facet_normal(trian, boundary_trian_glue)
244+
245+
rotate_normal_2d(n) = VectorValue(n[2], -n[1])
246+
247+
face_s_t = lazy_map(Operation(rotate_normal_2d), face_s_n)
248+
249+
return Fields.MemoArray(face_s_t)
250+
end
251+
252+
function get_edge_tangent(trian::BoundaryTriangulation{Dc,Dp,A,<:FaceToCellGlue}) where {Dc,Dp,A}
253+
Dp != 2 && error("get_edge_tangent only implemented for 2D")
254+
glue = trian.glue
255+
get_edge_tangent(trian, glue)
256+
end
257+
241258
function get_facet_normal(trian::BoundaryTriangulation{Dc,Dp,A,<:FaceToCellGlue}) where {Dc,Dp,A}
242259
glue = trian.glue
243260
get_facet_normal(trian, glue)
244261
end
262+
245263
function push_normal(invJt,n)
246264
v = invJtn
247265
m = sqrt(inner(v,v))

src/Geometry/Geometry.jl

+2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ import Gridap.ReferenceFEs: num_cell_dims
7070
import Gridap.ReferenceFEs: num_point_dims
7171
import Gridap.ReferenceFEs: simplexify
7272
import Gridap.ReferenceFEs: get_facet_normal
73+
import Gridap.ReferenceFEs: get_edge_tangent
7374
import Gridap.ReferenceFEs: Quadrature
7475

7576
export GridTopology
@@ -107,6 +108,7 @@ export get_cell_ref_coordinates
107108
export get_cell_reffe
108109
export get_cell_shapefuns
109110
export get_facet_normal
111+
export get_edge_tangent
110112
export test_triangulation
111113
export get_cell_map
112114

src/Geometry/SkeletonTriangulations.jl

+6
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ function get_facet_normal(trian::SkeletonTriangulation)
9494
SkeletonPair(plus,minus)
9595
end
9696

97+
function get_edge_tangent(trian::SkeletonTriangulation)
98+
plus = get_edge_tangent(trian.plus)
99+
minus = get_edge_tangent(trian.minus)
100+
SkeletonPair(plus,minus)
101+
end
102+
97103
# Related with CompositeTriangulation
98104
function _compose_glues(rglue::FaceToFaceGlue,dglue::SkeletonPair)
99105
plus = _compose_glues(rglue,dglue.plus)

test/GeometryTests/BoundaryTriangulationsTests.jl

+15
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ face_to_nvec_s = lazy_map(evaluate,face_to_nvec,face_to_s)
110110
test_array(face_to_nvec_s,collect(face_to_nvec_s))
111111
@test isa(face_to_nvec_s,Geometry.FaceCompressedVector)
112112

113+
face_to_tvec = get_edge_tangent(btrian)
114+
face_to_tvec_s = lazy_map(evaluate,face_to_tvec,face_to_s)
115+
test_array(face_to_tvec_s,collect(face_to_tvec_s))
116+
@test isa(face_to_tvec_s,Geometry.FaceCompressedVector)
117+
113118
#print_op_tree(face_shapefuns_q)
114119
#print_op_tree(face_grad_shapefuns_q)
115120
#print_op_tree(face_to_nvec_s)
@@ -147,6 +152,16 @@ r = Vector{Point{2,Float64}}[
147152
[(0.0,1.0),(0.0,1.0)],[(1.0,-0.0),(1.0,-0.0)]]
148153
test_array(nvec_s,r)
149154

155+
tvec = get_edge_tangent(btrian)
156+
157+
tvec_s = lazy_map(evaluate,tvec,s)
158+
159+
rotate_90(x) = [Point(x[1][2], -x[1][1]), Point(x[2][2], -x[2][1])]
160+
161+
rr = rotate_90.(r)
162+
163+
test_array(rr, tvec_s)
164+
150165
cellids = collect(1:num_cells(model))
151166

152167
face_to_cellid = lazy_map(Reindex(cellids),glue.tface_to_mface)

test/GeometryTests/SkeletonTriangulationsTests.jl

+16
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module SkeletonTriangulationsTests
22

33
using Test
4+
using Gridap
45
using Gridap.TensorValues
56
using Gridap.Arrays
67
using Gridap.Fields
@@ -52,6 +53,7 @@ vn = get_facet_normal(vtrian)
5253
Ω = Triangulation(model)
5354
Γ = BoundaryTriangulation(model)
5455
Λ = SkeletonTriangulation(Γ)
56+
normal = get_normal_vector(Λ)
5557
@test Λ.rtrian === Γ
5658
@test isa.dtrian,SkeletonTriangulation)
5759
glue = get_glue(Λ,Val(3))
@@ -110,13 +112,27 @@ itrian = InterfaceTriangulation(Ω_in,Ω_out)
110112
#writevtk(trian,"trian",celldata=["inout"=>cell_to_inout])
111113
#writevtk(itrian,"itrian",nsubcells=10,cellfields=["ni"=>ni,"nl"=>nl,"nr"=>nr])
112114

115+
ti = get_tangent_vector(itrian)
116+
tl = get_tangent_vector(ltrian)
117+
tr = get_tangent_vector(rtrian)
118+
119+
@test ti isa SkeletonPair
120+
@test tl isa Gridap.CellData.GenericCellField
121+
@test tr isa Gridap.CellData.GenericCellField
122+
113123
reffe = LagrangianRefFE(Float64,QUAD,(2,2))
114124
conf = CDConformity((CONT,DISC))
115125
face_own_dofs = get_face_own_dofs(reffe,conf)
116126
strian = SkeletonTriangulation(model,reffe,face_own_dofs)
117127
test_triangulation(strian)
118128
ns = get_facet_normal(strian)
129+
ts = get_edge_tangent(strian)
119130
@test length(ns.⁺) == num_cells(strian)
131+
@test length(ts.⁺) == num_cells(strian)
132+
# Test orthogonality
133+
should_be_zero = lazy_map(Broadcasting(Operation(dot)), ns.plus, ts.plus)
134+
ndot_t_evaluated = evaluate(should_be_zero, VectorValue.(0:0.05:1))
135+
@test maximum(ndot_t_evaluated) < 1e-15
120136

121137
#using Gridap.Visualization
122138
#writevtk(strian,"strian",cellfields=["normal"=>ns])

0 commit comments

Comments
 (0)