Skip to content

Commit

Permalink
Fix issues with heterogeneous soil profile types
Browse files Browse the repository at this point in the history
  • Loading branch information
bgroenks96 committed Oct 27, 2023
1 parent 5b5e700 commit c49143c
Show file tree
Hide file tree
Showing 10 changed files with 47 additions and 36 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "CryoGrid"
uuid = "a535b82e-5f3d-4d97-8b0b-d6483f5bebd5"
authors = ["Brian Groenke <[email protected]>", "Jan Nitzbon <[email protected]>", "Moritz Langer <[email protected]>"]
version = "0.20.2"
version = "0.20.3"

[deps]
Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
Expand Down
2 changes: 1 addition & 1 deletion examples/heat_freeW_lake_lite_implicit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ tempprofile_linear = TemperatureProfile(
)
modelgrid = Grid(vcat(-1.0u"m":0.02u"m":-0.02u"m", CryoGrid.Presets.DefaultGrid_2cm))
z_top = -1.0u"m"
z_sub = map(knot -> knot.depth, soilprofile)
z_sub = keys(soilprofile)
z_bot = modelgrid[end]
upperbc = TemperatureBC(forcings.Tair, NFactor(nf=0.5))
initT = initializer(:T, tempprofile_linear)
Expand Down
2 changes: 1 addition & 1 deletion examples/heat_freeW_snow_samoylov.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ soilprofile, tempprofile = CryoGrid.Presets.SamoylovDefault
initT = initializer(:T, tempprofile)
initsat = initializer(:sat, 1.0)
z_top = -2.0u"m"
z_sub = map(knot -> knot.depth, soilprofile)
z_sub = keys(soilprofile)
z_bot = 1000.0u"m"
upperbc = WaterHeatBC(
SurfaceWaterBalance(rainfall=forcings.rainfall, snowfall=forcings.snowfall),
Expand Down
2 changes: 1 addition & 1 deletion src/Numerics/Numerics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ function turbo(value::Bool)
global USE_TURBO = value
end

export Profile, ProfileKnot
export Profile
include("profile.jl")

export ∇, flux!, divergence!, nonlineardiffusion!, harmonicmean!, harmonicmean
Expand Down
52 changes: 30 additions & 22 deletions src/Numerics/profile.jl
Original file line number Diff line number Diff line change
@@ -1,29 +1,37 @@
struct ProfileKnot{D,V}
depth::D
value::V
end
Base.iterate(knot::ProfileKnot) = iterate((knot.depth, knot.value))
Base.iterate(knot::ProfileKnot, state) = iterate((knot.depth, knot.value), state)
Base.show(io::IO, knot::ProfileKnot) = print(io, "$(knot.depth): $(knot.value)")
struct Profile{N,V,D}
knots::NTuple{N,ProfileKnot{D,V}}
Profile(::Tuple{}) = new{0,Nothing,Nothing}(())
Profile(knots::Tuple{Vararg{ProfileKnot{D,V},N}}) where {N,D,V} = new{N,V,D}(knots)
Profile(pairs::Tuple{Vararg{Pair}}) = Profile(map(Base.splat(ProfileKnot), pairs))
"""
Profile{N,idxTypes,valTypes}
Represents a "profile" of values indexed typically over depth, i.e:
x₁: value 1
x₂: value 2
...
where xᵢ are the indices.
"""
struct Profile{N,idxTypes,valTypes}
indices::idxTypes
values::valTypes
Profile(::Tuple{}) = new{0,Tuple{},Tuple{}}((),())
Profile(indices::NTuple{N,Any}, values::NTuple{N,Any}) where {N} = new{N,typeof(indices),typeof(values)}(indices, values)
Profile(pairs::Tuple{Vararg{Pair}}) = Profile(map(first, pairs), map(last, pairs))
Profile(pairs::Pair...) = Profile(pairs)
end
function Base.show(io::IO, mime::MIME"text/plain", profile::Profile)
for knot in profile
show(io, mime, knot)
for (d,v) in profile
print("$d ")
show(io, mime, v)
println(io)
end
end
Base.length(::Profile{N}) where N = N
Base.iterate(profile::Profile) = iterate(profile.knots)
Base.iterate(profile::Profile, state) = iterate(profile.knots, state)
Base.map(f, profile::Profile) = Profile(map(knot -> ProfileKnot(knot.depth, f(knot.value)), profile.knots))
Base.getindex(profile::Profile, itrv::Interval) = Profile(Tuple(knot for knot in profile.knots if knot.depth itrv))
Base.getindex(profile::Profile, i::Int) = profile.knots[i]
Base.getindex(profile::Profile, i) = Profile(profile.knots[i])
Base.lastindex(profile::Profile) = lastindex(profile.knots)
StructTypes.StructType(::Type{<:Profile}) = StructTypes.UnorderedStruct()
Base.keys(profile::Profile) = profile.indices
Base.values(profile::Profile) = profile.values
Base.iterate(profile::Profile) = iterate(map(Pair, profile.indices, profile.values))
Base.iterate(profile::Profile, state) = iterate(map(Pair, profile.indices, profile.values), state)
Base.map(f, profile::Profile) = Profile(map((i,v) -> i => f(v), profile.indices, profile.values))
Base.getindex(profile::Profile, itrv::Interval) = Profile(Tuple(i => v for (i,v) in profile if i itrv))
Base.getindex(profile::Profile, i::Int) = profile.indices[i] => profile.values[i]
Base.getindex(profile::Profile, i) = Profile(profile.indices[i], profile.values[i])
Base.lastindex(profile::Profile) = lastindex(profile.indices)
StructTypes.StructType(::Type{<:Profile}) = StructTypes.UnorderedStruct()
1 change: 0 additions & 1 deletion src/Physics/Soils/ground.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ Base.@kwdef struct Ground{Tpara,Theat<:Optional{HeatBalance},Twater<:Optional{Wa
end
# Convenience constructors
Ground(para::GroundParameterization; kwargs...) = Ground(; para, kwargs...)
Ground(knot::ProfileKnot{T,<:GroundParameterization}; kwargs...) where {T} = Ground(; para=knot.value, kwargs...)

default_fcsolver(::Any, ::Any) = nothing
default_fcsolver(::HeatBalance{<:SFCC}, ::Nothing) = SFCCPreSolver(FreezeCurves.SFCCPreSolverCache1D())
Expand Down
10 changes: 7 additions & 3 deletions src/Physics/Soils/soil_para.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,16 @@ Hydrology.watercontent(soil::Soil{<:MineralOrganic,THeat,<:WaterBalance}, state)
Special `SoilParameterization` which wraps a `Profile` of another soil parameterization type
to indicate that it should be heterogeneous with over depth.
"""
Base.@kwdef struct Heterogeneous{V,N,D,Taux} <: SoilParameterization
profile::SoilProfile{N,V,D}
Base.@kwdef struct Heterogeneous{V,N,IT,VT,Taux} <: SoilParameterization
profile::SoilProfile{N,IT,VT}
aux::Taux = nothing
Heterogeneous(profile::SoilProfile{N,V,D}, aux::Taux=nothing) where {V,N,D,Taux} = new{V,N,D,Taux}(profile, aux)
Heterogeneous(::SoilProfile) = error("SoilProfile for heterogeneous layer must have uniform parameterization types (but the parameters may vary).")
Heterogeneous(profile::SoilProfile{N,IT,VT}, aux=nothing) where {N,V<:SoilParameterization,IT<:NTuple{N,DistQuantity},VT<:NTuple{N,V}} = new{V,N,IT,VT,typeof(aux)}(profile, aux)
end

"""
Ground(soilprofile::SoilProfile; kwargs...)
"""
Ground(soilprofile::SoilProfile; kwargs...) = Ground(Heterogeneous(soilprofile); kwargs...)

saturation(::Soil{<:Heterogeneous{<:MineralOrganic}}, state) = state.sat
Expand Down
4 changes: 2 additions & 2 deletions src/Physics/Soils/soil_types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ Type alias for any `AbstractGround` layer with parameterization of type `SoilPar
const Soil{Tpara,Theat,Twater} = AbstractGround{Tpara,Theat,Twater} where {Tpara<:SoilParameterization,Theat<:Optional{HeatBalance},Twater<:Optional{WaterBalance}}

"""
SoilProfile{N,V,D} = Profile{N,V,D} where {N,V<:SoilParameterization,D<:DistQuantity}
SoilProfile{N,IT,VT} = Profile{N,IT,VT} where {N,IT<:NTuple{N,DistQuantity},VT<:NTuple{N,SoilParameterization}}
Alias for depthwise `Profile` where the values are `SoilParameterization` types.
"""
const SoilProfile{N,V,D} = Profile{N,V,D} where {N,V<:SoilParameterization,D<:DistQuantity}
const SoilProfile{N,IT,VT} = Profile{N,IT,VT} where {N,IT<:NTuple{N,DistQuantity},VT<:NTuple{N,SoilParameterization}}

# Constructors
"""
Expand Down
2 changes: 1 addition & 1 deletion src/Presets/Presets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ but this can be changed via the `freezecurve` parameter. For example, to use the
function SoilHeatTile(heatop, upperbc::BoundaryProcess, lowerbc::BoundaryProcess, soilprofile::Profile, init::VarInitializer; grid::Grid=DefaultGrid_5cm, freezecurve::F=FreeWater(), fcsolver=SFCCPreSolver(), tile_kwargs...) where {F<:FreezeCurve}
strat = Stratigraphy(
grid[1] => Top(upperbc),
Tuple(knot.depth => Ground(knot.value, heat=HeatBalance(heatop, freezecurve=freezecurve), fcsolver=fcsolver) for (i,knot) in enumerate(soilprofile)),
Tuple(d => Ground(para, heat=HeatBalance(heatop, freezecurve=freezecurve), fcsolver=fcsolver) for (i,(d,para)) in enumerate(soilprofile)),
grid[end] => Bottom(lowerbc)
)
return Tile(strat, PresetGrid(grid), init; tile_kwargs...)
Expand Down
6 changes: 3 additions & 3 deletions src/initializers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ ConstructionBase.constructorof(::Type{T}) where {varname,T<:InterpInitializer{va

function CryoGrid.initialcondition!(init::InterpInitializer{var}, ::Layer, state) where var
profile, interp, extrap = init.profile, init.interp, init.extrap
depths = collect(map(knot -> ustrip(knot.depth), profile.knots))
depths = collect(ustrip.(keys(profile)))
u = getproperty(state, var)
z = cells(state.grid)
@assert length(z) == length(u) "$(length(z)) != $(length(u))"
if length(depths) > 1
f = Interpolations.extrapolate(
Interpolations.interpolate(
(depths,),
collect(map(knot -> ustrip(knot.value), profile.knots)),
collect(ustrip.(values(profile))),
Interpolations.Gridded(interp)
),
extrap
Expand All @@ -59,7 +59,7 @@ function CryoGrid.initialcondition!(init::InterpInitializer{var}, ::Layer, state
else
# if only one knot is defined, set to this value over all z;
# this is necessary because interpolate does not support interpolation grids of length 1
y = ustrip(profile.knots[1].value)
y = ustrip(first(values(profile)))
@. u = y
return u
end
Expand Down

0 comments on commit c49143c

Please sign in to comment.