Getting Started
This tutorial introduces the package through a realistic first workflow.
Goal
By the end, you will have:
- loaded weather into a
Weathertable - inspected the standardized variables
- aggregated fine-step weather into model-friendly steps
Why This Matters
Most weather workflows fail at the interface between raw data and model expectations:
- columns are named differently across sources
- durations and timestamps are not consistently encoded
- model clocks rarely match source resolution
The examples below show how PlantMeteo addresses these issues step by step.
1. Read and Standardize a Weather File
Convert heterogeneous input columns into Atmosphere-compatible variables and get a typed weather table (TimeStepTable{Atmosphere}) with known fields:
using PlantMeteo
using Dates
file = joinpath(dirname(dirname(pathof(PlantMeteo))), "test", "data", "meteo.csv")
meteo = read_weather(
file,
:temperature => :T,
:relativeHumidity => (x -> x ./ 100) => :Rh,
:wind => :Wind,
:atmosphereCO2_ppm => :Cₐ,
date_format = DateFormat("yyyy/mm/dd")
)
meteo| TimeStepTable{Atmosphere{(:date, :duration,...}(3 x 29): | |||||||||||||||||||||||||||||
| date | duration | T | Wind | P | Rh | Precipitations | Cₐ | e | eₛ | VPD | ρ | λ | γ | ε | Δ | clearness | Ri_SW_f | Ri_PAR_f | Ri_NIR_f | Ri_TIR_f | Ri_custom_f | hour_start | hour_end | temperature | relativeHumidity | Re_SW_f | wind | atmosphereCO2_ppm | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| DateTime | Dates.CompoundPeriod | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Time | Time | Float64 | Float64 | Float64 | Float64 | Float64 | |
| 1 | 2016-06-12T12:00:00 | 30 minutes | 25.0 | 1.0 | 101.325 | 0.6 | 0.0 | 380.0 | 1.90812 | 3.1802 | 1.27208 | 1.18389 | 2.44188e6 | 0.0675791 | 0.602345 | 0.190095 | 0.75 | Inf | Inf | Inf | Inf | Inf | 12:00:00 | 12:30:00 | 25.0 | 60.0 | 500.0 | 1.0 | 380.0 |
| 2 | 2016-06-12T12:30:00 | 30 minutes | 26.0 | 1.5 | 101.325 | 0.62 | 0.0 | 380.0 | 2.09238 | 3.37481 | 1.28243 | 1.17993 | 2.43951e6 | 0.0676446 | 0.610038 | 0.200215 | 0.75 | Inf | Inf | Inf | Inf | Inf | 12:30:00 | 13:00:00 | 26.0 | 62.0 | 500.0 | 1.5 | 380.0 |
| 3 | 2016-06-12T13:00:00 | 30 minutes | 25.3 | 1.5 | 101.325 | 0.58 | 0.0 | 380.0 | 1.87776 | 3.23752 | 1.35976 | 1.1827 | 2.44117e6 | 0.0675987 | 0.60088 | 0.193085 | 0.75 | Inf | Inf | Inf | Inf | Inf | 13:00:00 | 13:30:00 | 25.3 | 58.0 | 500.0 | 1.5 | 380.0 |
2. Inspect the Data You Will Feed to Models
Each row is an Atmosphere. You can get a row by indexing the table with a single index:
first_row = meteo[1]
first_row╭──── TimeStepRow ─────────────────────────────────────────────────────────────╮
│ Step 1: date=2016-06-12T12:00:00, duration=30 minutes, T=25.0, Wind=1.0, │
│ P=101.325, Rh=0.6, Precipitations=0.0, Cₐ=380.0, e=1.9081174488870076, │
│ eₛ=3.180195748145013 ... │
╰──────────────────────────────────────────────────────────────────────────────╯And because PlantMeteo implements the Tables.jl interface, table columns are easy to inspect and manipulate like a DataFrame. For example to get the temperature column as a vector:
meteo.T3-element Vector{Float64}:
25.0
26.0
25.3TimeStepTable also supports matrix-like indexing on rows and columns:
meteo[:T] # full column by Symbol
meteo["T"] # full column by String
meteo[1] # one row
meteo[1:2] # row subset as TimeStepTable
meteo[1, :] # one row (matrix-like syntax)
meteo[1, :T] # one cell by row + Symbol column
meteo[1, "T"] # one cell by row + String column
meteo[1:2, :T] # vector slice from one column
meteo[1:2, "T"]2-element Vector{Float64}:
25.0
26.03. Sample Weather to Match a Model Time Step
Sometimes models require weather at a coarser time step than the source data. For example, you may have hourly weather but want daily values for a crop model. PlantMeteo's sampling functions let you aggregate weather from source resolution to model resolution, and get an aggregated Atmosphere row per query step.
For example we can build a fake 2-day hourly series and sample it to get a daily-like Atmosphere for the 24th hour, using a rolling 24-source-step window:
# Build a longer hourly series for sampling demonstrations.
base = DateTime(2025, 1, 1)
meteo_hourly = Weather([
Atmosphere(
date = base + Hour(i),
duration = Hour(1),
T = 10.0 + i,
Wind = 1.0,
Rh = 0.55,
P = 100.0,
Ri_SW_f = 200.0
)
for i in 0:47
])
prepared = prepare_weather_sampler(meteo_hourly)
window = RollingWindow(24.0) # trailing 24-source-step window
sampled = sample_weather(prepared, 24; window = window)
# Get the temperature from the sampled row, which is a daily-averaged value of the 24 hourly source steps:
(;sampled.duration, sampled.date, sampled.T)(duration = Millisecond(86400000), date = DateTime("2025-01-01T23:00:00"), T = 21.5)This value is the mean of the 24 hourly temperatures from the source data, which matches our expectation for a daily-like sample:
using Statistics
mean(meteo_hourly[i].T for i in 1:24)21.54. Precompute Sampling for Whole Runs
Avoid repeated sampling work in long simulation loops, and get a cached sampled table for each requested sampling window:
tables = materialize_weather(prepared; windows = [window])
daily_like = tables[window]
(length(daily_like), daily_like[24].T ≈ sampled.T, length(meteo_hourly))(48, true, 48)5. Override Default Sampling Rules (Optional)
Encode model-specific aggregation rules per variable. Transforms are normalized and applied for the current call:
custom_transforms = (
T = MeanWeighted(),
Tmax = (source = :T, reducer = MaxReducer()),
Tsum = (source = :T, reducer = SumReducer())
)
custom_row = sample_weather(prepared, 24; window = window, transforms = custom_transforms)
(custom_row.T, custom_row.Tmax, custom_row.Tsum)(21.5, 33.0, 516.0)Next Steps
- Weather Data Sources: deeper look at file/API ingestion and export.
- Weather Sampling: windows, reducers, transforms, and caching strategy.
- API: full reference for all exported symbols.