Skip to content

Commit

Permalink
Docstrings update (#11)
Browse files Browse the repository at this point in the history
* DocStrings test code

* Add PARS and METHODS to Reaction docstrings

Uses PALEOboxes.DocStrings, which extends `DocStringExtensions` package

* Julia 1.6 compatibility fix (replace(...) only takes one pattern)

* another replace 1.6 compatibility fix

Co-authored-by: Stuart Daines <sd336@um-serve>
  • Loading branch information
sjdaines and Stuart Daines authored Apr 28, 2022
1 parent cf1af1c commit 9de81e1
Show file tree
Hide file tree
Showing 14 changed files with 290 additions and 38 deletions.
4 changes: 3 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
name = "PALEOboxes"
uuid = "804b410e-d900-4b2a-9ecd-f5a06d4c1fd4"
authors = ["Stuart Daines <[email protected]>"]
version = "0.16.0"
version = "0.17.0"

[deps]
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
Infiltrator = "5903a43b-9cc3-4c30-8d17-598619ec4e9b"
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
Expand All @@ -27,6 +28,7 @@ YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6"
[compat]
BenchmarkTools = "1.0"
DataFrames = "1.1"
DocStringExtensions = "0.8"
Documenter = "0.27"
Graphs = "1.4"
Infiltrator = "1.0"
Expand Down
2 changes: 1 addition & 1 deletion docs/src/CreateInitializeLoop.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ CurrentModule = PALEOboxes
```
## Model Creation

A [YAML](https://en.wikipedia.org/wiki/YAML) format configuration file defines both the model structure (spatial domains containing biogeochemical variables and reactions that operate on them) and model parameter values. Reactions (subtypes of [`AbstractReaction`](@ref)) are registered in [`reaction_factories`](@ref) Dict and identified by name in the configuration file.
A [YAML](https://en.wikipedia.org/wiki/YAML) format configuration file defines both the model structure (spatial domains containing biogeochemical variables and reactions that operate on them) and model parameter values. Reactions (subtypes of [`AbstractReaction`](@ref)) are identified by Type name (without module prefix) in the configuration file.

To create the model, the numerical solver or host should call [`create_model_from_config`](@ref) which reads the configuration file, and then:
- creates model [`Domain`](@ref)s and Reactions (subtypes of [`AbstractReaction`](@ref)), applying any [`Parameter`](@ref)s settings from the `parameters:` sections in the config file.
Expand Down
3 changes: 3 additions & 0 deletions docs/src/Reaction API.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ setvalue!
## Registering with PALEOboxes framework
```@docs
create_reaction(ReactionType::Type{<:AbstractReaction}, base::ReactionBase)
find_reaction
find_all_reactions
```

## Optional initialisation callbacks to define Domain Grids and array sizes
Expand Down
126 changes: 126 additions & 0 deletions src/DocStrings.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
"""
DocStrings
Extends Julia `DocStringExtensions` package to provide PALEO-specific "Abbreviations" to list
Reaction Parameters and Variables in docstrings.
exports PARS, METHODS_SETUP, METHODS_INITIALIZE, METHODS_DO
`\$(PARS)` expands to a list of Reaction Parameters
`\$(METHODS_SETUP)`, `\$(METHODS_INITIALIZE)`, `\$(METHODS_DO)` expand to a list of ReactionMethods and Variables
NB: a Reaction is created with `create_reaction`. In addition, `\$(METHODS_DO)` etc call `register_methods(rj::AbstractReaction)`,
so may fail if this call fails with default parameters.
"""
module DocStrings
import DocStringExtensions as DSE
import PALEOboxes as PB

struct Pars <: DSE.Abbreviation end

const PARS = Pars()

function DSE.format(::Pars, buf, doc)
local docs = get(doc.data, :fields, Dict())
local binding = doc.data[:binding]
local object = Docs.resolve(binding)

# println(buf, "PARS binding $binding object $object")

try
rj = PB.create_reaction(object, PB.ReactionBase(name="test", classname="test", external_parameters=Dict{String, Any}()))
if hasproperty(rj, :pars)
PB.add_par(rj, rj.pars)
end
# println(buf, "$object $(length(PB.get_parameters(rj))) Parameters" )
for p in PB.get_parameters(rj)
md = "- `$(p.name)[$(typeof(p.v))]`="
md *= md_value(p.v)
md *= isempty(p.units) ? "," : " ($(p.units)),"
md *= " `default_value`="*md_value(p.default_value)*","
md *= isempty(p.allowed_values) ? "" : " `allowed_values`=$(p.allowed_values),"
md *= " `description`=\"$(escape_md(p.description))\""
println(buf, md)
end
catch e
println(buf, escape_md("PARS exception: $e"))
end
return nothing
end

struct Methods <: DSE.Abbreviation
field::Symbol
end

const METHODS_SETUP = Methods(:methods_setup)
const METHODS_INITIALIZE = Methods(:methods_initialize)
const METHODS_DO = Methods(:methods_do)

function DSE.format(methods::Methods, buf, doc)
local docs = get(doc.data, :fields, Dict())
local binding = doc.data[:binding]
local object = Docs.resolve(binding)

try
# println(buf, "PARS binding $binding object $object")

rj = PB.create_reaction(object, PB.ReactionBase(name="test", classname="test", external_parameters=Dict{String, Any}()))
if hasproperty(rj, :pars)
PB.add_par(rj, rj.pars)
end

d = PB.Domain(name="test", ID=1, parameters=Dict{String, Any}())
rj.base.domain = d
PB.register_methods!(rj)

for m in getproperty(rj.base, methods.field)
println(buf, "- `$(m.name)`")
for v in PB.get_variables(m)
md = " - "
md *= v.link_optional ? "\\[" : ""
md *= "`$(v.localname)`"
md *= v.link_optional ? "\\]" : ""
ln = PB.combine_link_name(v)
md *= (ln == v.localname) ? "" : " --> $(escape_md(ln))"
units = PB.get_attribute(v, :units)
md *= " ($(units)),"
md *= " `$(PB.get_var_type(v))`,"
vfunction = PB.get_attribute(v, :vfunction)
md *= (ismissing(vfunction) || vfunction == PB.VF_Undefined) ? "" : " `$vfunction`,"
description = PB.get_attribute(v, :description)
md *= " `description`=\"$(escape_md(description))\""

println(buf, md)
end
end
catch e
println(buf, escape_md("METHODS $methods exception: $e"))
end

return nothing
end



function escape_md(str::AbstractString)
str = replace(str, "_"=>"\\_")
str = replace(str, "\$"=>"\\\$")
str = replace(str, "*"=>"\\*")
return str
end

md_value(v) = "$v"
md_value(v::AbstractString) = "\"$(escape_md(v))\""
function md_value(vv::Vector{<:AbstractString})
evv = [escape_md(v) for v in vv]
return "$evv"
end
function md_value(vv::Vector)
str = "$vv"
str = replace(str, "["=>"\\[")
str = replace(str, "]"=>"\\]")
return str
end

export PARS, METHODS_SETUP, METHODS_INITIALIZE, METHODS_DO
end
2 changes: 2 additions & 0 deletions src/PALEOboxes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import YAML
import Graphs # formerly LightGraphs
import DataFrames

include("DocStrings.jl")

include("Types.jl")
include("CoordsDims.jl")
include("Fields.jl")
Expand Down
4 changes: 2 additions & 2 deletions src/Reaction.jl
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,8 @@ end
Add a single parameter or parameters from fields of `objectwithpars` to a new Reaction.
Not usually needed: [`add_reaction_factory`](@ref) will provide a [`reaction_factory`](@ref)
that does this automatically for Parameters in pars::ParametersTuple.
Not usually needed: Parameters in `pars::ParametersTuple`` will be added automatically, only needed if there are additional
Parameters that are not members of `pars`.
"""
function add_par(reaction::AbstractReaction, par::AbstractParameter)

Expand Down
24 changes: 21 additions & 3 deletions src/ReactionFactory.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,30 @@ function find_all_reactions()

for (rname, ReactionType) in duplicate_keys
@warn "Duplicate reaction name $rname for Type $ReactionType (removing from Dict)"
delete!(rdict, rname)
if haskey(rdict, rname)
@warn "Duplicate reaction name $rname for Type $(rdict[rname]) (removing from Dict)"
delete!(rdict, rname)
end
end

return rdict
end

"""
find_reaction(class::AbstractString) -> ReactionType
Look up "class" in list of Reactions from [`find_all_reactions`](@ref), and return
fully-qualified Reaction Type (including module prefixes).
"""
function find_reaction(class::AbstractString)
rdict = find_all_reactions()
if haskey(rdict, class)
return rdict[class]
else
error("class \"$class\" not found")
end
end

"""
create_reaction(ReactionType::Type{<:AbstractReaction}, base::ReactionBase) -> reaction::AbstractReaction
Expand Down Expand Up @@ -77,11 +95,11 @@ Examples:
"""
function show_all_reactions(classnamefilter="", typenamefilter="")

for (classname, RType) in find_all_reactions()
for (classname, ReactionType) in find_all_reactions()
if occursin(classnamefilter, classname)
println(classname)

rtstring = Printf.@sprintf("%s", RType)
rtstring = Printf.@sprintf("%s", ReactionType)
if occursin(typenamefilter, rtstring )
println(" ", RType)

Expand Down
16 changes: 15 additions & 1 deletion src/reactioncatalog/FluxPerturb.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import Interpolations

import PALEOboxes as PB

using ..DocStrings

"""
ReactionFluxPerturb
Expand All @@ -16,7 +18,13 @@ The input time Variable is `tforce`, with default linking to the `global.tforce`
Use the configuration file to rename output variable `F` (and if necessary, the input Variable `tforce`).
NB: no extrapolation ! (so eg set guard values for `force_times` at -1e30, 1e30)
NB: no extrapolation ! (so eg set guard values for `perturb_times` at -1e30, 1e30)
# Parameters
$(PARS)
# Methods and Variables
$(METHODS_DO)
"""
Base.@kwdef mutable struct ReactionFluxPerturb{P} <: PB.AbstractReaction
base::PB.ReactionBase
Expand Down Expand Up @@ -108,6 +116,12 @@ end
ReactionRestore
Adds `RestoringFlux` in response to discrepancy between Variable `WatchLevel` and Parameter `RequiredLevel`
# Parameters
$(PARS)
# Methods and Variables
$(METHODS_DO)
"""
Base.@kwdef mutable struct ReactionRestore{P} <: PB.AbstractReaction
base::PB.ReactionBase
Expand Down
46 changes: 21 additions & 25 deletions src/reactioncatalog/Fluxes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import LinearAlgebra # for I

import Infiltrator # Julia debugger

using ..DocStrings
"""
FluxContrib(
fluxprefix::AbstractString, flux_list;
Expand Down Expand Up @@ -148,12 +149,17 @@ For each `fluxname` in `fluxlist`, creates:
(or if `const_stub==true`, a constant `VarProp`).
- if `flux_totals` is `true`, a total `VarPropScalar` `target_prefix.v*"flux_total_"*fluxname`.
# Parameters
$(PARS)
# Methods and Variables for default Parameters
$(METHODS_DO)
"""
Base.@kwdef mutable struct ReactionFluxTarget{P} <: PB.AbstractReaction
base::PB.ReactionBase

pars::P = PB.ParametersTuple(
PB.ParStringVec("fluxlist", String[],
PB.ParStringVec("fluxlist", ["example"],
description="available fluxes"),

PB.ParString("target_prefix", "flux_",
Expand Down Expand Up @@ -231,27 +237,15 @@ end
Copy fluxes, optionally using transfer matrices to define cell-cell mappings if input and output fluxes
are in a different Domain.
A transfer is defined by Parameters:
- `input_fluxes`: A list of input Variables generated by matches to string
`[inputdomain.][inputsubdomain.]prefix\$fluxname\$suffix` where `\$fluxname\$` matches any string.
These have local names `input_\$fluxname\$`.
- `output_fluxes`: Corresponding list of output Variables.
`[outputdomain.][outputsubdomain.]prefix\$fluxname\$suffix` where `\$fluxname\$` is generated from matches of `input_fluxes`.
These have local names `output_\$fluxname\$` and are optional (ie ignored if not linked). Usually the output Domain is the
Domain hosting the `ReactionFluxTransfer` (in general, it must be a Domain of the same size as the Domain hosting the
`ReactionFluxTransfer`).
- `transfer_multiplier`: A scalar multiplier (usually 1.0).
- `transfer_matrix`: Form of transfer matrix.
- 'Identity' copies fluxes directly assuming the output Domain has the same size as the input Domain.
- 'Distribute' uniformly distributes fluxes from input -> output, including common cases
n -> 1 (input flux is summed to a scalar output), 1 -> m (scalar input is distributed uniformly to m output cells).
There are three common cases:
- Transfer within a Domain, using `transfer_matrix` `Identity`
- Transfer from a `fluxXXX` Domain to the Domain hosting the `ReactionFluxTransfer`, using `transfer_matrix` to
define a mapping if these Domains are of different sizes.
- Transfer from a `fluxXXX` Domain to the boundary cells of an interior Domain (eg `ocean.oceansurface`),
where the Domain hosting the `ReactionFluxTransfer` is the corresponding boundary Domain (eg `oceansurface`).
# Parameters
$(PARS)
"""
Base.@kwdef mutable struct ReactionFluxTransfer{P} <: PB.AbstractReaction
base::PB.ReactionBase
Expand All @@ -260,20 +254,22 @@ Base.@kwdef mutable struct ReactionFluxTransfer{P} <: PB.AbstractReaction
PB.ParStringVec("fluxlist", String[],
description="available fluxes"),

PB.ParString("input_fluxes", "domain.flux_\$fluxname\$",
description="string to match to find input flux Variables,
will be linked to from local names \"input_<fluxname>\""),
PB.ParString("output_fluxes", "domain.subdomain.\$fluxname\$_sms",
description="string to use to generate output flux Variables where \$fluxname\$ is substituted from input_fluxes,
will be linked to from local names \"output_<fluxname>\""),
PB.ParString("input_fluxes", "[inputdomain.][inputsubdomain.]flux_\$fluxname\$",
description="string to match to find input flux Variables. "*
"These Variables will local names \"input_\$fluxname\$\"."),
PB.ParString("output_fluxes", "[outputdomain.][outputsubdomain.]\$fluxname\$_sms",
description="string to use to generate output flux Variables where \$fluxname\$ is substituted from input_fluxes. "*
"These Variables will local names \"output_\$fluxname\$\", and are optional (ie ignored if not linked). "*
"Usually the output Domain is the Domain hosting the ReactionFluxTransfer (in general, it must be a Domain "*
"of the same size as the Domain hosting the ReactionFluxTransfer)."),

PB.ParDouble("transfer_multiplier", 1.0,
description="multiplier for transfer"),
description="scalar multiplier for transfer"),
PB.ParString("transfer_matrix", "Identity",
allowed_values=["Identity", "Distribute", "Custom"],
description="matrix defining input Domain (length n) -> output Domain (length m) mapping:
'Identity' assumes m == n;
'Distribute' sums over n and them distributes fraction 1/m evenly to each m;
'Identity' copies fluxes directly, requires m == n;
'Distribute' uniformly distributes fluxes from input->output: sums over n and them distributes fraction 1/m evenly to each m;
'Custom' requires transfer matrix to be supplied"),
)

Expand Down
8 changes: 8 additions & 0 deletions src/reactioncatalog/Forcings.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module Forcings

import PALEOboxes as PB

using ..DocStrings

"""
ReactionForceInterp
Expand All @@ -14,6 +16,12 @@ The input time Variable is `tforce`, with default linking to the `global.tforce`
Use the configuration file to rename the output variable `F` (and if necessary, the input Variable `tforce`).
NB: no extrapolation ! (so eg set guard values for `force_times` at -1e30, 1e30)
# Parameters
$(PARS)
# Methods and Variables
$(METHODS_DO)
"""
Base.@kwdef mutable struct ReactionForceInterp{P} <: PB.AbstractReaction
base::PB.ReactionBase
Expand Down
Loading

2 comments on commit 9de81e1

@sjdaines
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register()

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/59328

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.17.0 -m "<description of version>" 9de81e17586c73d9e3b347555a245638f79dfae9
git push origin v0.17.0

Please sign in to comment.