Enzyme.jl icon indicating copy to clipboard operation
Enzyme.jl copied to clipboard

Method Error: no method matching MixedDuplicated for Oceananigans run with CATKE

Open jlk9 opened this issue 10 months ago • 8 comments

Running this code:

https://github.com/DJ4Earth/Enzymanigans.jl/blob/jlk9/with-simulation/dynamical_core/autodiff_double_gyre.jl

Produces the following method error:

error_catke_0203.txt

@wsmoses

jlk9 avatar Feb 03 '25 23:02 jlk9

I can't open the link so I cannot test whether it works now, is Enzymanigans.jl a private repository? You could try yourself on the branch of my PR:

https://github.com/EnzymeAD/Enzyme.jl/pull/2297

SouthEndMusic avatar Feb 04 '25 19:02 SouthEndMusic

https://github.com/DJ4Earth/Enzymanigans.jl/blob/jlk9/reduce-for-catke-0210/dynamical_core/autodiff_double_gyre.jl

jlk9 avatar Feb 13 '25 22:02 jlk9

using Enzyme

using KernelAbstractions: @kernel, @index, CPU as KCPU

Enzyme.API.looseTypeAnalysis!(true)
Enzyme.API.strictAliasing!(true)

function compute_diffusivities!(J, tracers)

    top_tracer_bcs = NamedTuple(c => tracers[c].boundary_conditions.top for c in propertynames(tracers))

    worksize = (16, 16)
    loop!    = compute_average_surface_buoyancy_flux!(KCPU(), worksize, worksize)

    loop!(J, top_tracer_bcs)

    return nothing
end

@kernel function compute_average_surface_buoyancy_flux!(J, top_tracer_bcs)
    i, j = @index(Global, NTuple)
    J[i, j, 1] = J[i, j, 1]
end

struct CPU     end
struct Bounded end

struct GridHomemade{FT, TX, TY, TZ, CZ, M, MY, FX, FY, VX, VY, Arch}
    architecture :: Arch
    Nx :: Int
    Ny :: Int
    Nz :: Int
    Hx :: Int
    Hy :: Int
    Hz :: Int
    Lx :: FT
    Ly :: FT
    Lz :: FT
    # All directions can be either regular (FX, FY, FZ) <: Number
    # or stretched (FX, FY, FZ) <: AbstractVector
    Δλᶠᵃᵃ :: FX
    Δλᶜᵃᵃ :: FX
    λᶠᵃᵃ  :: VX
    λᶜᵃᵃ  :: VX
    Δφᵃᶠᵃ :: FY
    Δφᵃᶜᵃ :: FY
    φᵃᶠᵃ  :: VY
    φᵃᶜᵃ  :: VY
    z     :: CZ
    # Precomputed metrics M <: Nothing means metrics will be computed on the fly
    Δxᶠᶜᵃ :: M
    Δxᶜᶠᵃ :: M
    Δxᶠᶠᵃ :: M
    Δxᶜᶜᵃ :: M
    Δyᶠᶜᵃ :: MY
    Δyᶜᶠᵃ :: MY
    Azᶠᶜᵃ :: M
    Azᶜᶠᵃ :: M
    Azᶠᶠᵃ :: M
    Azᶜᶜᵃ :: M
    # Spherical radius
    radius :: FT

    GridHomemade{TX, TY, TZ}(architecture::Arch,
                                      Nλ, Nφ, Nz,
                                      Hλ, Hφ, Hz,
                                      Lλ :: FT, Lφ :: FT, Lz :: FT,
                                      Δλᶠᵃᵃ :: FX, Δλᶜᵃᵃ :: FX,
                                       λᶠᵃᵃ :: VX,  λᶜᵃᵃ :: VX,
                                      Δφᵃᶠᵃ :: FY, Δφᵃᶜᵃ :: FY,
                                       φᵃᶠᵃ :: VY,  φᵃᶜᵃ :: VY,
                                       z :: CZ,
                                      Δxᶠᶜᵃ :: M,  Δxᶜᶠᵃ :: M,
                                      Δxᶠᶠᵃ :: M,  Δxᶜᶜᵃ :: M,
                                      Δyᶠᶜᵃ :: MY, Δyᶜᶠᵃ :: MY,
                                      Azᶠᶜᵃ :: M,  Azᶜᶠᵃ :: M, 
                                      Azᶠᶠᵃ :: M,  Azᶜᶜᵃ :: M,
                                     radius :: FT) where {Arch, FT, TX, TY, TZ,
                                                           FX, FY, CZ, VX, VY,
                                                           M, MY} =
    new{FT, TX, TY, TZ, CZ, M, MY, FX, FY, VX, VY, Arch}(architecture,
                                                         Nλ, Nφ, Nz,
                                                         Hλ, Hφ, Hz,
                                                         Lλ, Lφ, Lz,
                                                         Δλᶠᵃᵃ, Δλᶜᵃᵃ, λᶠᵃᵃ, λᶜᵃᵃ,
                                                         Δφᵃᶠᵃ, Δφᵃᶜᵃ, φᵃᶠᵃ, φᵃᶜᵃ,
                                                         z,
                                                         Δxᶠᶜᵃ, Δxᶜᶠᵃ, Δxᶠᶠᵃ, Δxᶜᶜᵃ,
                                                         Δyᶠᶜᵃ, Δyᶜᶠᵃ,
                                                         Azᶠᶜᵃ, Azᶜᶠᵃ, Azᶠᶠᵃ, Azᶜᶜᵃ, radius)
