diff --git a/docs/src/developing.md b/docs/src/developing.md index 48ff489cbc41846ef120ee972dcf3ef6941c6ccd..8049e8a2e2911b910eb0bcd9903e03255c7e7935 100644 --- a/docs/src/developing.md +++ b/docs/src/developing.md @@ -149,3 +149,10 @@ julia> 2km / 10m Within Persefone, the following units and dimensions have been imported for direct usage: `cm`, `m`, `km`, `m²`, `ha`, `km²`, `mg`, `g`, `kg`, `Length`, `Area`, `Mass`. + +### Dates + +Persefone expands the default [Dates](https://docs.julialang.org/en/v1/stdlib/Dates/) library +with the [`AnnualDate`](@ref) type, which can be used to store dates that recur every year +(e.g. migration or harvest). `AnnualDates` can be compared and added/subtracted just as normal +dates. Use [`thisyear()`](@ref) to convert an `AnnualDate` to a `Date`. diff --git a/docs/src/simulation.md b/docs/src/simulation.md index 11ccea7c9ae9f5956f14fc0143f8741c410e170a..6bd8e0b4edb00ca4505a30f8d45b79500fa94826 100644 --- a/docs/src/simulation.md +++ b/docs/src/simulation.md @@ -9,7 +9,7 @@ This file defines the module, including all exported symbols and two high-level ```@autodocs Modules = [Persefone] -Pages = ["Persefone.jl"] +Pages = ["Persefone.jl", "core/utils.jl"] ``` ## simulation.jl diff --git a/src/core/utils.jl b/src/core/utils.jl index f15df79f09d6d893bbd2478d7655404fb4c77d7b..d8077c38fcc0fa0f687625c877b8dd607a518fd5 100644 --- a/src/core/utils.jl +++ b/src/core/utils.jl @@ -8,10 +8,11 @@ import Unitful: cm, m, km, ha, mg, g, kg, Length, Area, Mass const m² = m^2 const km² = km^2 -import Base./ # enable division with different length/area unit types -/(x::S,y::T) where {S<:Length, T<:Length} = (upreferred(x)/m) / (upreferred(y)/m) -/(x::S,y::T) where {S<:Area, T<:Area} = (upreferred(x)/m²) / (upreferred(y)/m²) -/(x::S,y::T) where {S<:Mass, T<:Mass} = (upreferred(x)/g) / (upreferred(y)/g) + +# enable division with different length/area unit types +Base.:(/)(x::S,y::T) where {S<:Length, T<:Length} = (upreferred(x)/m) / (upreferred(y)/m) +Base.:(/)(x::S,y::T) where {S<:Area, T<:Area} = (upreferred(x)/m²) / (upreferred(y)/m²) +Base.:(/)(x::S,y::T) where {S<:Mass, T<:Mass} = (upreferred(x)/g) / (upreferred(y)/g) ## Utility type and function for working wth recurring dates @@ -20,6 +21,8 @@ import Base./ # enable division with different length/area unit types A type to handle recurring dates (e.g. migration, harvest). Stores a month and a day, and can be compared against normal dates. +To save typing, a Tuple{Int64,Int64} is automatically converted to an +AnnualDate, allowing this syntax: `nestingend::AnnualDate = (August, 15)`. """ mutable struct AnnualDate month::Int64 @@ -30,18 +33,31 @@ mutable struct AnnualDate if !(0 < month <= 12) Base.error("AnnualDate: month $month is out of range.") #TODO replace with exception elseif !(0 < day <= 31) + # not strictly accurate (AnnualDate(February, 30) is possible), but good enough Base.error("AnnualDate: day $day is out of range.") #TODO replace with exception else new(month, day) end end +# (automatically) convert integer tuples to AnnualDate. +# Allows writing `(August, 2)` instead of `AnnualDate(August, 2)` where an AnnualDate is expected. +AnnualDate(ad::Tuple{Int64,Int64}) = AnnualDate(ad...) +Base.convert(::Type{AnnualDate}, ad::Tuple{Int64,Int64}) = AnnualDate(ad) + # Interface with Dates AnnualDate(date::Date) = AnnualDate(month(date), day(date)) Dates.month(ad::AnnualDate) = ad.month Dates.day(ad::AnnualDate) = ad.day # Instantiate a recurring date for a given year +""" + thisyear(annualdate, model) + nextyear(annualdate, model) + lastyear(annualdate, model) + +Convert an AnnualDate to a Date, using the current/next/previous year of the simulation run. +""" thisyear(ad::AnnualDate, model::SimulationModel) = Date(year(model.date), ad.month, ad.day) nextyear(ad::AnnualDate, model::SimulationModel) = Date(year(model.date)+1, ad.month, ad.day) lastyear(ad::AnnualDate, model::SimulationModel) = Date(year(model.date)-1, ad.month, ad.day) diff --git a/src/nature/macros.jl b/src/nature/macros.jl index 54e3bef8ac70098a2d26f6552a3449ddf3605f5a..14a81d3f2546b0c9f217f010b0a603e48374dc58 100644 --- a/src/nature/macros.jl +++ b/src/nature/macros.jl @@ -495,8 +495,7 @@ end """ @thisyear(annualdate) -Construct a date object referring to the current model year from an AnnualDate -(== Tuple{Int64,Int64}). +Construct a date object referring to the current model year from an AnnualDate. """ macro thisyear(annualdate) :(thisyear($(esc(annualdate)), $(esc(:model)))) @@ -505,9 +504,17 @@ end """ @nextyear(annualdate) -Construct a date object referring to the next year in the model from an AnnualDate -(== Tuple{Int64,Int64}). +Construct a date object referring to the next year in the model from an AnnualDate. """ macro nextyear(annualdate) :(nextyear($(esc(annualdate)), $(esc(:model)))) end + +""" + @lastyear(annualdate) + +Construct a date object referring to the last year in the model from an AnnualDate. +""" +macro lastyear(annualdate) + :(lastyear($(esc(annualdate)), $(esc(:model)))) +end