Simulation Engine
Brian S. Yandell
03 August 2026
engine.RmdThis document details the core data structures, S3 class definitions, leftist-tree event queue algorithms, competing risk stage transitions, and operational execution loops governing the ewing simulation engine.
1. Community Simulation State & Data Matrices
This document details the critical data structures and primary execution pipelines used internally to drive the event-based simulation in Quantitative Population Ethology.
Table of Contents
1. State Management (The community Object)
The core architecture operates around a robust state object called
community. The simulation evaluates and updates this object
at every step, meaning past events are discarded except when
intrinsically expressed by an organism’s currently modeled history or
future transition path.
The community object comprises several components:
-
pop: A matrix constructed per species holding raw population attributes. Note: Thecommunity$popitems dynamically store dimensional data with features mapping to rows and individuals mapped to columns. This rotated alignment guarantees that calling an individual isolates its column, simultaneously retrieving its entire feature subset cleanly. -
org: Contains global organism configurations, traits, and behavioral interaction constants. (UsegetOrgInfoandgetOrgInteract). -
temp: A configuration array maintaining parameters like environmental temperature cycles and degree-day thresholds. -
count: Actively running summary tallies. -
cpu: Diagnostic CPU measurement.
Each organism tracked under pop evaluates parameters
defining precise spatial interactions, life cycles, and behavior
triggers. Notable row attributes inside the pop column
trace include:
-
left,right,up: Tree linkages to navigate network topologies -
pos.a,pos.b,pos.c: Triangular continuous positional coordinates mapped onto the immediate substrate environment -
stage&future: Current developmental array and the immediately pending advancement event -
sub.stage&sub.future: Current geographical substrate target and anticipated movement
Simulation events systematically pluck organisms holding imminent
mintime thresholds. Upon evaluation, monadic
(e.g. chronological molting) or dyadic (e.g. host-parasite conflicts)
modifications physically rewrite these state features on the subject
population matrices natively. Aggregated structural translations are
subsequently written outbound to writeCount for data
visualization.
2. Global Parameters (Datafiles)
Baseline operational properties, mapping keys, and environment
coefficients are pulled from global definitions within the source code
ewing/data/ structure.
Interaction & Functional Parameters
-
organism.features.txt: Base limits and definitions (units, offspring, attack behavior) mapping the base functional limits of species against environments. -
host.parasite.txt: Dyadic interaction matrices mapping parasite feeding behavior, oviposition matrices, and gender determination parameters to specific stages of standard host lifecycles.
Transition Matrices
These describe the fundamental arrays an organism iterates through based on the passage of time or ecological interaction.
-
future.host.txt&future.parasite.txt: Linkcurrentlifecycle states to predictablefuturetransitions based on temporal progressions (fid,time,pch,color). Example:egg->larva->pupa. -
substrate.host.txt&substrate.parasite.txt: Spatial matrices defining how organisms interpret physical positioning based on distinct geometry arrays (e.g., fruit vs. leaf vs. twig).
3. Simulation Functional Flow
The package operates via a sequence of procedural pipelines dividing into environment initialization, event logic sorting, and temporal adjustments.
A. Initialization (init.simulation |
init.R & community.R)
This initializes the entire topological network from an arbitrary starting population.
-
initOrgInfo: Pulls functional baselines from the environment datasets into network configurations. -
initTemp: Binds weather array logic to system base. -
init.population: Initializes array counts mapping strictly generated physical and temporal states across populations using a leftist tree framework.
B. Core Event Loop (future.events.R)
The quantitative framework iterates over timeline horizons.
-
Step & History Accumulation: When called on a
pre-existing
communityobject containing event counts (community$count$counts),future.eventsautomatically setsappend = TRUEand computesstart_stepfromcommunity$count$step. Subsequent steps increment asstart_step + istep, accumulating event logs continuously across sequential simulation blocks. - Generates counts and maps the lowest sequential transition milestone
via
update_mintime. - Passes identified organisms to target handlers based on condition:
-
event.death: Eliminates nodes out of positional topological maps. -
event.future: A monadic evaluator routing progress natively (moving between age classes and stages based on time horizons). Also triggers geographic movementevent.movedepending on active substrate mapping rules. -
event.attack: A dyadic evaluator representing predation. Models parasitic search routines over distinct topological layers until a target is acquired. Results either functionally remove targets (Ectoparasite) or compromise them.
-
-
Count Logging (
sim.R&fileCount.R):updateCount()updatescommunity$count$stepand captures return values fromwriteCount(), persisting complete event histories intocommunity$count$counts.setEvents()cleanly updates period tallies without truncatingcommunitywhen life stage vectors are empty.
4. Plot Routines
Graphic outputs are natively aggregated via S3 method generics
(autoplot / ggplot_ewing_*) and structured
outputs:
-
ewing_ageclass.R: Reshapes continuous event counts fromreadCount(community)into age-class time-series.ggplot_ewing_ageclass()formats titles dynamically (e.g."Age Distribution over Time (<nstep> steps)"), assigns open shape markers per species (scale_shape_manual(name = "Species", values = c(1, 2, 0, 5, 6, 3, 4))), and displays step-by-step history from step 0 to the final accumulated step. -
temp.R/temp.plot&temp.design: Evaluation vectors tracking degree-day dynamics and temperature threshold simulations -
triangle.R/plot_current: Two-dimensional plotting logic tracking population clusters specifically using triangular mappings upon the target substrates -
spline.R/five.plot: Interactive functional graphics used for plotting development loops
5. AI Documentation Refactoring
Historically, deep structural and architectural documentation for the
ewing project was nested entirely within the central
README.md in the form of raw developer logic logs tracing
back to 2014. These blocks were often intertwined with localized
debugging notes and granular pseudo-code matrices.
Through integrated AI assistance methodologies tracked via our
walkthrough and prompts structures:
- The literature was heavily partitioned. High-level installation and
accessibility notes remain natively within the core system
README.md, while strict architectural notes were securely abstracted into thisinst/doc/refactor/directory. - Former raw development logic was synthesized into comprehensive
explanations outlining system dependencies (e.g. clearly identifying
State Management,Global Parameters, andFunctional Pipelines). - Legacy debug logic (long-resolved codebase “To-Dos” and graphical constraints from older implementations) was thoroughly scrubbed to prevent downstream developmental confusion.
This compartmentalization vastly improves the cognitive parsing of the system boundaries and isolates architectural onboarding specifically to developers who require access without cluttering the baseline user experience.
Prompt
- Context: I’m refactoring a package for predator-prey simulation modeling
- Role: Act as an experienced R package developer and simulation modeler
- Action: Review my draft code in this repo and suggest improvements
- Format: Provide feedback as tracked changes with explanations
- Tone: Professional, constructive, and encouraging
Response
Viewed DESCRIPTION:1-24 Viewed NAMESPACE:1-177 Viewed sim.R:1-109 Viewed Org.R:1-335 Viewed c.ewing.R:1-26
It is fantastic to see the effort you are putting into refactoring
the ewing simulation package! Moving an established systems
model toward modern R standards is no small task. You’ve already made an
excellent step by integrating roxygen2 and leveraging
modern tidyverse dependencies.
Below is my review covering a few key files (R/Org.R,
R/c.ewing.R, and R/sim.R). I’ve structured my
feedack with tracked changes, explanations, and modern R styling tips to
help with CRAN compliance and robustness.
1. Safely Handling Warnings (R/Org.R)
In getOrgFeature, the code suppresses warnings when
coercing f to numeric. However, it explicitly alters the
global options(warn = -1) state.
- opwarn <- options( warn = -1 )
- numf <- suppressWarnings(as.numeric( f ))
- options( opwarn )
+ numf <- suppressWarnings(as.numeric(f))Explanation: suppressWarnings()
executes its inner expression in a controlled context and natively
handles the suppression of warnings. Explicitly tweaking global option
states is redundant and risky—if an unexpected error occurs during
coercion and execution immediately halts, the
options(opwarn) restore command will never execute, leaving
the user with all warnings permanently disabled in their R session.
2. Avoiding .GlobalEnv Modifications
(R/Org.R)
In copyOrgInfo, variables are dynamically queried and
assigned directly to the user’s global environment
(.GlobalEnv).
copyOrgInfo <- function( fromname, toname )
{
for( i in c("sim")) {
from <- paste( fromname, i, sep = "." )
if( exists( from )) {
toto <- paste( toname, i, sep = "." )
- assign( toto, get( from ), ".GlobalEnv" )
+ assign( toto, get( from ), parent.frame() )
cat( "copied", from, "to", toto, "\n" )
}
}
invisible()
}Explanation: CRAN heavily penalizes packages for
unexpectedly modifying the global environment as it silently overwrites
the user’s workspace variables. If the required design maps this
variable directly to the caller’s environment, pointing
assign() to parent.frame() is significantly
safer and is CRAN-compliant! Alternatively, returning the objects inside
of a named list() is an even safer functional approach.
3. Safer Iteration with Sequences (R/Org.R)
Standard practice calls for cautious sequence generation to avoid failures when operating on subsets that might equal zero lengths.
get.alive <- function( community, species, substrate )
{
alive <- getOrgAlive( community, species )
- alive <- seq( length( alive ))[alive]
+ alive <- seq_along(alive)[alive]
alive[ substrate == get.species.element( community, species, "sub.stage", alive ) ]
}
-getOrgSubstrate <- function( community, species, elements = seq( nrow( inter )),
+getOrgSubstrate <- function( community, species, elements = seq_len( nrow( inter )),
substrate = getOrgFeature( community, species, "substrate" ),
inter = getOrgInteract( community, substrate, species ))
{ ...Explanation: Using seq(length(x)) or
seq(nrow(y)) can fail disastrously if the target is empty,
as seq(0) translates to 1 0. Utilizing
seq_along() (for vector lengths) and seq_len()
(for rows/counts) acts defensively strings empty vectors together
correctly, saving you quite a few headaches when dealing with edge-case
simulations!
4. Proper Exporting of S3 Methods (R/c.ewing.R)
You have an S3 method for concatenating ewing objects
computationally. It functions perfectly, yet looking at the
NAMESPACE file it appears the object method is not being
correctly mapped.
Explanation: In modern roxygen2
architectures, appending #' @export directly above an S3
method behaves correctly under the hood—it parses the name and
automatically handles calling S3method(c, ewing)
dynamically inside the generated NAMESPACE file. Without an
export or manual mapping, users calling
c(simulation1, simulation2) will simply default to the core
R method rather than triggering your custom routine.
5. Improving Subsetting Readability (R/sim.R)
There are several older styling aesthetics in
updateCount that are vestiges of older R coding designs.
Take the following parsing parameters:
- if( !missing( step ))
- community <- setCount( community,, list( step = step ))
+ if (!missing(step)) {
+ community <- setCount(community, list(step = step))
+ }Explanation: The multi-comma separation syntax
,, was previously frequently leveraged to skip default
arguments. Updating your package to use precise named argument supplying
vastly increases code readability for downstream collaborators.
Migrating logic blocks to bracket spacing styles (like
if (!missing(step)) { ... }) aligns exceptionally well with
standard Tidyverse style guidelines.
You are doing tremendous work bridging this classic package into the
modern ecosystem! Taking advantage of vectorized functional checks (like
seq_along()) and avoiding explicit GlobalEnv
overrides will make your code beautifully CRAN-compliant. If you have
any questions or’d like to dive into cleaning up a specific file
structure further, please don’t hesitate to ask!.
Walkthrough
The following implementation plan has been completely deployed. By
using robust R development guidelines (including the
tidyverse styling philosophy and roxygen2
documentation), this package logic is significantly more resilient to
edge cases and compliant with modern CRAN expectations.
R/Org.R:
-
Disabled Options Hijacking: Removed global
options(warn = -1)state modifications around parameter coercions, allowingsuppressWarningsto natively and safely act locally without leaking configuration states into the user’s base R environment. -
Modernized
copyOrgInfo: Rather than invokingassigndirectly to.GlobalEnv(a major anti-pattern in R package development), the wrapper now dynamically loops attributes and saves them directly into a newly evaluated and returned R list (out <- list()). -
Robust Sequences: Refactored logic loops iterating
over raw matrix rows or vector counts arrays. Methods like
getOrgSubstrate(),sampleOrgSubstrate(), andget.alive()now rely safely onseq_along(target)orseq_len(nrow(target))instead of the fragileseq()mechanism that fails catastrophically for empty lists.
R/sim.R:
-
Styling Consistency Changes: Fixed archaic logical
AND array evaluators (updating
&conditional evaluators insideifstatements to the non-vectorized short-circuit logical&&identifier). Fixed older spacing layouts to match modern visual style guides acrossupdateCount. -
Explicit Arguments Mapping: Fixed skipping logical
positional parameters (replacing explicit omission identifiers
,,with dynamically mapped parameter sets). Modified call parameters tocommunity <- setCount(community, elements = list(step = step)).
Verification & Deployment
After editing the c.ewing.R component,
devtools::document() was executed locally to dynamically
consume the roxygen2 tags and appropriately create real
registrations pointing to the concatenator logic inside the backend
NAMESPACE file. Following compilation, the package itself
successfully loaded into a session namespace using
devtools::load_all() to confirm no explicit syntax blocks
or malformed variables blocked compilation.
Prompt
- Context: I have a Shiny app that simulates predator-prey dynamics
- Role: Act as an expert R developer and Shiny application designer
- Action: Review my draft shiny app code in this repo and suggest ways to view the simulation step by step (or at least in smaller chunks than the current implementation)
- Format: Provide feedback as tracked changes with explanations
- Tone: Professional, constructive, and encouraging
Response
It’s great to see you exploring ways to make the Ewing simulation more interactive for users! Implementing a step-through debugger or “play-by-play” viewing mode provides fantastic intuition for complex predator-prey dynamics.
Based on the structure of future.events and
ewingApp.R, your core simulation algorithm
(future.events) actually already supports
accepting a pre-existing community list and continuing the
simulation from exactly where it left off! The bottleneck is simply how
your Shiny Application manages its caching memory.
To achieve step-by-step interactivity, we need to transition away
from static functional caching (bindCache) and move toward
stateful reactivity using
shiny::reactiveVal.
Here is my technical review and the pseudo-code tracking changes to achieve this:
1. Shift from Caching to Stateful Reactivity
Currently, simres is built using a rigid functional
pipeline that runs initialization and execution synchronously. We need
to decouple initialization from stepping.
Analogy: Right now, your app acts like a microwave—you type
in the time, hit start, and wait for the final timer to ding to get your
meal. By switching to reactiveVal, we turn the app into a
stovetop, allowing the user to stir the pot, pause, check the
temperature, and continue cooking!
Suggestions in ewingApp.R:
- simres <- shiny::bindEvent(
- shiny::bindCache(
- shiny::reactive({
- nsim <- as.integer(input$nsim)
...
- future.events(siminit, nstep = input$steps, plotit = FALSE) # simulate future events
- } else {
...
- }),
- input$host, input$parasite, input$steps, input$nsim,
- input$datafile),
- input$go)
+ # 1. Provide an isolated memory block to hold the active simulation graph
+ active_sim <- shiny::reactiveVal(NULL)
+
+ # 2. Add an initialization observer linked to a "Reset/Init" Action Button
+ shiny::observeEvent(input$go_init, {
+ siminit <- init.simulation(count = as.numeric(c(input$host, input$parasite)),
+ datafile = datafile())
+ active_sim(siminit) # Stores standard state
+ })
+
+ # 3. Step forward when user clicks the Step Button
+ shiny::observeEvent(input$go_step, {
+ shiny::req(active_sim()) # Wait until initialized
+ new_state <- future.events(active_sim(), nstep = input$step_size, plotit = FALSE)
+ active_sim(new_state) # Update the global memory
+ })Explanation: reactiveVal explicitly
establishes a memory hook inside the user’s session. The moment the user
clicks input$go_init, the application generates the Day 0
community and stores it in active_sim. When the user clicks
input$go_step, Shiny extracts the current
community graph, pushes it back into
future.events() to advance it forward by
input$step_size, and actively overwrites the history!
2. Upgrading the UI Triggers
You will need to break up the single execution button in
ewingInput() to support independent initialization vs. step
inputs.
- shiny::sliderInput(ns("steps"),
- label = "Simulation steps:",
...
- shiny::actionButton(ns("go"), "Start Simulation"),
+ shiny::sliderInput(ns("step_size"),
+ label = "Steps per click:",
+ min = 10, max = 500, value = 50, step = 10),
+
+ shiny::fluidRow(
+ shiny::column(6, shiny::actionButton(ns("go_init"), "Initialize")),
+ shiny::column(6, shiny::actionButton(ns("go_step"), "Step Forward!"))
+ ),Explanation: By breaking up the flow, users clearly
define the environment using the sliders, commit the environment via
“Initialize”, and then advance the clock dynamically via “Step”. Because
the graphs natively monitor active_sim() recursively, every
time “Step Forward!” is clicked, ggplot2 outputs will
smoothly redraw the advancing curve.
3. Preserving the Historical Snapshot Plot List (Optional)
When future.events runs, it builds out a
$plot attribute. Currently, each recursive call to
future.events completely resets the pstep
attribute list. If you want the interactive charts to append to
historical datasets rather than just drawing the immediate window, you
can map the historical vectors natively inside the
future.events.R function:
- pstep <- 0
+ # Preserve historical snapshots if community already has prior plot graphs loaded
+ pstep <- length(community$plot)
+ if (!is.null(community$plot)) p <- community$plotImplementing these updates effectively turns your Shiny wrapper into
a dynamic stepping simulator. If you’re ready to try these out, I can
organize an implementation plan applying reactiveVal
integrations!
Walkthrough
The interactive step-by-step refactor is completely
deployed and verified. The ewingApp.R file natively
executes state-mapping for single simulations, allowing the user to
instantiate the environment via “Init / Run” and safely push updates
incrementally using “Step Forward”!
Logic Execution
-
State Initialization: Replaced the static
functional block in
bindCache()withactive_sim <- shiny::reactiveVal(NULL). Memory generation is now actively tied to discrete trigger scopes (shiny::observeEvent). -
Split UI Actions: Overhauled
ewingInput(). The legacy genericgoaction button was replaced by two distinct functions:go_inittargets state-rebuilding and applies to both single & multiple simulation blocks, whilego_stepprovides interactive iteration targeted firmly at single instances (nsim == 1). -
New Slider Variables: The generic parameter slider
length defaults dynamically inside the server block. Renaming the
variable array
step_sizeensures downstream logic separates bounding boxes appropriately! We mappednsim > 1looping cleanly to bypass stepping requirements. -
Fixed
future.events.RHistory: Adjusted local list arrays pointing tocommunity$plotinsidefuture.events(), meaning consecutive requests to step bounding variables properly concatenate onto existing arrays instead of dumping your simulated past.
Status Report (April 6, 2026)
✅ Completed
-
Reactive State Management: Switched from a static
simulation block to a
reactiveVal(active_sim). This allows the simulation to persist and be updated incrementally. -
Interactive UI:
- Added “Init / Run” to initialize the environment.
- Added “Step Forward” to progress the simulation by a user-defined step size (default 50).
- Unified these buttons in the main output panel for better accessibility.
-
Error Handling & Guardrails:
- Empty Plots: Fixed runtime crashes when viewing plots before the first step by adding placeholder messages (“Step 0: … data not yet available”).
-
History Persistence: Updated
future.events.Rto ensure stepping forward appends to the simulation history instead of resetting it.
-
Verification: Verified codebase integrity using
devtools::load_all('.').
Status Report (April 7, 2026)
✅ Completed (April 7)
-
Initial Plot Loading (
nsim == 1): Modifiedgo_initto instantly advance bystep_sizeafter simulation initialization to prevent rendering empty “Step 0” graphs. -
Improved Plot Rendering
(
ewing_ageclass.R):- Migrated from
geom_path()togeom_step()combined withgeom_point(). This represents discrete population changes more accurately and prevents errors when groups have a single observation. - Enforced
group = Stateinternally to ensure ggplot handles layers reliably.
- Migrated from
-
Extinction Handling:
ewingApp.Rnow intercepts empty substrate data (e.g. population reaches 0) and smoothly renders an"extinct"panel instead of choking the ggplot pipeline. -
Substrate Legend Logic: Resolved the floating TODO
in
ewingApp.R. The app now automatically segments species populations from their environmental arrays usingget.species(), allowingewing_substratecharts to natively append their correct structural titles without arbitrary manual overrides. -
Legend Output Formatting:
-
ewing_substrate.R: Appended plotting aesthetic overrides (override.aes) directly togeom_textguides to natively render precise mapping characters natively instead of defaulting generic placeholder"a"labels. -
ewing_ageclass.R: Built structured mapping indexing and explicitly typed raw variables into an orderedfactor. Output graphs now renderStateprogression chronologically instead of alphabetically natively! Adjusted conditional scaling block to explicitly position the"total"classification squarely between active species groups, and disabled substrates manually withinewingApp.Rbindings.
-
-
Plot Rendering Architecture: Resolved a deep-seated
rendering layer bug breaking shiny app reactivity locally and on posit
servers
(
Can't add sppplot()[[i]] to a <ggplot> object.). Removed incompatibleggplot2addition (+) routines for combined application outputs across all primary files (ewingApp.R,multApp.R,substrateApp.R, andorigEwingApp.R). Cleanly shifted visualization grids to properly utilizecowplot::plot_grid(plotlist = sppplot())list handling functions natively for scaled visual structures.
Status Report (July 27, 2026)
✅ Completed (July 27)
-
Organism Movement on Hex Grid
(
hexmoveApp):- Implemented per-substrate unit triangle coordinate rescaling () with a 15% inner padding buffer to keep organism symbols 100% inside their substrate polygon borders.
- Created discrete 6-sided polygon tile overlay
(
create_hex_overlay) across plant substrate elements. - Created
hexmoveApplauncher script (inst/scripts/hexmoveApp.R) and Quarto demo (demos/hexmoveApp.qmd).
-
Systems Ethology Platform Refactor (
sysetholApp&ewingApp):- Exported
R/sysetholApp.R(sysetholApp,sysetholInput,sysetholOutput,sysetholServer). - Added sidebar
Run EngineandResetaction buttons to keep the main panel header clean. - Added conditional sidebar controls (
conditionalPanel):Steps per clickwhennsim == 1,Total Simulation steps/Confidence Band Envelopewhennsim > 1. - Added dynamic tab visibility in
sysetholOutput(displayingEnvelope Plotstab ONLY whennsim > 1). - Simplified
R/ewingApp.Rto compose UI/server directly fromsysetholAppcomponents +downloadApp. - Embedded serverless WebAssembly Shinylive block into
demos/sysetholApp.qmd.
- Exported
-
Dist Plots Aesthetics & Continuous Stepping
History:
- Renamed title to
"Age Distribution over Time"with dynamic step formatting ("Age Distribution over Time (<nstep> steps)"or"(<nstep> steps, nsim = <nsim>)"). - Mapped species legends (
shape = .data$Species) using distinct open symbol aesthetics (scale_shape_manual(name = "Species", values = c(1, 2, 0, 5, 6, 3, 4))) for open circles, triangles, squares, etc. - Preserved continuous step history across sequential simulation
stepping (
future.events.R,initCount.R,fileCount.R,sim.R), accumulating step indices (start_step + istep) so “Run Engine” stepping renders complete history starting from step 0. - Fixed
setEvents()inR/Events.Rso empty life stage vectors do not truncate thecommunityobject.
- Renamed title to
6. Isle Royale Predator-Prey Framework & Temporal Scaling
A. Ecological & Demographic Stage Structure
-
Moose Life Cycle (
future.moose):- Calf (0–1 yr): High vulnerability to wolf predation and winter weather.
- Yearling (1–2 yrs): Dispersal stage across habitat features.
- Adult (2–9 yrs): Prime reproductive cows (1–2 calves/yr). Low natural vulnerability to wolf predation.
- Senior (10+ yrs): Aging adults with tooth wear and osteoarthritis; prime target for wolf pack hunting.
-
Wolf Life Cycle (
future.wolf):- Pups, Subadults, & Adults: Pack structure with denning pups, roving subadults, and breeding pack adults.
-
Predation Functional Response: Wolf attack rates in
competing risk tables (
moose.wolf.txt) selectively target vulnerable Calves and Senior adults over prime Adults.
B. Temporal Scaling Rules & Thermal Regimes
-
Time Unit Equivalence:
- 1 Time Unit (1 Step) = 1 Day (Degree-Day / daily unit increment).
- 365 Time Units (Steps) = 1 Year.
-
Life-Cycle Scaling:
-
Stage Transitions: Transitions in
future.mooseandfuture.wolf(Calf Yearling Adult; Pup Subadult Adult) occur at365steps (1 year). -
Reproductive & Lifespan Scaling: Annual births
occur every
365steps. Adult Moose lifespan =2920steps (8 years); Adult Wolf lifespan =2555steps (7 years).
-
Stage Transitions: Transitions in
-
Thermal Regimes: Moose timing uses Degree-Days
(
units = DD, 1 step = 1 daily fluctuation cycle); Wolf timing uses clock hours (units = hr, 24 hours = 1 step / day).