end

# Use autodiff to compute a gradient
grid_homemade = GridHomemade{Bounded, Bounded, Bounded}(CPU(),
                                      16, 16, 2,
                                      2, 2, 2,
                                      60.0, 60.0, 1800.0,
                                      3.75, 3.75,
                                      [0, 1.0],  [0, 1.0],
                                      3.75, 3.75,
                                      [0, 1.0],  [0, 1.0],
                                      ([0,1.0], [0,1.0], [0,1.0]),
                                      [0, 1.0],  [0, 1.0],
                                      [0, 1.0],  [0, 1.0],
                                      1.0, 1.0,
                                      [0, 1.0],  [0, 1.0], 
                                      [0, 1.0],  [0, 1.0],
                                      6.371e6)


struct HomeField{D, B}
    data :: D
    boundary_conditions :: B
end

struct HomeGridField{G}
    grid :: G
end

struct Flux end

mutable struct HomeFieldBoundaryConditions{B, T}
    bottom :: B
    top :: T
end

struct HomeBoundaryCondition{C, T}
    classification :: C
    condition :: T
end

struct HomeDiscreteBoundaryFunction{P, F}
    func :: F
    parameters :: P
end

struct HomeTKETopBoundaryConditionParameters{C, U}
    top_tracer_boundary_conditions :: C
    top_velocity_boundary_conditions :: U
end

@inline top_tke_flux(i, j, grid, clock, fields, parameters, closure, buoyancy) = zero(grid)

# Use autodiff to compute a gradient
zero_flux = HomeBoundaryCondition(Flux(), nothing)
u_top_bc  = HomeBoundaryCondition(Flux(), HomeGridField(grid_homemade))

etop_condition_parameters = HomeTKETopBoundaryConditionParameters((b=zero_flux, e=zero_flux), (u=u_top_bc, v=zero_flux))
etop_condition            = HomeDiscreteBoundaryFunction(top_tke_flux, etop_condition_parameters)

hero_flux = HomeBoundaryCondition(Flux(), nothing)
etop_flux = HomeBoundaryCondition(Flux(), etop_condition)

b_bc = HomeFieldBoundaryConditions(zero_flux, zero_flux)
e_bc = HomeFieldBoundaryConditions(zero_flux, etop_flux)

b = HomeField(0.0, b_bc)
e = HomeField(0.0, e_bc)

tracers = (; b, e)

dtracers = Enzyme.make_zero(tracers)

J  = zeros(Float64, 400)
dJ = zeros(Float64, 400)

dedν = autodiff(set_runtime_activity(Enzyme.Reverse),
                compute_diffusivities!,
                Duplicated(J, dJ),
                Duplicated(tracers, dtracers))

wsmoses avatar Feb 14 '25 17:02 wsmoses

using Enzyme

using KernelAbstractions: @kernel, @index, CPU as KCPU

Enzyme.API.printall!(true)

# Enzyme.API.looseTypeAnalysis!(true)
# Enzyme.API.strictAliasing!(true)

@kernel function compute_average_surface_buoyancy_flux!(top_tracer_bcs)
end

function compute_diffusivities!(tracers)

    top_tracer_bcs = NamedTuple(c => tracers[c] for c in propertynames(tracers))

    worksize = (16, 16)
    loop!    = compute_average_surface_buoyancy_flux!(KCPU(), worksize, worksize)

    loop!(top_tracer_bcs)

    return nothing
end

struct GridHomemade
    Lx :: Float64
    v::Vector{Float64}
end

# Use autodiff to compute a gradient
grid_homemade = GridHomemade(0.0, [0.0])

struct HomeGridField{G}
    grid :: G
end

