Skip to content
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

Refactor of autodiff for MultiField #1070

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
Draft

Refactor of autodiff for MultiField #1070

wants to merge 10 commits into from

Conversation

JordiManyer
Copy link
Member

@JordiManyer JordiManyer commented Dec 21, 2024

The current implementation of autodiff for MultiField spaces is a mess... It relies on a lot of specific code, and has severe performance issues. I believe that with some though we could use the same code as for SingleField.

This is motivated by the fact that I do not want to replicate the current implementation for GridapDistributed. I believe it will also solve many issues we currently have for autodiff+multifield.

To-Dos

  • gradient
  • jacobian
  • hessian
  • skeletons

To-Think

Skeletons are wrong when the number of dofs is different in the left and right cells. The issue is that we are dualizing both left and right cells at once, but witout knowing which cells those are (we just assume they are the same). I think I'll have to go back to what Kishore was doing, i.e doing left then right. Hopefully without any densifying maps.

The other solution would be to directly port the cell values to the destination triangulation, and evaluate the bilinear forms with cellfields that are already there. The issue is that this will likely break the change_domain apis in an unpredictable way.

Copy link

codecov bot commented Dec 23, 2024

Codecov Report

Attention: Patch coverage is 94.60784% with 11 lines in your changes missing coverage. Please review.

Project coverage is 89.24%. Comparing base (04edf7e) to head (503ed49).

Files with missing lines Patch % Lines
src/Fields/ArrayBlocks.jl 90.75% 11 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1070      +/-   ##
==========================================
+ Coverage   89.05%   89.24%   +0.19%     
==========================================
  Files         197      197              
  Lines       23905    23767     -138     
==========================================
- Hits        21288    21212      -76     
+ Misses       2617     2555      -62     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@zjwegert
Copy link
Contributor

zjwegert commented Jan 5, 2025

All the GridapTopOpt state map tests pass for this branch. I've also benchmarked the changes and compared to master. Here are the results for 6e649c5:

origin/master
n=64 n=128 n=256
Gradient (lazy) 1.411 ms (41658 allocations: 4.77 MiB) 1.478 ms (41658 allocations: 4.82 MiB) 1.589 ms (41658 allocations: 5.00 MiB)
(assembly) 11.165 ms (7113 allocations: 1.70 MiB) 43.124 ms (7119 allocations: 1.98 MiB) 172.846 ms (7097 allocations: 3.10 MiB)
Jacobian 1.668 ms (46740 allocations: 5.76 MiB) 1.692 ms (46740 allocations: 5.81 MiB) 1.866 ms (46740 allocations: 5.99 MiB)
22.680 ms (37196 allocations: 17.42 MiB) 92.452 ms (110928 allocations: 61.80 MiB) 407.529 ms (405834 allocations: 240.20 MiB)
Hessian 5.620 ms (48140 allocations: 23.14 MiB) 5.467 ms (48140 allocations: 23.19 MiB) 5.698 ms (48140 allocations: 23.37 MiB)
126.329 ms (15875 allocations: 21.62 MiB) 534.504 ms (15877 allocations: 48.75 MiB) 2.257 s (15883 allocations: 158.15 MiB)
origin/autodiff
Gradient 1.788 ms (46056 allocations: 5.67 MiB) 1.827 ms (46056 allocations: 5.71 MiB) 1.975 ms (46056 allocations: 5.90 MiB)
10.947 ms (5555 allocations: 1.25 MiB) 42.580 ms (5599 allocations: 1.54 MiB) 171.281 ms (5523 allocations: 2.64 MiB)
Jacobian 1.768 ms (46589 allocations: 5.72 MiB) 1.841 ms (46589 allocations: 5.76 MiB) 1.942 ms (46589 allocations: 5.95 MiB)
20.421 ms (6150 allocations: 10.00 MiB) 82.224 ms (6148 allocations: 37.14 MiB) 334.994 ms (6138 allocations: 146.53 MiB)
Hessian 8.615 ms (101935 allocations: 38.30 MiB) 8.315 ms (101935 allocations: 38.35 MiB) 8.400 ms (101935 allocations: 38.54 MiB)
139.630 ms (20796 allocations: 19.79 MiB) 529.733 ms (20754 allocations: 46.91 MiB) 2.165 s (20830 allocations: 156.35 MiB)
Script
using Gridap, Gridap.MultiField
using BenchmarkTools

