diff --git a/src/GraphDataInputs/expand.jl b/src/GraphDataInputs/expand.jl index 126d38972..4990bf24d 100644 --- a/src/GraphDataInputs/expand.jl +++ b/src/GraphDataInputs/expand.jl @@ -54,7 +54,8 @@ export @build_from_symbol # ========================================================================================== # Assuming the input is a scalar, expand to the desired size. -to_size(scalar, s) = fill(scalar, s isa Integer ? (s,) : s) +to_size(scalar, s) = fill(scalar, s) +to_size(scalar, s::Integer) = fill(scalar, (s,)) export to_size #------------------------------------------------------------------------------------------- diff --git a/src/components/blueprint_modules.jl b/src/components/blueprint_modules.jl index 6c4c605d6..e90a556c4 100644 --- a/src/components/blueprint_modules.jl +++ b/src/components/blueprint_modules.jl @@ -4,7 +4,26 @@ # Take this opportunity to reassure JuliaLS: these are keywords for the macros. # https://github.com/julia-vscode/StaticLint.jl/issues/381#issuecomment-2361743645 if (false) - local graph, property, get, depends, nodes, edges, ref_cached, requires, E, V, Map, dense + #! format: off + ( + local + E, + Map, + V, + dense, + depends, + edges, + get, + graph, + nodes, + property, + ref, + ref_cached, + requires, + + var"" + ) + #! format: on end module BlueprintModule @@ -25,6 +44,7 @@ import EcologicalNetworksDynamics: refs, refspace, to_dense_vector, + to_size, to_sparse_matrix, @GraphData, @check_list_refs, @@ -51,6 +71,7 @@ export Blueprint, refs, refspace, to_dense_vector, + to_size, to_sparse_matrix, @GraphData, @blueprint, diff --git a/src/components/body_mass.jl b/src/components/body_mass.jl index f413e5b78..096e7e7bb 100644 --- a/src/components/body_mass.jl +++ b/src/components/body_mass.jl @@ -1,7 +1,7 @@ # Set or generate body masses for every species in the model. # (reassure JuliaLS) -(false) && (local BodyMass, _BodyMass) +(false) && (local BodyMass) # ========================================================================================== # Blueprints. @@ -38,7 +38,7 @@ end mutable struct Map <: Blueprint M::@GraphData Map{Float64} species::Brought(Species) - BodyMassFromRawValues(M, sp = _Species) = new(@tographdata Map{Float64}, sp) + Map(M, sp = _Species) = new(@tographdata M Map{Float64}, sp) end F.implied_blueprint_for(bp::Map, ::_Species) = Species(refs(bp.M)) @blueprint Map "{species ↦ mass} map" @@ -62,9 +62,9 @@ end # From raw values. mutable struct Raw <: Blueprint - M::Vector{Float64} # HERE: also accept scalar. + M::Vector{Float64} species::Brought(Species) - BodyMassFromRawValues(M, sp = _Species) = new(Float64.(M), sp) + Raw(M, sp = _Species) = new(Float64.(M), sp) end F.implied_blueprint_for(bp::Raw, ::_Species) = Species(length(bp.M)) @blueprint Raw "masses values" @@ -76,75 +76,42 @@ function F.late_check(raw, bp::Map) @check_size M S end -function F.expand!(model, bp::BodyMassFromRawValues) - (; M) = bp - S = @get raw.S - @to_size_if_scalar Real M S - model._foodweb.M = M -end - -end - -# Body mass are either given as-is by user -# or they are calculated from the foodweb with a Z-value. -# As a consequence, component expansion only requires `Foodweb` -# in the second case. -# In spirit, this leads to the definition -# of "two different blueprints for the same component". - -# ========================================================================================== -# Emulate this with an abstract blueprint type. - -abstract type BodyMass <: ModelBlueprint end -# All subtypes must require(Species). - -# Construct either variant based on user input. -function BodyMass(raw = nothing; Z = nothing, M = nothing) - - (!isnothing(raw) && !isnothing(M)) && argerr("Body masses 'M' specified twice:\n\ - once as : $(repr(raw))\n\ - and once as : $(repr(M))") - israw = !isnothing(raw) || !isnothing(M) - isZ = !isnothing(Z) - M = israw ? (isnothing(raw) ? M : raw) : nothing +F.expand!(model, bp::Raw) = model._foodweb.M = bp.M - (!israw && !isZ) && argerr("Either 'M' or 'Z' must be provided to define body masses.") +#------------------------------------------------------------------------------------------- +# From a scalar broadcasted to all species. - (israw && isZ) && argerr("Cannot provide both 'M' and 'Z' to specify body masses. \n\ - Received M: $(repr(M))\n \ - and Z: $(repr(Z)).") +mutable struct Broadcast <: Blueprint + M::Float64 +end +@blueprint Broadcast "homogeneous mass value" depends(Species) +export Broadcast - israw && return BodyMassFromRawValues(M) +F.expand!(model, bp::Raw) = model._noodweb.M = to_size(bp.M, @get raw.S) - BodyMassFromZ(Z) end +# ========================================================================================== +# Component and generic constructor. + +@component BodyMass{Internal} requires(Species) blueprints(BodyMassBlueprints) export BodyMass -#------------------------------------------------------------------------------------------- -# Don't specify both ways. -@conflicts(BodyMassFromRawValues, BodyMassFromZ) -# Temporary semantic fix before framework refactoring. -F.componentof(::Type{<:BodyMass}) = BodyMass +_BodyMass(M) = BodyMass.Raw(M) +_BodyMass(M::Number) = BodyMass.Broadcast(M) +_BodyMass(; Z = nothing) = isnothing(Z) && argerr("Either 'M' or 'Z' must be provided \ + to define body masses.") -# ========================================================================================== # Basic query. - @expose_data nodes begin property(body_masses, M) get(BodyMasses{Float64}, "species") - ref(m -> m._foodweb.M) + ref(raw -> raw._foodweb.M) @species_index depends(BodyMass) end -# ========================================================================================== # Display. - -# Highjack display to make it like both blueprints provide the same component. -display_short(bp::BodyMass; kwargs...) = display_short(bp, BodyMass; kwargs...) -display_long(bp::BodyMass; kwargs...) = display_long(bp, BodyMass; kwargs...) - -function F.display(model, ::Type{<:BodyMass}) - "Body masses: [$(join_elided(model._body_masses, ", "))]" +function F.shortline(io::IO, model::Model, ::_BodyMass) + print(io, "BodyMass: [$(join_elided(model.body_masses, ", "))]") end diff --git a/src/components/foodweb.jl b/src/components/foodweb.jl index 83d3aef2f..2ead1cd93 100644 --- a/src/components/foodweb.jl +++ b/src/components/foodweb.jl @@ -6,7 +6,7 @@ # and values checks are performed against this layer. # (reassure JuliaLS) -(false) && (local Foodweb, _Foodweb, trophic) +(false) && (local Foodweb, trophic) # ========================================================================================== # Blueprints. @@ -135,7 +135,7 @@ const (TrophicLayer, _TrophicLayer) = (Foodweb, _Foodweb) export Foodweb, TrophicLayer # Precise edges specifications. -function (::_Foodweb)(A) +function _Foodweb(A) A = @tographdata A {SparseMatrix, Adjacency}{:bin} if A isa AbstractMatrix Foodweb.Matrix(A) @@ -145,7 +145,7 @@ function (::_Foodweb)(A) end # Construct blueprint from a random model. -function (::_Foodweb)(model::Union{Symbol,AbstractString}; kwargs...) +function _Foodweb(model::Union{Symbol,AbstractString}; kwargs...) model = @tographdata model Y{} @kwargs_helpers kwargs diff --git a/src/components/species.jl b/src/components/species.jl index ba5196ef4..4e44e736d 100644 --- a/src/components/species.jl +++ b/src/components/species.jl @@ -4,7 +4,7 @@ # because too many things depend on these. # (reassure JuliaLS) -(false) && (local Species, _Species, species) +(false) && (local Species, species) # ========================================================================================== # Blueprints. @@ -72,12 +72,12 @@ end # Component and generic constructor. @component Species{Internal} blueprints(SpeciesBlueprints) +export Species # Build from a number or default to names. -(::_Species)(n::Integer) = Species.Number(n) -(::_Species)(names) = Species.Names(names) +_Species(n::Integer) = Species.Number(n) +_Species(names) = Species.Names(names) -export Species # Display. function F.shortline(io::IO, model::Model, ::_Species)