struct Flux end

struct HomeBoundaryCondition{C, T}
    classification :: C
    condition :: T
end

struct HomeDiscreteBoundaryFunction{P, F}
    func :: F
    parameters :: P
end

struct HomeTKETopBoundaryConditionParameters{C, U}
    top_tracer_boundary_conditions :: C
    top_velocity_boundary_conditions :: U
end

@inline top_tke_flux(i, j, grid, clock, fields, parameters, closure, buoyancy) = zero(grid)

# Use autodiff to compute a gradient
zero_flux = HomeBoundaryCondition(Flux(), nothing)
u_top_bc  = HomeBoundaryCondition(Flux(), HomeGridField(grid_homemade))

etop_condition_parameters = HomeTKETopBoundaryConditionParameters((b=zero_flux, e=zero_flux), (u=u_top_bc, v=zero_flux))
etop_condition            = HomeDiscreteBoundaryFunction(top_tke_flux, etop_condition_parameters)

etop_flux = HomeBoundaryCondition(Flux(), etop_condition)

b = zero_flux
e = etop_flux

tracers = (; b, e)

dtracers = Enzyme.make_zero(tracers)

dedν = autodiff(set_runtime_activity(Enzyme.Reverse),
                compute_diffusivities!,
                Duplicated(tracers, dtracers))

wsmoses avatar Feb 24 '25 15:02 wsmoses

error_0310.txt

jlk9 avatar Mar 10 '25 14:03 jlk9

Current MWE:

using Enzyme

using KernelAbstractions: @kernel, CPU as KCPU

Enzyme.API.looseTypeAnalysis!(true)
Enzyme.API.strictAliasing!(true)

function compute_diffusivities!(J, tracers)

    top_tracer_bcs = NamedTuple(c => tracers[c].boundary_conditions.top for c in propertynames(tracers))

    worksize = (16, 16)
    loop!    = compute_average_surface_buoyancy_flux!(KCPU(), worksize, worksize)

    loop!(J, top_tracer_bcs)

    return nothing
end

@kernel function compute_average_surface_buoyancy_flux!(J, top_tracer_bcs)
    J[1, 1, 1] = J[1, 1, 1]
end

struct CPU     end
struct Bounded end

struct GridHomemade{FT, TX, TY, TZ, CZ, M, MY, FX, FY, VX, VY, Arch}
    architecture :: Arch
    Nx :: Int
    Ny :: Int
    Nz :: Int
    Hx :: Int
    Hy :: Int
    Hz :: Int
    Lx :: FT
    Ly :: FT
    Lz :: FT
    # All directions can be either regular (FX, FY, FZ) <: Number
    # or stretched (FX, FY, FZ) <: AbstractVector
    Δλᶠᵃᵃ :: FX
    Δλᶜᵃᵃ :: FX
    λᶠᵃᵃ  :: VX
    λᶜᵃᵃ  :: VX
    Δφᵃᶠᵃ :: FY
    Δφᵃᶜᵃ :: FY
    φᵃᶠᵃ  :: VY
    φᵃᶜᵃ  :: VY
    z     :: CZ
    # Precomputed metrics M <: Nothing means metrics will be computed on the fly
    Δxᶠᶜᵃ :: M
    Δxᶜᶠᵃ :: M
    Δxᶠᶠᵃ :: M
    Δxᶜᶜᵃ :: M
    Δyᶠᶜᵃ :: MY
    Δyᶜᶠᵃ :: MY
    Azᶠᶜᵃ :: M
    Azᶜᶠᵃ :: M
    Azᶠᶠᵃ :: M
    Azᶜᶜᵃ :: M
    # Spherical radius
    radius :: FT

    GridHomemade{TX, TY, TZ}(architecture::Arch,
                                      Nλ, Nφ, Nz,
                                      Hλ, Hφ, Hz,
                                      Lλ :: FT, Lφ :: FT, Lz :: FT,
                                      Δλᶠᵃᵃ :: FX, Δλᶜᵃᵃ :: FX,
                                       λᶠᵃᵃ :: VX,  λᶜᵃᵃ :: VX,
                                      Δφᵃᶠᵃ :: FY, Δφᵃᶜᵃ :: FY,
                                       φᵃᶠᵃ :: VY,  φᵃᶜᵃ :: VY,
                                       z :: CZ,
                                      Δxᶠᶜᵃ :: M,  Δxᶜᶠᵃ :: M,
                                      Δxᶠᶠᵃ :: M,  Δxᶜᶜᵃ :: M,
                                      Δyᶠᶜᵃ :: MY, Δyᶜᶠᵃ :: MY,
                                      Azᶠᶜᵃ :: M,  Azᶜᶠᵃ :: M, 
                                      Azᶠᶠᵃ :: M,  Azᶜᶜᵃ :: M,
                                     radius :: FT) where {Arch, FT, TX, TY, TZ,
                                                           FX, FY, CZ, VX, VY,
                                                           M, MY} =
    new{FT, TX, TY, TZ, CZ, M, MY, FX, FY, VX, VY, Arch}(architecture,
                                                         Nλ, Nφ, Nz,
                                                         Hλ, Hφ, Hz,
                                                         Lλ, Lφ, Lz,
                                                         Δλᶠᵃᵃ, Δλᶜᵃᵃ, λᶠᵃᵃ, λᶜᵃᵃ,
                                                         Δφᵃᶠᵃ, Δφᵃᶜᵃ, φᵃᶠᵃ, φᵃᶜᵃ,
                                                         z,
                                                         Δxᶠᶜᵃ, Δxᶜᶠᵃ, Δxᶠᶠᵃ, Δxᶜᶜᵃ,
                                                         Δyᶠᶜᵃ, Δyᶜᶠᵃ,
                                                         Azᶠᶜᵃ, Azᶜᶠᵃ, Azᶠᶠᵃ, Azᶜᶜᵃ, radius)
