Skip to content

Commit

Permalink
🚧 Upgrade ProducerGrowth components.
Browse files Browse the repository at this point in the history
  • Loading branch information
iago-lito committed Dec 12, 2024
1 parent e8ddf79 commit e09a37b
Show file tree
Hide file tree
Showing 13 changed files with 101 additions and 94 deletions.
4 changes: 4 additions & 0 deletions src/Framework/blueprint_macro.jl
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,10 @@ function Base.convert(BF::Type{BroughtField{C,V}}, akw::Tuple{Tuple,NamedTuple})
BF(implicit_constructor_for(C, V, args, kwargs, akw))
end

#-------------------------------------------------------------------------------------------
# Transparent use of the broughtfield inner value.
Base.:(==)(a::BroughtField, b) = refvalue(a) == b

#-------------------------------------------------------------------------------------------
# Checked call to implicit constructor, supposed to yield a consistent blueprint.
function implicit_constructor_for(
Expand Down
3 changes: 2 additions & 1 deletion src/Framework/component_macro.jl
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ function component_macro(__module__, __source__, input...)
if !isnothing(super)
# Infer value type from the abstract supercomponent.
# Same, for abstract component types.
super isa Symbol || perr("Expected supercomponent symbol, got: $(repr(super)).")
is_identifier_path(super) ||
perr("Expected supercomponent path, got: $(repr(super)).")
push_res!(
quote
SuperComponent =
Expand Down
52 changes: 26 additions & 26 deletions src/components/main.jl
Original file line number Diff line number Diff line change
Expand Up @@ -84,31 +84,31 @@ include("./foodweb.jl")
include("./body_mass.jl")
include("./metabolic_class.jl")

# # Useful global values to calculate other biorates.
# # (typical example 'graph' data)
# include("./temperature.jl")
# Useful global values to calculate other biorates.
# (typical example 'graph' data)
include("./temperature.jl")

# # Replicated/adapted from the above.
# # TODO: factorize subsequent repetitions there.
# # Easier once the Internals become more consistent?
# include("./hill_exponent.jl") # <- First, good example of 'graph' component. Read first.
# include("./growth_rate.jl") # <- First, good example of 'node' component. Read first.
# include("./efficiency.jl") # <- First, good example of 'edges' component. Read first.
# include("./carrying_capacity.jl")
# include("./mortality.jl")
# include("./metabolism.jl")
# include("./maximum_consumption.jl")
# include("./producers_competition.jl")
# include("./consumers_preferences.jl")
# include("./handling_time.jl")
# include("./attack_rate.jl")
# include("./half_saturation_density.jl")
# include("./intraspecific_interference.jl")
# include("./consumption_rate.jl")
# Replicated/adapted from the above.
# TODO: factorize subsequent repetitions there.
# Easier once the Internals become more consistent?
include("./hill_exponent.jl") # <- First, good example of 'graph' component. Read first.
include("./growth_rate.jl") # <- First, good example of 'node' component. Read first.
include("./efficiency.jl") # <- First, good example of 'edges' component. Read first.
include("./carrying_capacity.jl")
include("./mortality.jl")
include("./metabolism.jl")
include("./maximum_consumption.jl")
include("./producers_competition.jl")
include("./consumers_preferences.jl")
include("./handling_time.jl")
include("./attack_rate.jl")
include("./half_saturation_density.jl")
include("./intraspecific_interference.jl")
include("./consumption_rate.jl")

# # Namespace nutrients data.
# include("./nutrients/main.jl")
# export Nutrients
# Namespace nutrients data.
include("./nutrients/main.jl")
export Nutrients

include("./nontrophic_layers/main.jl")
using .NontrophicInteractions
Expand All @@ -119,9 +119,9 @@ export Facilitation
export Interference
export Refuge

# # The above components mostly setup *data* within the model.
# # In the nex they mostly specify the *code* needed to simulate it.
# include("./producer_growth.jl")
# The above components mostly setup *data* within the model.
# In the next they mostly specify the *code* needed to simulate it.
include("./producer_growth.jl")
# include("./functional_responses.jl")
# # Metabolism and Mortality are also code components,
# # but they are not reified yet and only reduce
Expand Down
2 changes: 1 addition & 1 deletion src/components/nontrophic_layers/competition.jl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ end
(false) && (local Layer, _Layer) # (reassure JuliaLS)
# For some (legacy?) reason, the foodweb topology is not the only requirement.
@component begin
Layer <: NtiLayer
Layer <: Nti.Layer
requires(BodyMass, MetabolicClass, Topology, FunctionalForm, Intensity)
blueprints(Pack::Pack)
end
Expand Down
2 changes: 1 addition & 1 deletion src/components/nontrophic_layers/facilitation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ end
(false) && (local Layer, _Layer) # (reassure JuliaLS)
# For some (legacy?) reason, the foodweb topology is not the only requirement.
@component begin
Layer <: NtiLayer
Layer <: Nti.Layer
requires(BodyMass, MetabolicClass, Topology, FunctionalForm, Intensity)
blueprints(Pack::Pack)
end
Expand Down
2 changes: 1 addition & 1 deletion src/components/nontrophic_layers/interference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ end
(false) && (local Layer, _Layer) # (reassure JuliaLS)
# For some (legacy?) reason, the foodweb topology is not the only requirement.
@component begin
Layer <: NtiLayer
Layer <: Nti.Layer
requires(BodyMass, MetabolicClass, Topology, Intensity)
blueprints(Pack::Pack)
end
Expand Down
2 changes: 1 addition & 1 deletion src/components/nontrophic_layers/main.jl
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ multiplex_defaults = MultiplexParametersDict(;
),
)

abstract type NtiLayer <: Component end
abstract type Layer <: Component end

# The following layers code is heavily duplicated,
# but keep it as-is since they may diverge in the future.
Expand Down
2 changes: 1 addition & 1 deletion src/components/nontrophic_layers/nti_modules.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ const F = Framework
import .F: @blueprint

import .EN.NontrophicInteractions:
NtiLayer,
check_functional_form,
expand_topology!,
fields_from_multiplex_parms,
Expand All @@ -44,6 +43,7 @@ import .EN.NontrophicInteractions:
random_nti_early_check,
set_layer!,
set_layer_scalar_data!
const Nti = EN.NontrophicInteractions

# (reassure JuliaLS)
include("../macros_keywords.jl")
Expand Down
2 changes: 1 addition & 1 deletion src/components/nontrophic_layers/refuge.jl
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ end
(false) && (local Layer, _Layer) # (reassure JuliaLS)
# For some (legacy?) reason, the foodweb topology is not the only requirement.
@component begin
Layer <: NtiLayer
Layer <: Nti.Layer
requires(BodyMass, MetabolicClass, Topology, FunctionalForm, Intensity)
blueprints(Pack::Pack)
end
Expand Down
81 changes: 53 additions & 28 deletions src/components/producer_growth.jl
Original file line number Diff line number Diff line change
@@ -1,56 +1,73 @@
# Subtypes commit to specifying the code
# associated with producer growth terms,
# and all associated required data.
# and all required associated data.
# They are all mutually exclusive.
abstract type ProducerGrowth <: ModelBlueprint end
abstract type ProducerGrowth <: Component end
export ProducerGrowth

# For the sake of simplicity, to not have defaults lying everywhere.
# But this is open to discussion.
abstract type ProducerGrowthBlueprint <: Blueprint end
F.implied_blueprint_for(::ProducerGrowthBlueprint, ::Component) = F.cannot_imply_construct()

#-------------------------------------------------------------------------------------------
# Simple logistic growth.

mutable struct LogisticGrowth <: ProducerGrowth
r::Option{GrowthRate}
K::Option{CarryingCapacity}
producers_competition::Option{ProducersCompetition}
LogisticGrowth(; kwargs...) = new(
(false) && (local LogisticGrowth, _LogisticGrowth) # (reassure JuliaLS)

# Only 1 blueprint for now: same name as component, suffixed with '_'.
mutable struct LogisticGrowth_ <: ProducerGrowthBlueprint
r::Brought(GrowthRate)
K::Brought(CarryingCapacity)
producers_competition::Brought(ProducersCompetition)
LogisticGrowth_(; kwargs...) = new(
fields_from_kwargs(
LogisticGrowth,
LogisticGrowth_,
kwargs;
default = (r = :Miele2019, K = 1, producers_competition = (; diag = 1)),
)...,
)
end
@blueprint LogisticGrowth_

function F.expand!(model, ::LogisticGrowth)
function F.expand!(raw, ::LogisticGrowth_)
# Gather all data set up by brought components
# to construct the actual functional response value.
s = model._scratch
s = raw._scratch
lg = Internals.LogisticGrowth(
# Alias so values gets updated on component `write!`.
s[:producers_competition],
s[:carrying_capacity],
# Growth rates are already stored in `model.biorates` at this point.
)
model.producer_growth = lg
raw.producer_growth = lg
end

@component LogisticGrowth
@component begin
LogisticGrowth <: ProducerGrowth
# requires(GrowthRate, CarryingCapacity, ProducersCompetition) # HERE: is that implied by brought fields?
blueprints(Blueprint::LogisticGrowth_)
end
export LogisticGrowth

(::_LogisticGrowth)(args...; kwargs...) = LogisticGrowth_(args...; kwargs...)

#-------------------------------------------------------------------------------------------
# Nutrient intake.

# Convenience elision of e.g. 'nodes = 2': just use NutrientIntake(2) to bring nodes.
# Alternately, the number of nodes can be inferred
# from the non-scalar values if any is given.
mutable struct NutrientIntake <: ProducerGrowth
r::Option{GrowthRate}
nodes::Option{Nutrients.Nodes}
turnover::Option{Nutrients.Turnover}
supply::Option{Nutrients.Supply}
concentration::Option{Nutrients.Concentration}
half_saturation::Option{Nutrients.HalfSaturation}
function NutrientIntake(nodes = missing; kwargs...)
(false) && (local NutrientIntake, _NutrientIntake) # (reassure JuliaLS)

mutable struct NutrientIntake_ <: ProducerGrowthBlueprint
r::Brought(GrowthRate)
nodes::Brought(Nutrients.Nodes)
turnover::Brought(Nutrients.Turnover)
supply::Brought(Nutrients.Supply)
concentration::Brought(Nutrients.Concentration)
half_saturation::Brought(Nutrients.HalfSaturation)
# Convenience elision of e.g. 'nodes = 2': just use NutrientIntake(2) to bring nodes.
# Alternately, the number of nodes can be inferred
# from the non-scalar values if any is given.
function NutrientIntake_(nodes = missing; kwargs...)
nodes = if haskey(kwargs, :nodes)
ismissing(nodes) ||
argerr("Nodes specified once as plain argument ($(repr(nodes))) \
Expand All @@ -77,9 +94,10 @@ mutable struct NutrientIntake <: ProducerGrowth
new(fields...)
end
end
@blueprint NutrientIntake_

function F.expand!(model, ::NutrientIntake)
s = model._scratch
function F.expand!(raw, ::NutrientIntake_)
s = raw._scratch
ni = Internals.NutrientIntake(
s[:nutrients_turnover],
s[:nutrients_supply],
Expand All @@ -88,13 +106,20 @@ function F.expand!(model, ::NutrientIntake)
s[:nutrients_names],
s[:nutrients_index],
)
model.producer_growth = ni
raw.producer_growth = ni
end

@component NutrientIntake requires(Foodweb)
@component begin
NutrientIntake <: ProducerGrowth
requires(Foodweb)
blueprints(Blueprint::NutrientIntake_)
end
export NutrientIntake

(::_NutrientIntake)(args...; kwargs...) = NutrientIntake_(args...; kwargs...)

@conflicts(NutrientIntake, Nti.Layer)

#-------------------------------------------------------------------------------------------
# These are exclusive ways to specify producer growth.
@conflicts(LogisticGrowth, NutrientIntake)
@conflicts(NutrientIntake, NtiLayer)
2 changes: 2 additions & 0 deletions src/dedicate_framework_to_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import .Framework
const F = Framework # Convenience alias for the whole components library.
import .F:
@blueprint,
@component,
@conflicts,
@method,
Brought,
CheckError,
Expand Down
30 changes: 1 addition & 29 deletions test/user/03-components.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,35 +18,7 @@ import .EN: WriteError

# Many small similar components tests files, although they easily diverge.
only = [
# "./data_components/species.jl"
# "./data_components/foodweb.jl"
# "./data_components/body_mass.jl"
# "./data_components/metabolic_class.jl"
# "./data_components/temperature.jl"
# "./data_components/hill_exponent.jl"
# "./data_components/growth_rate.jl"
# "./data_components/efficiency.jl"
# "./data_components/carrying_capacity.jl"
# "./data_components/mortality.jl"
# "./data_components/metabolism.jl"
# "./data_components/maximum_consumption.jl"
# "./data_components/producers_competition.jl"
# "./data_components/consumers_preferences.jl"
# "./data_components/handling_time.jl"
# "./data_components/attack_rate.jl"
# "./data_components/half_saturation_density.jl"
# "./data_components/intraspecific_interference.jl"
# "./data_components/consumption_rate.jl"
# "./data_components/nutrients/nodes.jl"
# "./data_components/nutrients/turnover.jl"
# "./data_components/nutrients/supply.jl"
# "./data_components/nutrients/concentration.jl"
# "./data_components/nutrients/half_saturation.jl"
"./data_components/nontrophic_layers/competition.jl"
"./data_components/nontrophic_layers/facilitation.jl"
"./data_components/nontrophic_layers/interference.jl"
"./data_components/nontrophic_layers/refuge.jl"
# "./code_components/linear_response.jl"
"./code_components/logistic_growth.jl",
] # Only run these if specified.
if isempty(only)
for subfolder in ["./data_components", "./code_components"]
Expand Down
11 changes: 7 additions & 4 deletions test/user/code_components/logistic_growth.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
m = base + lg
@test m.r == [0, 0, 0, 1, 1]
@test m.K == [0, 0, 0, 1, 1]
@test m.producers_competition == [
@test m.producers.competition == [
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
Expand All @@ -36,7 +36,7 @@
m = base + lg
@test m.r == [0, 0, 0, 1, 1]
@test m.K == [0, 0, 0, 1, 1]
@test m.producers_competition == [
@test m.producers.competition == [
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
Expand All @@ -47,8 +47,11 @@
# Cannot bring blueprints if corresponding components are already there.
@sysfails(
base + GrowthRate(5) + LogisticGrowth(; r = 1),
Check(LogisticGrowth),
"blueprint also brings '$GrowthRate', which is already in the system."
Add(
BroughtAlreadyInValue,
GrowthRate,
[GrowthRate.Flat, false, LogisticGrowth.Blueprint],
)
)

# In this situation, just stop bringing.
Expand Down

0 comments on commit e09a37b

Please sign in to comment.