Documentation
¶
Overview ¶
Package firewx provides the shared observation and unit types used by the fire danger models in this repository.
Every physical quantity has a named type. The public API never accepts a bare float64 for a measurement, because the models in this repository disagree about units in ways that are easy to get wrong and hard to notice: the Canadian FWI system is specified in degrees Celsius, km/h and millimetres, while NFDRS and the Rothermel spread model are specified in degrees Fahrenheit, mph, inches and imperial fuel loadings.
The convention is that observations are carried in SI, and each model converts at its own boundary to whatever its published equations require.
Example ¶
Example shows the shared observation type. An observation carries SI units, and each derived value is absent when an input is absent.
package main
import (
"fmt"
firewx "alpineworks.io/firewx"
)
func main() {
o := firewx.Obs{
Temperature: firewx.Some(firewx.Celsius(20)),
RelativeHumidity: firewx.Some(firewx.Percent(50)),
}
// The dew point comes from the temperature and the humidity.
dew := o.DewPoint().Must()
// The vapour pressure deficit comes from the same two inputs.
vpd := o.VaporPressureDeficit().Must()
fmt.Printf("dew point=%.1f C\n", dew)
fmt.Printf("VPD=%.2f kPa\n", vpd)
}
Output: dew point=9.3 C VPD=1.17 kPa
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Celsius ¶
type Celsius float64
Celsius is a temperature in degrees Celsius. This is the canonical temperature unit for observations.
func DewPoint ¶
DewPoint returns the dew point temperature for t and rh, inverting the Magnus-Tetens approximation. Results below roughly -45C or above the ambient temperature should be treated as out of range.
func (Celsius) Fahrenheit ¶
func (c Celsius) Fahrenheit() Fahrenheit
Fahrenheit converts c to degrees Fahrenheit.
Example ¶
ExampleCelsius_Fahrenheit shows a unit conversion. Each physical quantity has a named type, so the compiler stops a unit mistake.
package main
import (
"fmt"
firewx "alpineworks.io/firewx"
)
func main() {
boiling := firewx.Celsius(100)
fmt.Printf("%.0f F\n", boiling.Fahrenheit())
}
Output: 212 F
type Degrees ¶
type Degrees float64
Degrees is a compass bearing, 0-360, measured clockwise from true north.
type Fahrenheit ¶
type Fahrenheit float64
Fahrenheit is a temperature in degrees Fahrenheit, used by NFDRS and by several of the older single-equation indices.
func (Fahrenheit) Celsius ¶
func (f Fahrenheit) Celsius() Celsius
Celsius converts f to degrees Celsius.
type Feet ¶
type Feet float64
Feet is a length. NFDRS specifies its reference anemometer height in feet.
type Hectopascals ¶
type Hectopascals float64
Hectopascals is a pressure, the conventional unit for both station pressure and for the Hot-Dry-Windy index's vapour pressure deficit term.
func (Hectopascals) Kilopascals ¶
func (h Hectopascals) Kilopascals() Kilopascals
Kilopascals converts h to kilopascals.
type Inches ¶
type Inches float64
Inches is a depth of precipitation, as required by NFDRS and the Keetch-Byram Drought Index.
func (Inches) Millimeters ¶
func (i Inches) Millimeters() Millimeters
Millimeters converts i to millimetres.
type KilometersPerHour ¶
type KilometersPerHour float64
KilometersPerHour is a wind speed, as required by the Canadian FWI system's Initial Spread Index.
func (KilometersPerHour) MetersPerSecond ¶
func (v KilometersPerHour) MetersPerSecond() MetersPerSecond
MetersPerSecond converts v to metres per second.
type Kilopascals ¶
type Kilopascals float64
Kilopascals is a pressure, used for vapour pressure and vapour pressure deficit.
func SaturationVaporPressure ¶
func SaturationVaporPressure(t Celsius) Kilopascals
SaturationVaporPressure returns the saturation vapour pressure at temperature t, using the Magnus-Tetens approximation. The coefficients (0.6108, 17.27, 237.3) are the form given by Allen et al. 1998, FAO Irrigation and Drainage Paper 56, Equation 11.
func VaporPressureDeficit ¶
func VaporPressureDeficit(t Celsius, rh Percent) Kilopascals
VaporPressureDeficit returns the difference between the saturation vapour pressure at t and the actual vapour pressure implied by rh.
VPD is a stronger predictor of fire activity in the Pacific Northwest than relative humidity alone, and is the moisture term of the Hot-Dry-Windy index.
func (Kilopascals) Hectopascals ¶
func (k Kilopascals) Hectopascals() Hectopascals
Hectopascals converts k to hectopascals.
type Meters ¶
type Meters float64
Meters is a length, used for sensor heights and station elevation.
type MetersPerSecond ¶
type MetersPerSecond float64
MetersPerSecond is a wind speed. This is the canonical wind unit for observations.
func AdjustWindHeight ¶
func AdjustWindHeight(v MetersPerSecond, from, to Meters, z0 Roughness) MetersPerSecond
AdjustWindHeight converts a wind speed measured at height from to the equivalent speed at height to, using the logarithmic wind profile.
This corrects for measurement height only. It does not correct for shelter: a sensor on a house in a treed neighbourhood is not measuring a slowed version of the open-country wind, it is measuring a different flow. Treat the result as a defensible approximation for trend purposes rather than as equivalent to a properly sited RAWS observation.
Example ¶
ExampleAdjustWindHeight shows the wind-height correction. It raises a wind speed from a low sensor to the 10 m reference the Canadian system assumes.
package main
import (
"fmt"
firewx "alpineworks.io/firewx"
)
func main() {
// A 3 m sensor over suburban roughness reads 4 m/s. Correct it up to 10 m.
corrected := firewx.AdjustWindHeight(4, 3, firewx.HeightFWI, firewx.RoughnessSuburban)
fmt.Printf("%.1f m/s\n", corrected)
}
Output: 6.7 m/s
func (MetersPerSecond) KilometersPerHour ¶
func (v MetersPerSecond) KilometersPerHour() KilometersPerHour
KilometersPerHour converts v to kilometres per hour.
func (MetersPerSecond) MilesPerHour ¶
func (v MetersPerSecond) MilesPerHour() MilesPerHour
MilesPerHour converts v to miles per hour.
type MilesPerHour ¶
type MilesPerHour float64
MilesPerHour is a wind speed, as required by NFDRS and the Fosberg index.
func (MilesPerHour) MetersPerSecond ¶
func (v MilesPerHour) MetersPerSecond() MetersPerSecond
MetersPerSecond converts v to metres per second.
type Millimeters ¶
type Millimeters float64
Millimeters is a depth of precipitation, or a length. This is the canonical precipitation unit for observations.
type Obs ¶
type Obs struct {
Time time.Time `json:"time"`
Temperature Opt[Celsius] `json:"temperature"`
RelativeHumidity Opt[Percent] `json:"relative_humidity"`
WindSpeed Opt[MetersPerSecond] `json:"wind_speed"`
WindDirection Opt[Degrees] `json:"wind_direction"`
WindGust Opt[MetersPerSecond] `json:"wind_gust"`
SolarRadiation Opt[WattsPerSquareMeter] `json:"solar_radiation"`
Pressure Opt[Hectopascals] `json:"pressure"`
// Precipitation accumulated since the previous observation, not since
// midnight and not a rate. Accumulation windows are a common source of
// silent error when merging sources; normalise on ingest.
Precipitation Opt[Millimeters] `json:"precipitation"`
// PrecipDuration is the time during which precipitation actually fell
// within the accumulation window. NFDRS requires this, and most stations
// can only estimate it. A tipping bucket logged at a short archive
// interval can derive it directly, which is one of the few respects in
// which a well-configured personal station beats a RAWS.
PrecipDuration Opt[time.Duration] `json:"precip_duration"`
// SnowCovered suppresses fire danger output when set. NFDRS treats a
// snow-covered station as having no fire danger regardless of the other
// measurements.
SnowCovered Opt[bool] `json:"snow_covered"`
}
Obs is a single weather observation.
Time is always UTC. Every measurement is optional, because every sensor can fail independently of the others; a station that has lost its pyranometer can still drive the Canadian FWI system, and one that has lost its rain gauge cannot drive either system correctly but should fail loudly rather than silently assume zero.
func (Obs) DewPoint ¶
DewPoint returns the dew point derived from temperature and relative humidity. It returns an empty Opt if either input is absent, or if the dew point is undefined.
The package DewPoint function returns NaN at a non-positive humidity. This method must not put that NaN into a present Opt, because NaN then propagates silently through the models. Therefore an undefined dew point becomes an absent value here.
func (Obs) VaporPressureDeficit ¶
func (o Obs) VaporPressureDeficit() Opt[Kilopascals]
VaporPressureDeficit returns the VPD derived from temperature and relative humidity, or an empty Opt if either input is missing.
type Opt ¶
type Opt[T any] struct { // contains filtered or unexported fields }
Opt holds a value that may be absent.
Missing data is the normal case, not an error case: RAWS drop hours routinely, consumer weather stations drop them more often, and a sensor can fail independently of the rest of the station. The alternative of signalling absence with NaN is a trap here, because NaN propagates silently through the finite-difference loop in the Nelson dead fuel moisture model and produces output that looks plausible but is not.
Opt marshals to JSON as either the bare value or null, so persisted observations and model state round-trip through a jsonb column without a wrapper object.
Example ¶
ExampleOpt shows the optional value type. A present value holds data; an absent value holds nothing. This replaces the use of NaN for missing data.
package main
import (
"fmt"
firewx "alpineworks.io/firewx"
)
func main() {
present := firewx.Some(firewx.Celsius(21.5))
absent := firewx.None[firewx.Celsius]()
if v, ok := present.Get(); ok {
fmt.Printf("present: %.1f\n", v)
}
fmt.Printf("absent is valid: %t\n", absent.Valid())
}
Output: present: 21.5 absent is valid: false
func MapOpt ¶
MapOpt applies fn to the value of o if present. It is a free function rather than a method because Go does not permit additional type parameters on methods.
func (Opt[T]) MarshalJSON ¶
MarshalJSON encodes a present value as itself and an absent value as null.
func (Opt[T]) Must ¶
func (o Opt[T]) Must() T
Must returns the value, panicking if it is absent. Intended for tests and for call sites that have already checked Valid.
func (*Opt[T]) UnmarshalJSON ¶
UnmarshalJSON decodes null as an absent value and any other JSON as a present value.
type Percent ¶
type Percent float64
Percent is a relative humidity or a fuel moisture content, expressed as a percentage. Fuel moisture content routinely exceeds 100% and may reach several hundred percent for live fuels, so this type is deliberately not range-limited.
func RelativeHumidity ¶
RelativeHumidity returns the relative humidity implied by a temperature and dew point. Some data sources report dew point rather than RH.
type Roughness ¶
type Roughness float64
Roughness is an aerodynamic roughness length in metres, characterising how much the surrounding terrain and vegetation slow the wind near the ground.
const ( RoughnessWater Roughness = 0.0002 RoughnessMownGrass Roughness = 0.01 RoughnessOpenGrass Roughness = 0.03 // the reference exposure both systems assume RoughnessFarmland Roughness = 0.10 RoughnessScattered Roughness = 0.25 RoughnessSuburban Roughness = 0.50 RoughnessForest Roughness = 1.00 RoughnessCityCentre Roughness = 2.00 )
Representative roughness lengths. These are order-of-magnitude guides, not measurements; if the corrected wind matters, calibrate against a nearby RAWS over a period of similar synoptic conditions rather than trusting a constant.
type Station ¶
type Station struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
Elevation Meters `json:"elevation"`
// LSTOffset is the fixed offset from UTC that defines local standard time
// at this station.
//
// This is deliberately a fixed offset and not a *time.Location. Both the
// Canadian FWI system and NFDRS define their observation windows in local
// standard time, which does not shift with daylight saving. Using a
// zoneinfo location here introduces a one hour discontinuity in the
// carryover codes twice a year that is nearly invisible in the output and
// extremely annoying to find.
LSTOffset time.Duration `json:"lst_offset"`
// AnemometerHeight is the height of the wind sensor above ground.
//
// The Canadian FWI system assumes 10 m open exposure; NFDRS assumes 20 ft
// (6.096 m). A backyard installation is usually lower than both and more
// sheltered than either, and the Initial Spread Index is sensitive enough
// to wind that ignoring this materially changes the output.
AnemometerHeight Meters `json:"anemometer_height"`
// Roughness is the aerodynamic roughness length of the surrounding
// terrain, used to correct wind speed between heights.
Roughness Roughness `json:"roughness"`
}
Station describes the site an observation came from, including the terms needed to correct a non-standard sensor exposure toward the reference exposure each fire danger model assumes.
type WattsPerSquareMeter ¶
type WattsPerSquareMeter float64
WattsPerSquareMeter is an irradiance. NFDRS2016's Nelson dead fuel moisture model requires measured solar radiation; without it the dominant term in the fuel particle energy balance has to be estimated from cloud cover.