end

# Use autodiff to compute a gradient
grid_homemade = GridHomemade{Bounded, Bounded, Bounded}(CPU(),
                                      16, 16, 2,
                                      2, 2, 2,
                                      60.0, 60.0, 1800.0,
                                      3.75, 3.75,
                                      [0, 1.0],  [0, 1.0],
                                      3.75, 3.75,
                                      [0, 1.0],  [0, 1.0],
                                      ([0,1.0], [0,1.0], [0,1.0]),
                                      [0, 1.0],  [0, 1.0],
                                      [0, 1.0],  [0, 1.0],
                                      1.0, 1.0,
                                      [0, 1.0],  [0, 1.0], 
                                      [0, 1.0],  [0, 1.0],
                                      6.371e6)


struct HomeField{D, B}
    data :: D
    boundary_conditions :: B
end

struct HomeGridField{G}
    grid :: G
end

struct Flux end

mutable struct HomeFieldBoundaryConditions{B, T}
    bottom :: B
    top :: T
end

struct HomeBoundaryCondition{C, T}
    classification :: C
    condition :: T
end

struct HomeDiscreteBoundaryFunction{P, F}
    func :: F
    parameters :: P
end

struct HomeTKETopBoundaryConditionParameters{C, U}
    top_tracer_boundary_conditions :: C
    top_velocity_boundary_conditions :: U
end

@inline top_tke_flux(i, j, grid, clock, fields, parameters, closure, buoyancy) = zero(grid)

# Use autodiff to compute a gradient
zero_flux = HomeBoundaryCondition(Flux(), nothing)
u_top_bc  = HomeBoundaryCondition(Flux(), HomeGridField(grid_homemade))

etop_condition_parameters = HomeTKETopBoundaryConditionParameters((b=zero_flux, e=zero_flux), (u=u_top_bc, v=zero_flux))
etop_condition            = HomeDiscreteBoundaryFunction(top_tke_flux, etop_condition_parameters)

hero_flux = HomeBoundaryCondition(Flux(), nothing)
etop_flux = HomeBoundaryCondition(Flux(), etop_condition)

b_bc = HomeFieldBoundaryConditions(zero_flux, zero_flux)
e_bc = HomeFieldBoundaryConditions(zero_flux, etop_flux)

b = HomeField(0.0, b_bc)
e = HomeField(0.0, e_bc)

tracers = (; b, e)

dtracers = Enzyme.make_zero(tracers)

J  = zeros(Float64, 400)
dJ = zeros(Float64, 400)

dedν = autodiff(set_runtime_activity(Enzyme.Reverse),
                compute_diffusivities!,
                Duplicated(J, dJ),
                Duplicated(tracers, dtracers))

jlk9 avatar Mar 10 '25 14:03 jlk9

Yeah it makes me wonder if https://github.com/EnzymeAD/Enzyme.jl/blob/43654f904beeeed866305bd5b9e282818a098e50/src/rules/jitrules.jl#L187 should wrap the shadow in a Ref

but I don't know that code well enough.

vchuravy avatar Mar 10 '25 14:03 vchuravy

No that would result in a correctness issue (as the rrror here is that someone provided a value that should have been a ref but was not a ref). The issue is figuring out who/where

wsmoses avatar Mar 10 '25 14:03 wsmoses