Skip to content

Commit 779c7fa

Browse files
authored
Merge pull request #176 from zjwegert/mf-grad-diff-trians
Split AD for Multifield
2 parents 97c5662 + cd79199 commit 779c7fa

File tree

6 files changed

+149
-14
lines changed

6 files changed

+149
-14
lines changed

NEWS.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +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-
## [Unreleased]
8+
## [0.4.10] - 2025-09-29
99

1010
### Added
1111

1212
- Added support for multiple ghost layers on cartesian models. Since PR[#182](https://github.com/gridap/GridapDistributed.jl/pull/182).
13+
- Added new way of doing AD for MultiField, where partials are computed separately for each field then merged together. Since PR[#176](https://github.com/gridap/GridapDistributed.jl/pull/176).
1314

1415
### Fixed
1516

@@ -159,7 +160,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
159160

160161
### Fixed
161162

162-
- Added missing parameter to `allocate_jacobian`, needed after Gridap v0.17.18. Since PR [126](https://github.com/gridap/GridapDistributed.jl/pull/126).
163+
- Added missing parameter to `allocate_jacobian`, needed after Gridap v0.17.18. Since PR [126](https://github.com/gridap/GridapDistributed.jl/pull/126).
163164

164165
## [0.2.8] - 2023-07-31
165166

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "GridapDistributed"
22
uuid = "f9701e48-63b3-45aa-9a63-9bc6c271f355"
33
authors = ["S. Badia <santiago.badia@monash.edu>", "A. F. Martin <alberto.f.martin@anu.edu.au>", "F. Verdugo <f.verdugo.rojano@vu.nl>"]
4-
version = "0.4.9"
4+
version = "0.4.10"
55

66
[deps]
77
BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e"

src/Autodiff.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,34 @@ function FESpaces._change_argument(op,f,local_trians,uh::DistributedADTypes)
8080
g
8181
end
8282

83+
# Distributed counterpart of: src/MultiField/MultiFieldAutodiff.jl
84+
85+
for (op,_op) in ((:gradient,:_gradient),(:jacobian,:_jacobian))
86+
@eval begin
87+
function FESpaces.$(op)(f::Function,uh::DistributedMultiFieldFEFunction;ad_type=:split)
88+
fuh = f(uh)
89+
if ad_type == :split
90+
MultiField.multifield_autodiff_split($op,f,uh,fuh)
91+
elseif ad_type == :monolithic
92+
FESpaces.$(_op)(f,uh,fuh)
93+
else
94+
@notimplemented """Unknown ad_type = $ad_type
95+
Options:
96+
- :split -- compute the gradient for each field separately, then merge
97+
- :monolithic -- compute the gradient for all fields together
98+
"""
99+
end
100+
end
101+
end
102+
end
103+
104+
function MultiField._combine_contributions(op::Function,terms,fuh::DistributedDomainContribution)
105+
local_terms = map(local_views(fuh),local_views.(terms)...) do fuh,terms...
106+
MultiField._combine_contributions(op,terms,fuh)
107+
end
108+
DistributedDomainContribution(local_terms)
109+
end
110+
83111
# Distributed counterpart of: src/Arrays/Autodiff.jl
84112
# autodiff_array_xxx
85113

src/Geometry.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ function _cartesian_model_with_periodic_bcs(pdesc::DistributedCartesianDescripto
394394
remove_boundary = map((p,n)->((p && (n!=1)) ? true : false),desc.isperiodic,parts)
395395
CartesianDiscreteModel(_desc,cmin,cmax,remove_boundary)
396396
end
397-
397+
398398
return models, global_partition
399399
end
400400

@@ -659,7 +659,7 @@ function Geometry.SkeletonTriangulation(
659659
return filter_cells_when_needed(portion,gids,parent)
660660
end
661661

662-
# NOTE: The following constructors require adding back the ghost cells:
662+
# NOTE: The following constructors require adding back the ghost cells:
663663
# Potentially, the input `trian` has had some/all of its ghost cells removed. If we do not
664664
# add them back, some skeleton facets might look like boundary facets to the local constructors...
665665
function Geometry.BoundaryTriangulation(
@@ -937,4 +937,4 @@ function generate_cell_gids(dmodel::DistributedDiscreteModel{Dm},
937937
tgids = PRange(indices)
938938
end
939939
return tgids
940-
end
940+
end

src/MultiField.jl

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ MultiField.num_fields(m::DistributedMultiFieldCellField) = length(m.field_fe_fun
3333
Base.iterate(m::DistributedMultiFieldCellField) = iterate(m.field_fe_fun)
3434
Base.iterate(m::DistributedMultiFieldCellField,state) = iterate(m.field_fe_fun,state)
3535
Base.getindex(m::DistributedMultiFieldCellField,field_id::Integer) = m.field_fe_fun[field_id]
36+
Base.getindex(m::DistributedMultiFieldCellField,field_id::AbstractUnitRange) = m.field_fe_fun[field_id]
37+
Base.lastindex(m::DistributedMultiFieldCellField) = num_fields(m)
3638
Base.length(m::DistributedMultiFieldCellField) = num_fields(m)
3739

3840
function LinearAlgebra.dot(a::DistributedMultiFieldCellField,b::DistributedMultiFieldCellField)
@@ -161,7 +163,7 @@ function FESpaces.EvaluationFunction(
161163
isconsistent=false
162164
)
163165
free_values = change_ghost(_free_values,f.gids;is_consistent=isconsistent,make_consistent=true)
164-
166+
165167
# Create distributed single field functions
166168
field_fe_fun = DistributedSingleFieldFEFunction[]
167169
for i in 1:num_fields(f)
@@ -221,7 +223,7 @@ function FESpaces.interpolate_everywhere!(
221223
)
222224
msg = "free_values and FESpace have incompatible index partitions."
223225
@check PartitionedArrays.matching_local_indices(axes(free_values,1),get_free_dof_ids(space)) msg
224-
226+
225227
# Interpolate each field
226228
field_fe_fun = DistributedSingleFieldFEFunction[]
227229
for i in 1:num_fields(space)
@@ -396,16 +398,16 @@ function generate_multi_field_gids(
396398
end
397399
collect(keys(dict))
398400
end
399-
401+
400402
f_p_parts_snd, f_p_parts_rcv = map(x->assembly_neighbors(partition(x)),f_frange) |> tuple_of_arrays
401403
p_f_parts_snd = map(v,f_p_parts_snd...)
402404
p_f_parts_rcv = map(v,f_p_parts_rcv...)
403405
p_neigs_snd = map(merge_neigs,p_f_parts_snd)
404406
p_neigs_rcv = map(merge_neigs,p_f_parts_rcv)
405-
407+
406408
exchange_graph = ExchangeGraph(p_neigs_snd,p_neigs_rcv)
407409
assembly_neighbors(p_iset;neighbors=exchange_graph)
408-
410+
409411
PRange(p_iset)
410412
end
411413

@@ -446,7 +448,7 @@ end
446448

447449
# BlockSparseMatrixAssemblers
448450

449-
const DistributedBlockSparseMatrixAssembler{R,C} =
451+
const DistributedBlockSparseMatrixAssembler{R,C} =
450452
MultiField.BlockSparseMatrixAssembler{R,C,<:AbstractMatrix{<:DistributedSparseMatrixAssembler}}
451453

452454
function FESpaces.SparseMatrixAssembler(

test/AutodiffTests.jl

Lines changed: 106 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ function main_sf(distribute,parts)
4444
@test b b_AD
4545

4646
# Skeleton AD
47-
# I would like to compare the results, but we cannot be using FD in parallel...
47+
# I would like to compare the results, but we cannot be using FD in parallel...
4848
Λ = SkeletonTriangulation(model)
4949
= Measure(Λ,2*k)
5050
g_Λ(v) = (mean(v))*
@@ -73,7 +73,7 @@ function main_mf(distribute,parts)
7373

7474
Ω = Triangulation(model)
7575
= Measure(Ω,2*(k+1))
76-
76+
7777
ν = 1.0
7878
f = VectorValue(0.0,0.0)
7979

@@ -104,9 +104,113 @@ function main_mf(distribute,parts)
104104
@test b b_AD
105105
end
106106

107+
## MultiField AD with different triangulations for each field
108+
function generate_trian(ranks,model,case)
109+
cell_ids = get_cell_gids(model)
110+
trians = map(ranks,local_views(model),partition(cell_ids)) do rank, model, ids
111+
cell_mask = zeros(Bool, num_cells(model))
112+
if case == :partial_trian
113+
if rank (1,2)
114+
cell_mask[own_to_local(ids)] .= true
115+
else
116+
t = own_to_local(ids)
117+
cell_mask[t[1:floor(Int,length(t)/2)]] .= true
118+
end
119+
elseif case == :half_empty_trian
120+
if rank (3,4)
121+
cell_mask[own_to_local(ids)] .= true
122+
end
123+
elseif case == :trian_with_empty_procs
124+
if rank (1,2)
125+
t = own_to_local(ids)
126+
cell_mask[t[1:floor(Int,length(t)/2)]] .= true
127+
end
128+
else
129+
error("Unknown case")
130+
end
131+
Triangulation(model,cell_mask)
132+
end
133+
GridapDistributed.DistributedTriangulation(trians,model)
134+
end
135+
136+
function mf_different_fespace_trians(distribute,parts)
137+
ranks = distribute(LinearIndices((prod(parts),)))
138+
model = CartesianDiscreteModel(ranks,parts,(0,1,0,1),(10,10))
139+
V2 = FESpace(model,ReferenceFE(lagrangian,VectorValue{2,Float64},1))
140+
V3 = FESpace(model,ReferenceFE(lagrangian,Float64,1))
141+
for case in (:boundary,:partial_trian, :half_empty_trian, :trian_with_empty_procs)
142+
if case == :boundary
143+
Γ = BoundaryTriangulation(model)
144+
else
145+
Γ = generate_trian(ranks,model,case)
146+
end
147+
= Measure(Γ,2)
148+
V1 = FESpace(Γ,ReferenceFE(lagrangian,Float64,1))
149+
X = MultiFieldFESpace([V1,V2,V3])
150+
uh = zero(X);
151+
152+
f(xh) = (xh[1]+xh[2]xh[2]+xh[1]*xh[3])dΓ
153+
df(v,xh) = (v[1]+2*v[2]xh[2]+v[1]*xh[3]+xh[1]*v[3])dΓ
154+
du = gradient(f,uh)
155+
du_vec = assemble_vector(du,X)
156+
df_vec = assemble_vector(v->df(v,uh),X)
157+
158+
@test df_vec du_vec
159+
160+
f2(xh,yh) = (xh[1]yh[1]+xh[2]yh[2]+xh[1]xh[2]yh[2]+xh[1]*xh[3]*yh[3])dΓ
161+
dv = get_fe_basis(X);
162+
j = jacobian(uh->f2(uh,dv),uh)
163+
J = assemble_matrix(j,X,X)
164+
165+
f2_jac(xh,dxh,yh) = (dxh[1]yh[1]+dxh[2]yh[2]+dxh[1]xh[2]yh[2]+xh[1]dxh[2]yh[2]+dxh[1]*xh[3]*yh[3]+xh[1]*dxh[3]*yh[3])dΓ
166+
op = FEOperator(f2,f2_jac,X,X)
167+
J_fwd = jacobian(op,uh)
168+
169+
@test reduce(&,map(,partition(J),partition(J_fwd)))
170+
end
171+
end
172+
173+
function skeleton_mf_different_fespace_trians(distribute,parts)
174+
ranks = distribute(LinearIndices((prod(parts),)))
175+
model = CartesianDiscreteModel(ranks,parts,(0,1,0,1),(10,10))
176+
for case in (:partial_trian, :half_empty_trian, :trian_with_empty_procs)
177+
Γ = generate_trian(ranks,model,case)
178+
V1 = FESpace(Γ,ReferenceFE(lagrangian,Float64,1),conformity=:L2)
179+
V2 = FESpace(model,ReferenceFE(lagrangian,VectorValue{2,Float64},1),conformity=:L2)
180+
V3 = FESpace(model,ReferenceFE(lagrangian,Float64,1),conformity=:L2)
181+
X = MultiFieldFESpace([V1,V2,V3])
182+
uh = zero(X);
183+
Λ = SkeletonTriangulation(model)
184+
= Measure(Λ,2)
185+
186+
f(xh) = (mean(xh[1])+mean(xh[2])mean(xh[2])+mean(xh[1])*mean(xh[3]))dΛ
187+
df(v,xh) = (mean(v[1])+2*mean(v[2])mean(xh[2])+mean(v[1])*mean(xh[3])+mean(xh[1])*mean(v[3]))dΛ
188+
du = gradient(f,uh)
189+
du_vec = assemble_vector(du,X)
190+
df_vec = assemble_vector(v->df(v,uh),X)
191+
192+
@test df_vec du_vec
193+
194+
# Skel jac
195+
f2(xh,yh) = (mean(xh[1])mean(yh[1])+mean(xh[2])mean(yh[2])+mean(xh[1])mean(xh[2])mean(yh[2])+mean(xh[1])*mean(xh[3])*mean(yh[3]))dΛ
196+
dv = get_fe_basis(X);
197+
j = jacobian(uh->f2(uh,dv),uh);
198+
J = assemble_matrix(j,X,X)
199+
200+
f2_jac(xh,dxh,yh) = (mean(dxh[1])mean(yh[1])+mean(dxh[2])mean(yh[2])+mean(dxh[1])mean(xh[2])mean(yh[2]) +
201+
mean(xh[1])mean(dxh[2])mean(yh[2])+mean(dxh[1])*mean(xh[3])*mean(yh[3])+mean(xh[1])*mean(dxh[3])*mean(yh[3]))dΛ
202+
op = FEOperator(f2,f2_jac,X,X)
203+
J_fwd = jacobian(op,uh)
204+
205+
@test reduce(&,map(,partition(J),partition(J_fwd)))
206+
end
207+
end
208+
107209
function main(distribute,parts)
108210
main_sf(distribute,parts)
109211
main_mf(distribute,parts)
212+
mf_different_fespace_trians(distribute,parts)
213+
skeleton_mf_different_fespace_trians(distribute,parts)
110214
end
111215

112216
end

0 commit comments

Comments
 (0)