Skip to content

Commit 618d57c

Browse files
Merge pull request #799 from kishore-nori/AD-for-SkeletonIntegration
Extended `gradient` for `MultiField` functionals involving Skeleton integration
2 parents 56b97ea + 24a70ad commit 618d57c

File tree

5 files changed

+232
-11
lines changed

5 files changed

+232
-11
lines changed

NEWS.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77
## Unreleased
88

99
### Added
10-
- Added functionality to take gradient of functional involving integration (`DomainContribution`) over Skeleton faces, with respect to the degrees of freedom `FEFunction`. The interface remains the same - `gradient(f,uh)`. Since PR [#797](https://github.com/gridap/Gridap.jl/pull/797)
10+
- Added functionality to take gradient of functional involving integration (`DomainContribution`) over Skeleton faces, with respect to the degrees-of-freedom `FEFunction`. The interface remains the same - `gradient(f,uh)`. Since PR [#797](https://github.com/gridap/Gridap.jl/pull/797)
11+
- Extended the `MultiField` functional gradient (with respect to degrees-of-freedom of `MultiFieldFEFunction`) to functionals involving Skeleton integration. The interface remains the same `gradient(f,xh)`. Since PR [#799](https://github.com/gridap/Gridap.jl/pull/799)
1112

1213
## [0.17.13] - 2022-05-31
1314

src/MultiField/MultiField.jl

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ using Gridap.Fields
1818
using Gridap.FESpaces: FEBasis, TestBasis, TrialBasis, get_cell_dof_values
1919
using Gridap.FESpaces: SingleFieldFEBasis, TestBasis, TrialBasis
2020
using Gridap.CellData: CellFieldAt
21+
using Gridap.CellData: SkeletonCellFieldPair
22+
2123
import Gridap.Fields: gradient, DIV, ∇∇
2224

2325
using ForwardDiff

src/MultiField/MultiFieldFEAutodiff.jl

+76-10
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,22 @@ function _get_cell_dofs_field_offsets(uh::MultiFieldFEFunction)
1515
dofs_field_offsets
1616
end
1717

18+
function _restructure_cell_grad!(
19+
cell_grad, uh::MultiFieldFEFunction, trian)
20+
21+
monolithic_result=cell_grad
22+
blocks = [] # TO-DO type unstable. How can I infer the type of its entries?
23+
nfields = length(uh.fe_space.spaces)
24+
cell_dofs_field_offsets=_get_cell_dofs_field_offsets(uh)
25+
for i in 1:nfields
26+
view_range=cell_dofs_field_offsets[i]:cell_dofs_field_offsets[i+1]-1
27+
block=lazy_map(x->view(x,view_range),monolithic_result)
28+
append!(blocks,[block])
29+
end
30+
cell_grad=lazy_map(BlockMap(nfields,collect(1:nfields)),blocks...)
31+
cell_grad
32+
end
33+
1834
function FESpaces._gradient(f,uh::MultiFieldFEFunction,fuh::DomainContribution)
1935
terms = DomainContribution()
2036
U = get_fe_space(uh)
@@ -23,16 +39,7 @@ function FESpaces._gradient(f,uh::MultiFieldFEFunction,fuh::DomainContribution)
2339
cell_u = lazy_map(DensifyInnerMostBlockLevelMap(),get_cell_dof_values(uh))
2440
cell_id = FESpaces._compute_cell_ids(uh,trian)
2541
cell_grad = autodiff_array_gradient(g,cell_u,cell_id)
26-
monolithic_result=cell_grad
27-
blocks = [] # TO-DO type unstable. How can I infer the type of its entries?
28-
nfields = length(U.spaces)
29-
cell_dofs_field_offsets=_get_cell_dofs_field_offsets(uh)
30-
for i in 1:nfields
31-
view_range=cell_dofs_field_offsets[i]:cell_dofs_field_offsets[i+1]-1
32-
block=lazy_map(x->view(x,view_range),monolithic_result)
33-
append!(blocks,[block])
34-
end
35-
cell_grad=lazy_map(BlockMap(nfields,collect(1:nfields)),blocks...)
42+
cell_grad = _restructure_cell_grad!(cell_grad, uh, trian)
3643
add_contribution!(terms,trian,cell_grad)
3744
end
3845
terms
@@ -147,3 +154,62 @@ function FESpaces._hessian(f,uh::MultiFieldFEFunction,fuh::DomainContribution)
147154
end
148155
terms
149156
end
157+
158+
# overloads for AD of SkeletonTriangulation DomainContribution with MultiFields
159+
160+
function FESpaces._change_argument(
161+
op::typeof(gradient),f,trian::SkeletonTriangulation,uh::MultiFieldFEFunction)
162+
163+
U = get_fe_space(uh)
164+
function g(cell_u)
165+
single_fields_plus = SkeletonCellFieldPair[]
166+
single_fields_minus = SkeletonCellFieldPair[]
167+
nfields = length(U.spaces)
168+
cell_dofs_field_offsets=_get_cell_dofs_field_offsets(uh)
169+
for i in 1:nfields
170+
view_range=cell_dofs_field_offsets[i]:cell_dofs_field_offsets[i+1]-1
171+
cell_values_field = lazy_map(a->view(a,view_range),cell_u)
172+
cf_dual = CellField(U.spaces[i],cell_values_field)
173+
scfp_plus = SkeletonCellFieldPair(cf_dual, uh[i])
174+
scfp_minus = SkeletonCellFieldPair(uh[i], cf_dual)
175+
push!(single_fields_plus,scfp_plus)
176+
push!(single_fields_minus,scfp_minus)
177+
end
178+
xh_plus = MultiFieldCellField(single_fields_plus)
179+
xh_minus = MultiFieldCellField(single_fields_minus)
180+
cell_grad_plus = f(xh_plus)
181+
cell_grad_minus = f(xh_minus)
182+
get_contribution(cell_grad_plus,trian), get_contribution(cell_grad_minus,trian)
183+
end
184+
g
185+
end
186+
187+
function FESpaces._change_argument(
188+
op::typeof(jacobian),f,trian::SkeletonTriangulation,uh::MultiFieldFEFunction)
189+
190+
@notimplemented
191+
end
192+
193+
function FESpaces._change_argument(
194+
op::typeof(hessian),f,trian::SkeletonTriangulation,uh::MultiFieldFEFunction)
195+
196+
@notimplemented
197+
end
198+
199+
function _restructure_cell_grad!(
200+
cell_grad, uh::MultiFieldFEFunction, trian::SkeletonTriangulation)
201+
202+
monolithic_result=cell_grad
203+
blocks = [] # TO-DO type unstable. How can I infer the type of its entries?
204+
nfields = length(uh.fe_space.spaces)
205+
cell_dofs_field_offsets=_get_cell_dofs_field_offsets(uh)
206+
for i in 1:nfields
207+
view_range=cell_dofs_field_offsets[i]:cell_dofs_field_offsets[i+1]-1
208+
block_plus = lazy_map(x->view(x[1],view_range),monolithic_result)
209+
block_minus = lazy_map(x->view(x[2],view_range),monolithic_result)
210+
block = lazy_map(BlockMap(2,[1,2]),block_plus,block_minus)
211+
push!(blocks,block)
212+
end
213+
cell_grad=lazy_map(BlockMap(nfields,collect(1:nfields)),blocks...)
214+
cell_grad
215+
end

test/CellDataTests/SkeletonCellFieldPairTests.jl

+85
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ using Gridap.Fields
99
using Gridap.ReferenceFEs
1010
using Gridap.Geometry
1111
using Gridap.CellData
12+
using Gridap.MultiField
1213
using ForwardDiff
1314

1415
model = CartesianDiscreteModel((0.,1.,0.,1.),(3,3))
@@ -88,4 +89,88 @@ scfp_dualplus = SkeletonCellFieldPair(uh_dual,uh)
8889

8990
# AD of Integrals over Skeleton faces tested in FESpaces -> FEAutodiffTests.jl
9091

92+
# testing the MultiField SkeletonCellFieldPair (SCFP)
93+
# this uses some low-level API to construct MultiFieldCellFields with SCFP
94+
95+
Q = TestFESpace(model,reffe,conformity=:L2)
96+
P = TrialFESpace(Q)
97+
98+
Y = MultiFieldFESpace([V, Q])
99+
X = MultiFieldFESpace([U, P])
100+
101+
xh = FEFunction(X,rand(num_free_dofs(X)))
102+
uh,ph = xh
103+
104+
cellu = lazy_map(DensifyInnerMostBlockLevelMap(),get_cell_dof_values(xh))
105+
cellu_dual = lazy_map(DualizeMap(ForwardDiff.gradient),cellu)
106+
single_fields_plus = SkeletonCellFieldPair[]
107+
single_fields = SkeletonCellFieldPair[]
108+
single_fields_dual = GenericCellField[]
109+
U = xh.fe_space
110+
nfields = length(U.spaces)
111+
cell_dofs_field_offsets = MultiField._get_cell_dofs_field_offsets(xh)
112+
113+
for i in 1:nfields
114+
view_range = cell_dofs_field_offsets[i]:cell_dofs_field_offsets[i+1]-1
115+
cell_values_field = lazy_map(a->view(a,view_range),cellu_dual)
116+
cf_dual = CellField(U.spaces[i],cell_values_field)
117+
scfp_plus = SkeletonCellFieldPair(cf_dual, xh[i])
118+
scfp = SkeletonCellFieldPair(xh[i], xh[i])
119+
push!(single_fields_dual,cf_dual)
120+
push!(single_fields_plus,scfp_plus)
121+
push!(single_fields,scfp)
122+
end
123+
124+
xh_scfp = MultiFieldCellField(single_fields)
125+
uh_scfp, ph_scfp = xh_scfp
126+
127+
xh_scfp_dual_plus = MultiFieldCellField(single_fields_plus)
128+
uh_scfp_dual_plus, ph_scfp_dual_plus = xh_scfp_dual_plus
129+
130+
xh_dual = MultiFieldCellField(single_fields_dual)
131+
uh_dual, ph_dual = xh_dual
132+
133+
g_Λ((uh,ph)) = ( mean(uh) + mean(ph) + mean(uh)*mean(ph) )dΛ
134+
a_Λ((uh,ph)) = ( - jump(uh*n_Λ)mean((ph))
135+
- mean((uh))jump(ph*n_Λ)
136+
+ jump(uh*n_Λ)jump(ph*n_Λ) )dΛ
137+
138+
# few basic tests if integrations on trians are done fine
139+
@test sum((uh_scfp*ph_scfp)*dΓ) == sum((uh*ph)*dΓ)
140+
@test sum((uh_scfp - ph_scfp)*dΓ) == sum((uh - ph)*dΓ)
141+
@test sum((uh_scfp*ph_scfp)*dΩ) == sum((uh*ph)*dΩ)
142+
@test sum((uh_scfp - ph_scfp)*dΩ) == sum((uh - ph)*dΩ)
143+
144+
# integrations involving SkeletonCellFieldPair derivative terms
145+
@test sum((ph_scfp*(uh_scfp))*dΩ) == sum((ph*(uh))*dΩ)
146+
@test sum((uh_scfp*(ph_scfp))*dΩ) == sum((uh*(ph))*dΩ)
147+
@test sum((uh_scfp*(ph_scfp)n_Γ)*dΓ) == sum((uh*(ph)n_Γ)*dΓ)
148+
@test sum((ph_scfp*(uh_scfp)n_Γ)*dΓ) == sum((ph*(uh)n_Γ)*dΓ)
149+
150+
# dualized tests
151+
152+
# few basic tests if integrations on trians are done fine
153+
@test sum((uh_scfp_dual_plus*ph_scfp_dual_plus)*dΓ).value == sum((uh*ph)*dΓ)
154+
@test sum((uh_scfp_dual_plus*ph_scfp_dual_plus)*dΓ).partials == sum((uh_dual*ph_dual)*dΓ).partials
155+
@test sum((uh_scfp_dual_plus - ph_scfp_dual_plus)*dΓ).value == sum((uh - ph)*dΓ)
156+
@test sum((uh_scfp_dual_plus - ph_scfp_dual_plus)*dΓ).partials == sum((uh_dual - ph_dual)*dΓ).partials
157+
@test sum((uh_scfp_dual_plus*ph_scfp_dual_plus)*dΩ).value == sum((uh*ph)*dΩ)
158+
@test sum((uh_scfp_dual_plus*ph_scfp_dual_plus)*dΩ).partials == sum((uh_dual*ph_dual)*dΩ).partials
159+
@test sum((uh_scfp_dual_plus - ph_scfp_dual_plus)*dΩ).value == sum((uh - ph)*dΩ)
160+
@test sum((uh_scfp_dual_plus - ph_scfp_dual_plus)*dΩ).partials == sum((uh_dual - ph_dual)*dΩ).partials
161+
162+
# integrations involving SkeletonCellFieldPair derivative terms
163+
@test sum((ph_scfp_dual_plus*(uh_scfp_dual_plus))*dΩ)[1].value == sum((ph*(uh))*dΩ)[1]
164+
@test sum((ph_scfp_dual_plus*(uh_scfp_dual_plus))*dΩ)[1].partials == sum((ph_dual*(uh_dual))*dΩ)[1].partials
165+
@test sum((uh_scfp_dual_plus*(ph_scfp_dual_plus))*dΩ)[1].value == sum((uh*(ph))*dΩ)[1]
166+
@test sum((uh_scfp_dual_plus*(ph_scfp_dual_plus))*dΩ)[1].partials == sum((uh_dual*(ph_dual))*dΩ)[1].partials
167+
@test sum((uh_scfp_dual_plus*(ph_scfp_dual_plus)n_Γ)*dΓ).value == sum((uh*(ph)n_Γ)*dΓ)
168+
@test sum((uh_scfp_dual_plus*(ph_scfp_dual_plus)n_Γ)*dΓ).partials == sum((uh_dual*(ph_dual)n_Γ)*dΓ).partials
169+
@test sum((ph_scfp_dual_plus*(uh_scfp_dual_plus)n_Γ)*dΓ).value == sum((ph*(uh)n_Γ)*dΓ)
170+
@test sum((ph_scfp_dual_plus*(uh_scfp_dual_plus)n_Γ)*dΓ).partials == sum((ph_dual*(uh_dual)n_Γ)*dΓ).partials
171+
172+
# test for SkeletonTriangulation
173+
@test sum(g_Λ((uh_scfp_dual_plus,ph_scfp_dual_plus))).value == sum(g_Λ((uh,ph)))
174+
@test sum(a_Λ((uh_scfp_dual_plus,ph_scfp_dual_plus))).value == sum(a_Λ((uh,ph)))
175+
91176
end # module

test/MultiFieldTests/MultiFieldFEAutodiffTests.jl

+67
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ module MultiFieldFEAutodiffTests
33
using Test
44

55
using Gridap.Algebra
6+
using Gridap.Arrays
67
using Gridap.Geometry
78
using Gridap.CellData
89
using Gridap.ReferenceFEs
910
using Gridap.FESpaces
1011
using Gridap.MultiField
1112

13+
using ForwardDiff
14+
1215
domain = (0,1,0,1)
1316
partition = (2,2)
1417
model = CartesianDiscreteModel(domain,partition)
@@ -132,4 +135,68 @@ c=jup_uh_ph[Ω]
132135
@test all(a .== map(x->x.array[1,1],c))
133136
@test all(b .== map(x->x.array[2,2],c))
134137

138+
# AD tests for Multifiled functionals involving SkeletonTriangulation
139+
# compared with direct ForwardDiff results
140+
141+
reffeV = ReferenceFE(lagrangian,Float64,2)
142+
reffeQ = ReferenceFE(lagrangian,Float64,1)
143+
V = TestFESpace(model,reffeV,conformity=:L2)
144+
Q = TestFESpace(model,reffeQ,conformity=:L2)
145+
146+
U = TrialFESpace(V)
147+
P = TrialFESpace(Q)
148+
149+
Y = MultiFieldFESpace([V, Q])
150+
X = MultiFieldFESpace([U, P])
151+
152+
xh = FEFunction(X,rand(num_free_dofs(X)))
153+
154+
Λ = SkeletonTriangulation(model)
155+
= Measure(Λ,2)
156+
n_Λ = get_normal_vector(Λ)
157+
158+
g_Λ((uh,ph)) = ( mean(uh) + mean(ph) + mean(uh)*mean(ph) )dΛ
159+
# f_Λ((uh,ph)) = ∫( mean(uh*uh) + mean(uh*ph) + mean(ph*ph) )dΛ
160+
a_Λ((uh,ph)) = ( - jump(uh*n_Λ)mean((ph))
161+
- mean((uh))jump(ph*n_Λ)
162+
+ jump(uh*n_Λ)jump(ph*n_Λ) )dΛ
163+
164+
function f_uh_free_dofs(f,xh,θ)
165+
dir_u = similar(xh[1].dirichlet_values,eltype(θ))
166+
dir_p = similar(xh[2].dirichlet_values,eltype(θ))
167+
X = xh.fe_space
168+
θ_uh = restrict_to_field(X,θ,1)
169+
θ_ph = restrict_to_field(X,θ,2)
170+
uh = FEFunction(X[1],θ_uh,dir_u)
171+
ph = FEFunction(X[2],θ_ph,dir_p)
172+
xh = MultiFieldFEFunction(θ,xh.fe_space,[uh,ph])
173+
sum(f(xh))
174+
end
175+
# can also do the above by constructing a MultiFieldCellField
176+
177+
g_Λ_(θ) = f_uh_free_dofs(g_Λ,xh,θ)
178+
# f_Λ_(θ) = f_uh_free_dofs(f_Λ,xh,θ)
179+
a_Λ_(θ) = f_uh_free_dofs(a_Λ,xh,θ)
180+
181+
θ = get_free_dof_values(xh)
182+
183+
# check if the evaluations are working
184+
# @test sum(f_Λ(xh)) == f_Λ_(θ)
185+
@test sum(a_Λ(xh)) == a_Λ_(θ)
186+
@test sum(g_Λ(xh)) == g_Λ_(θ)
187+
188+
f_Ω((uh,ph)) = (uh*uh + ph*uh + uh*ph)*
189+
f_Ω_(θ) = f_uh_free_dofs(f_Ω,xh,θ)
190+
gridapgradf_Ω = assemble_vector(gradient(f_Ω,xh),X)
191+
fdgradf_Ω = ForwardDiff.gradient(f_Ω_,θ)
192+
test_array(gridapgradf_Ω,fdgradf_Ω,)
193+
194+
gridapgradg = assemble_vector(gradient(g_Λ,xh),X)
195+
fdgradg = ForwardDiff.gradient(g_Λ_,θ)
196+
test_array(gridapgradg,fdgradg,)
197+
198+
gridapgrada = assemble_vector(gradient(a_Λ,xh),X)
199+
fdgrada = ForwardDiff.gradient(a_Λ_,θ)
200+
test_array(gridapgrada,fdgrada,)
201+
135202
end # module

0 commit comments

Comments
 (0)