function main(n)
  println("Running: n = $n")
  model = CartesianDiscreteModel((0,1,0,1),(n,n))
  order = 1
  Ω = Triangulation(model)
  dΩ = Measure(Ω,2*order)

  V1 = FESpace(Ω,ReferenceFE(lagrangian,Float64,order);dirichlet_tags="boundary")
  V2 = FESpace(Ω,ReferenceFE(lagrangian,VectorValue{2,Float64},order);dirichlet_tags="boundary")

  U1 = TrialFESpace(V1)
  U2 = TrialFESpace(V2)
  UB = MultiFieldFESpace([U1,U2])
  VB = MultiFieldFESpace([V1,V2])

  uh = FEFunction(UB,rand(num_free_dofs(UB)))

  res((u1,u2),(v1,v2)) = (u1*(u1)(v1) + (u2  u2) * (u2)(v2))dΩ
  ## Gradient
  println("Gradient:")
  _res_grad(uh) = res(uh,uh)
  g = gradient(_res_grad,uh)
  @btime gradient($_res_grad,$uh);
  @btime Gridap.FESpaces.assemble_vector($g,$VB);

  ## Jacobian
  println("Jacobian:")
  dv = get_fe_basis(VB)
  _res_jac(uh) = res(uh,dv)
  j = jacobian(_res_jac,uh)
  @btime jacobian($_res_jac,$uh);
  @btime Gridap.FESpaces.assemble_matrix($j,$UB,$VB);

  ## Hessian
  println("Hessian:")
  h = hessian(_res_grad,uh);
  @btime hessian($_res_grad,$uh);
  @btime Gridap.FESpaces.assemble_matrix($h,$UB,$VB);
end

main(64);
main(128);
main(256);

@zjwegert
Copy link
Contributor

zjwegert commented Jan 15, 2025

Note that this currently breaks for the following

using Gridap

order = 1
degree = 2order

model = CartesianDiscreteModel((0,1,0,1),(10,10))
V_φ = TestFESpace(model,ReferenceFE(lagrangian,Float64,1))
φf(x) = sqrt((x[1]-0.5)^2 + (x[2]-0.5)^2) - 0.3
φh = interpolate(φf,V_φ)

V = TestFESpace(model,ReferenceFE(lagrangian,VectorValue{2,Float64},order,space=:P))
Q = TestFESpace(model,ReferenceFE(lagrangian,Float64,order,space=:P))
VQ = MultiFieldFESpace([V,Q])

Λ = SkeletonTriangulation(model)
dΛ = Measure(Λ,degree)
n_Λ = get_normal_vector(Λ)
dΩ = Measure(get_triangulation(model),degree)

res((u,p),(v,q),φ) = (uv + φ)dΩ + (jump(n_Λ  (u))  jump(n_Λ  (v)))dΛ
# res((u,p),(v,q),φ) = ∫(u⋅v + φ)dΩ + ∫(jump(n_Λ ⋅ ∇(u)) ⋅ jump(n_Λ ⋅ ∇(v)) + 0mean(φ))dΛ # workaround

->res(zero(VQ),zero(VQ),φ),φh)

with

ERROR: MethodError: no method matching +(::Int64, ::Tuple{Int64, Int64})
The function `+` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  +(::Any, ::Any, ::Any, ::Any...)
   @ Base operators.jl:596
  +(::Real, ::Complex{Bool})
   @ Base complex.jl:322
  +(::Integer, ::BlockArrays.Block)
   @ BlockArrays ~/.julia/packages/BlockArrays/X84bj/src/blockindices.jl:73
  ...

Stacktrace:
  [1] extract_gradient_block!
    @ ~/.julia/dev/Gridap/src/Fields/ArrayBlocks.jl:1583 [inlined]
  [2] evaluate!
    @ ~/.julia/dev/Gridap/src/Fields/ArrayBlocks.jl:1536 [inlined]
  [3] evaluate
    @ ~/.julia/dev/Gridap/src/Arrays/Maps.jl:87 [inlined]
  [4] return_value
    @ ~/.julia/dev/Gridap/src/Arrays/Maps.jl:64 [inlined]
  [5] return_type
    @ ~/.julia/dev/Gridap/src/Arrays/Maps.jl:62 [inlined]
  [6] lazy_map(::Gridap.Arrays.AutoDiffMap, ::Gridap.Arrays.LazyArray{…}, ::Gridap.Arrays.LazyArray{…})
    @ Gridap.Arrays ~/.julia/dev/Gridap/src/Arrays/LazyArrays.jl:57
  [7] autodiff_array_gradient(a::Gridap.FESpaces.var"#g#66"{}, i_to_x::Gridap.Arrays.LazyArray{…}, j_to_i::Gridap.Arrays.LazyArray{…})
    @ Gridap.Arrays ~/.julia/dev/Gridap/src/Arrays/Autodiff.jl:31
  [8] _gradient(f::Function, uh::Gridap.FESpaces.SingleFieldFEFunction{…}, fuh::Gridap.CellData.DomainContribution)
    @ Gridap.FESpaces ~/.julia/dev/Gridap/src/FESpaces/FEAutodiff.jl:23
  [9] gradient(f::var"#15#16", uh::Gridap.FESpaces.SingleFieldFEFunction{Gridap.CellData.GenericCellField{ReferenceDomain}})
    @ Gridap.FESpaces ~/.julia/dev/Gridap/src/FESpaces/FEAutodiff.jl:5
 [10] top-level scope
    @ ~/.julia/dev/GridapTopOpt/scripts/Embedded/Bugs/multifield_MWE.jl:23
Some type information was truncated. Use `show(err)` to see complete types.

The current workaround is included in the script

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants