-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c8ffd93
commit fcfbd24
Showing
3 changed files
with
225 additions
and
204 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,177 +1,211 @@ | ||
const DEGRADATION_RATE = Param(0.5; bounds=(0.0, 100.0)) | ||
const PULSE_LEVEL = Param(20.0; bounds=(0.0, 100.0)) | ||
const POPULATION_THRESHOLD = Param(20.0; bounds=(0.0, 100.0)) | ||
const CROP_OCCUPANCY = Param(5.0; bounds=(0.0, 10.0)) | ||
const ACTIVE = Param(5.0; bounds=(0.0, 10.0)) | ||
const ROTATION_NUMBER = Param(1.0; bounds=(0.0, 1000.0)) | ||
const ROTATION_SIZE = Param(1.0; bounds=(0.0, 1000.0)) | ||
|
||
""" | ||
Abstract supertype of `CellRule` for rules of exposure dynamics | ||
""" | ||
|
||
abstract type Exposure{R,W} <: CellRule{R,W} end | ||
|
||
""" | ||
ExponentialDecrease(; rate, timestep) | ||
ExponentialDecrease{R}(; rate, timestep) | ||
ExponentialDecrease{R,W}(; rate, timestep) | ||
Exponential decrease based on a degradation rate data, using exact solution. | ||
# Keyword Arguments | ||
- `rate`: Degradation rate. May be a `Number`, an [`Aux`](@ref) array or another [`Grid`](@ref). | ||
- `timestep`: Time step for the degradation rate, in a type compatible with the simulation `tspan`. | ||
Pass grid `Symbol`s to `R` or both `R` and `W` type parameters to use to specific grids. | ||
""" | ||
struct ExponentialDecrease{R,W,GR,TS,S} <: CellRule{R,W} | ||
rate::GR | ||
timestep::TS | ||
nsteps::S | ||
end | ||
function ExponentialDecrease{R,W}(; | ||
rate=DEGRADATION_RATE, | ||
timestep, | ||
) where {R,W} | ||
ExponentialDecrease{R,W}(rate, timestep, nothing) | ||
end | ||
|
||
precalcrule(rule::ExponentialDecrease, data) = precalc_timestep(rule, data) | ||
|
||
@inline function applyrule(data, rule::ExponentialDecrease, N, I) | ||
N > zero(N) || return zero(N) | ||
rt = get(data, rule.rate, I...) * rule.nsteps | ||
return @fastmath N * exp(- rt) | ||
end | ||
|
||
""" | ||
ThresholdExposure{R,W}(; | ||
crop, pulselevel, popthreshold, degradationrate, timestep, | ||
active, pulselevel, popthreshold, timestep, | ||
) | ||
Exposure by treatment depending on a threshold layer (population size) set with | ||
Exposure depending on a threshold layer (population size) set with | ||
`popthreshold`. | ||
Treatment is applied only on `crop` fields (value of `crop` cell > 0). | ||
The amount of treatment `pulselevel` is applied once this threshold is passed. | ||
At every time step the pesticide is degradated at rate `degradationrate` | ||
Exposure is applied only on `active` fields (value of `active` cell > 0). | ||
The amount of exposure `pulselevel` is applied once this threshold is passed. | ||
# Keyword Arguments | ||
- `crop`: Crop field. May be a `Number`, an [`Aux`](@ref) array or another [`Grid`](@ref). | ||
Treatment is applied when `crop > zero(crop) || return zero(pesticide) `. | ||
- `pulselevel`: Pulse level of treatment. May be a `Number`, an [`Aux`](@ref) array or | ||
another [`Grid`](@ref). | ||
- `popthreshold`: Threshold population level triggering the treatment release. May be a | ||
`Number`, an [`Aux`](@ref) array or another [`Grid`](@ref) of the same type as the | ||
`population` layer (Read is a Tuple(pesticide, population)) since used in the comparison: | ||
`population < rule.popthreshold`. | ||
- `degradationrate`: Rate of degradation of the treatment. May be a `Number`, an | ||
[`Aux`](@ref) array or another [`Grid`](@ref) of a type compatible with `pulselevel`. | ||
- `active`: Crop field. Exposure is applied when when `active` is above zero. | ||
- `pulselevel`: Pulse level of exposure. | ||
- `popthreshold`: Threshold population level triggering the exposure release. Must be | ||
of the same type as the `population` layer (Read is a Tuple(pesticide, population)) | ||
since used in the comparison: `population < rule.popthreshold`. | ||
- `timestep`: Time step for the exposure, in a type compatible with the simulation `tspan`. | ||
Arguments `active`, pulselevel` and `popthreshold` may be | ||
a `Number`, an [`Aux`](@ref) array or another [`Grid`](@ref). | ||
Pass grid `Symbol`s to `R` or both `R` and `W` type parameters to use to specific grids. | ||
Read: Tuple(pesticide, population) | ||
Written: Tuple(pesticide) | ||
Read: Tuple(exposure, population) | ||
Written: Tuple(exposure) | ||
""" | ||
struct ThresholdExposure{R,W,CP,PL,PT,DR,TS,S} <: Exposure{R,W} | ||
crop::CP | ||
struct ThresholdExposure{R,W,CP,PL,PT,TS,S} <: Exposure{R,W} | ||
active::CP | ||
pulselevel::PL | ||
popthreshold::PT | ||
degradationrate::DR | ||
timestep::TS | ||
nsteps::S | ||
end | ||
ThresholdExposure{R,W}(; | ||
crop=CROP_OCCUPANCY, | ||
active=ACTIVE, | ||
pulselevel=PULSE_LEVEL, | ||
popthreshold=POPULATION_THRESHOLD, | ||
degradationrate=DEGRADATION_RATE, | ||
timestep, | ||
) where {R,W} = ThresholdExposure{R,W}(crop, pulselevel, popthreshold, degradationrate, timestep, nothing) | ||
) where {R,W} = ThresholdExposure{R,W}(active, pulselevel, popthreshold, timestep, nothing) | ||
|
||
precalcrule(rule::ThresholdExposure, data) = precalc_timestep(rule, data) | ||
|
||
@inline function applyrule(data, rule::ThresholdExposure, (pesticide, population), I) | ||
crop = get(data, rule.crop, I...) | ||
crop > zero(crop) || return zero(pesticide) | ||
@inline function applyrule(data, rule::ThresholdExposure, (X, N), I) | ||
active = get(data, rule.active, I...) | ||
active > zero(active) || return zero(X) | ||
pulse = get(data, rule.pulselevel, I...) | ||
z = get(data, rule.popthreshold, I...) | ||
rt = get(data, rule.degradationrate, I...) * rule.nsteps | ||
# return pesticide only | ||
if population < z | ||
@fastmath pesticide * exp(- rt) | ||
if N < z | ||
return X | ||
else | ||
@fastmath pesticide * exp(- rt) + pulse | ||
return X + pulse | ||
end | ||
end | ||
|
||
""" | ||
RotationExposure{R,W}(; | ||
crop, rotationsize, rotationindex, popthreshold, degradationrate, timestep, | ||
active, rotationsize, rotationindex, popthreshold, timestep, | ||
) | ||
Exposure by several treatments in rotation depending on a threshold layer (population size) set with `popthreshold`. | ||
Treatment is applied only on `crop` fields. | ||
`rotationsize` is the total number of treatments, and `rotationindex` is the number (index) of a specific treatment. | ||
Several exposures in rotation depending on a threshold layer (population size) set with `popthreshold`. | ||
Exposure is applied only on `active` fields. | ||
`rotationsize` is the total number of exposures, and `rotationindex` is the number (index) of a specific exposure. | ||
Example: with 3 treatments, `rotationsize=3` for each treatment rule and `rotationindex` is set to 1, 2 and 3 respectivelly. | ||
Example: with 3 exposures, `rotationsize=3` for each exposure rule and `rotationindex` is set to 1, 2 and 3 respectivelly. | ||
The amount of pesticide `pulselevel` is applied once the threshold `popthreshold` is passed given by second layer. | ||
At every time step the pesticide is degradated at rate `degradationrate` | ||
# Keyword Arguments | ||
- `crop`: Crop field. May be a `Number`, an [`Aux`](@ref) array or another [`Grid`](@ref). | ||
Treatment is applied when `crop > zero(crop) || return zero(pesticide) `. | ||
- `rotationsize`: Total number of type in rotation. May be a `Number`, an [`Aux`](@ref) array or | ||
another [`Grid`](@ref). | ||
- `rotationindex`: Index of the treatment applied. A type compatible with `rotationsize`. | ||
- `pulselevel`: Pulse level of treatment. May be a `Number`, an [`Aux`](@ref) array or | ||
another [`Grid`](@ref). | ||
- `popthreshold`: Threshold population level triggering the treatment release. May be a | ||
`Number`, an [`Aux`](@ref) array or another [`Grid`](@ref) of the same type as the | ||
`population` layer (Read is a Tuple(pesticide, population)) since used in the comparison: | ||
`population < rule.popthreshold`. | ||
- `degradationrate`: Rate of degradation of the treatment. May be a `Number`, an | ||
[`Aux`](@ref) array or another [`Grid`](@ref) of a type compatible with `pulselevel`. | ||
- `active`: Crop field. Exposure is applied when `active` is above zero. | ||
- `rotationsize`: Total number of type in rotation. | ||
- `rotationindex`: Index of the exposure applied. A type compatible with `rotationsize`. | ||
- `pulselevel`: Pulse level of exposure. | ||
- `popthreshold`: Threshold population level triggering the exposure release. May be of | ||
the same type as the `population` layer (Read is a Tuple(pesticide, population)) | ||
since used in the comparison: `population < rule.popthreshold`. | ||
- `timestep`: Time step for the exposure, in a type compatible with the simulation `tspan`. | ||
Arguments `active`, `rotationsize`, `pulselevel` and `popthreshold` may be | ||
a `Number`, an [`Aux`](@ref) array or another [`Grid`](@ref). | ||
Pass grid `Symbol`s to `R` or both `R` and `W` type parameters to use to specific grids. | ||
Read: Tuple(pesticide, population, rotation) | ||
Written: Tuple(pesticide, rotation) | ||
""" | ||
struct RotationExposure{R,W,CP,RS,RN, PL,PT,DR,TS,S} <: Exposure{R,W} | ||
crop::CP | ||
struct RotationExposure{R,W,CP,RS,RN, PL,PT,TS,S} <: Exposure{R,W} | ||
active::CP | ||
rotationsize::RS | ||
rotationindex::RN | ||
pulselevel::PL | ||
popthreshold::PT | ||
degradationrate::DR | ||
timestep::TS | ||
nsteps::S | ||
end | ||
RotationExposure{R,W}(; | ||
crop=CROP_OCCUPANCY, | ||
active=ACTIVE, | ||
rotationsize=ROTATION_SIZE, | ||
rotationindex=ROTATION_NUMBER, | ||
pulselevel=PULSE_LEVEL, | ||
popthreshold=POPULATION_THRESHOLD, | ||
degradationrate=DEGRADATION_RATE, | ||
timestep, | ||
) where {R,W} = RotationExposure{R,W}(crop, rotationsize, rotationindex, pulselevel, popthreshold, degradationrate, timestep, nothing) | ||
) where {R,W} = RotationExposure{R,W}( | ||
active, rotationsize, rotationindex, pulselevel, popthreshold, timestep, nothing | ||
) | ||
|
||
precalcrule(rule::RotationExposure, data) = precalc_timestep(rule, data) | ||
|
||
@inline function applyrule(data, rule::RotationExposure, (pesticide, population, rotation), I) | ||
crop = get(data, rule.crop, I...) | ||
crop > zero(crop) || return (zero(pesticide), rotation) | ||
@inline function applyrule(data, rule::RotationExposure, (X, N, R), I) | ||
active = get(data, rule.active, I...) | ||
active > zero(active) || return (zero(X), R) | ||
pulse = get(data, rule.pulselevel, I...) | ||
z = get(data, rule.popthreshold, I...) | ||
rt = get(data, rule.degradationrate, I...) * rule.nsteps | ||
if ( population >= z && | ||
mod(_rotationstep(rotation), rule.rotationsize) == mod(rule.rotationindex, rule.rotationsize) && | ||
_timestep(rotation) != currentframe(data) ) | ||
# println("IN $I") | ||
@fastmath ( | ||
pesticide * exp(- rt) + pulse, | ||
_updatestep(rotation + 1, currentframe(data)) | ||
) | ||
# Test 3 conditions: | ||
# 1. population should be over the threshold z | ||
# 2. `rotationstep` should match the modularity of `rotationindex` compared to the number of rotation = `rotationsize` | ||
# 3. `datestep` of rotation should be different to currentframe | ||
if (N >= z && | ||
mod(_rotationstep(R), rule.rotationsize) == mod(rule.rotationindex, rule.rotationsize) && | ||
_datestep(R) != currentframe(data)) | ||
# apply pesticide and update datestep and rotation | ||
return (X + pulse, @fastmath _updaterotation(R, currentframe(data))) | ||
else | ||
# println("OUT $I") | ||
@fastmath ( | ||
pesticide * exp(- rt), | ||
rotation | ||
) | ||
return (X, R) | ||
end | ||
end | ||
|
||
struct RotationStruct{RS,TS} | ||
rotationStep::RS | ||
timeStep::TS | ||
struct Rotation{RS,TS} | ||
rotationstep::RS | ||
datestep::TS | ||
end | ||
|
||
_rotationstep(rs::Rotation) = rs.rotationstep | ||
_datestep(rs::Rotation) = rs.datestep | ||
|
||
# All numerical operation are on the first element | ||
Base.:*(rs::RotationStruct, x::Number) = RotationStruct(x * rs.rotationStep, rs.timeStep) | ||
Base.:*(x::Number, rs::RotationStruct) = RotationStruct(x * rs.rotationStep, rs.timeStep) | ||
Base.:+(rs::RotationStruct, x::Number) = RotationStruct(x + rs.rotationStep, rs.timeStep) | ||
Base.:+(x::Number, rs::RotationStruct) = RotationStruct(x + rs.rotationStep, rs.timeStep) | ||
Base.:+(rs1::RotationStruct, rs2::RotationStruct) = RotationStruct(rs1.rotationStep + rs2.rotationStep, rs1.timeStep + rs2.timeStep) | ||
Base.:-(rs::RotationStruct, x::Number) = RotationStruct(rs.rotationStep - x, rs.timeStep) | ||
Base.:-(x::Number, rs::RotationStruct) = RotationStruct(x - rs.rotationStep, rs.timeStep) | ||
Base.:-(rs1::RotationStruct, rs2::RotationStruct) = RotationStruct(rs1.rotationStep - rs2.rotationStep, rs1.timeStep - rs2.timeStep) | ||
|
||
Base.zero(::Type{<:RotationStruct{T1,T2}}) where {T1,T2} = RotationStruct(zero(T1), zero(T2)) | ||
|
||
_updatestep(rs::RotationStruct, y::Number) = RotationStruct(rs.rotationStep, y) | ||
_updatestep(rs::RotationStruct, y::DateTime) = RotationStruct(rs.rotationStep, y) | ||
_rotationstep(rs::RotationStruct) = rs.rotationStep | ||
_timestep(rs::RotationStruct) = rs.timeStep | ||
|
||
initrotation(tspan_start, dims) = fill(RotationStruct(1,tspan_start), dims) | ||
Base.:*(rs::Rotation, x::Number) = Rotation(x * rs.rotationstep, rs.datestep) | ||
Base.:*(x::Number, rs::Rotation) = Rotation(x * rs.rotationstep, rs.datestep) | ||
Base.:/(rs::Rotation, x::Number) = Rotation(rs.rotationstep / x, rs.datestep) | ||
# Keep datestep of first element | ||
Base.:+(rs1::Rotation, rs2::Rotation) = Rotation(rs1.rotationstep + rs2.rotationstep, rs1.datestep) | ||
# Keep datestep of first element | ||
Base.:-(rs1::Rotation, rs2::Rotation) = Rotation(rs1.rotationstep - rs2.rotationstep, rs1.datestep) | ||
|
||
Base.isless(rs1::Rotation, rs2::Rotation) = isless(rs1.rotationstep, rs2.rotationstep) | ||
Base.zero(::Type{<:Rotation{T1,T2}}) where {T1,T2} = Rotation(zero(T1), zero(T2)) | ||
Base.oneunit(::Type{<:Rotation{T1,T2}}) where {T1,T2} = Rotation(oneunit(T1), oneunit(T2)) | ||
|
||
_updaterotation(rs::Rotation, y::Int) = Rotation(rs.rotationstep+1, y) | ||
_updaterotation(rs::Rotation, y::DateTime) = Rotation(rs.rotationstep+1, y) | ||
|
||
|
||
""" | ||
initrotation(tspan_start, dims) | ||
Initialise a `rotation` grid fill of `1` at time `tspan_start`. | ||
# Keyword Arguments | ||
- `tspan_start`: Starting time to apply `RotationExposure` rules. | ||
- `dims`: an `AbstractVector` defining the size of the grid. | ||
""" | ||
initrotation(tspan_start, dims) = fill(Rotation(1,tspan_start), dims) | ||
|
Oops, something went wrong.