epw_rs/weather_data.rs
1// https://designbuilder.co.uk/cahelp/Content/EnergyPlusWeatherFileFormat.htm
2
3use chrono::{DateTime, FixedOffset};
4
5/// Present Weather codes
6///
7/// The present weather struct based on TMY2 conventions. Note that the most important fields are
8/// those representing liquid precipitation - where the surfaces of the building would be wet.
9/// EnergyPlus uses “Snow Depth” to determine if snow is on the ground
10#[derive(Debug)]
11pub struct PresentWeather {
12 /// Occurrence of Thunderstorm, Tornado, or Squall
13 /// ### Definition:
14 /// - `0` = Thunderstorm-lightning and thunder. Wind gusts less than 25.7 m/s, and hail, if any, less than 1.9 cm diameter
15 /// - `1` = Heavy or severe thunderstorm-frequent intense lightning and thunder. Wind gusts greater than 25.7 m/s and hail, if any, 1.9 cm or greater diameter
16 /// - `2` = Report of tornado or waterspout
17 /// - `4` = Moderate squall-sudden increase of wind speed by at least 8.2 m/s, reaching 11.3 m/s or more and lasting for at least 1 minute
18 /// - `6` = Water spout (beginning January 1984)
19 /// - `7` = Funnel cloud (beginning January 1984)
20 /// - `8` = Tornado (beginning January 1984)
21 /// - `9` = None if Observation Indicator element equals `0`, or else unknown or missing if Observation Indicator element equals `9`
22 pub thunderstorm: u8,
23
24 /// Occurrence of Rain, Rain Showers, or Freezing Rain
25 /// ### Definition:
26 /// - `0` = Light rain
27 /// - `1` = Moderate rain
28 /// - `2` = Heavy rain
29 /// - `3` = Light rain showers
30 /// - `4` = Moderate rain showers
31 /// - `5` = Heavy rain showers
32 /// - `6` = Light freezing rain
33 /// - `7` = Moderate freezing rain
34 /// - `8` = Heavy freezing rain
35 /// - `9` = None if Observation Indicator element equals `0`, or else unknown or missing if Observation Indicator element equals `9`
36 /// ### Notes:
37 /// <dl>
38 /// <dt>Light</dt>
39 /// <dd>up to 0.25 cm per hour</dd>
40 /// <dt>Heavy</dt>
41 /// <dd>greater than 0.76cm per hour</dd>
42 /// </dl>
43 pub rain: u8,
44
45 /// Occurrence of Rain Squalls, Drizzle, or Freezing Drizzle
46 /// ### Definition:
47 /// - `0` = Light rain squalls
48 /// - `1` = Moderate rain squalls
49 /// - `3` = Light drizzle
50 /// - `4` = Moderate drizzle
51 /// - `5` = Heavy drizzle
52 /// - `6` = Light freezing drizzle
53 /// - `7` = Moderate freezing drizzle
54 /// - `8` = Heavy freezing drizzle
55 /// - `9` = None if Observation Indicator element equals `0`, or else unknown or missing if Observation Indicator element equals `9`
56 /// ### Notes:
57 /// #### When drizzle or freezing drizzle occurs with other weather phenomena:
58 /// <dl>
59 /// <dt>Light</dt>
60 /// <dd>up to 0.025 cm per hour</dd>
61 /// <dt>Moderate</dt>
62 /// <dd>0.025 to 0.051cm per hour</dd>
63 /// <dt>Heavy</dt>
64 /// <dd>greater than 0.051 cm per hour</dd>
65 /// </dl>
66 ///
67 /// #### When drizzle or freezing drizzle occurs alone:
68 /// <dl>
69 /// <dt>Light</dt>
70 /// <dd> visibility 1 km or greater</dd>
71 /// <dt>Moderate</dt>
72 /// <dd>visibility between 0.5 and 1 km</dd>
73 /// <dt>Heavy</dt>
74 /// <dd>visibility 0.5 km or less</dd>
75 /// </dl>
76 pub rain_squalls: u8,
77
78 /// Occurrence of Snow, Snow Pellets, or Ice Crystals
79 /// ### Definition:
80 /// - `0` = Light snow
81 /// - `1` = Moderate snow
82 /// - `2` = Heavy snow
83 /// - `3` = Light snow pellets
84 /// - `4` = Moderate snow pellets
85 /// - `5` = Heavy snow pellets
86 /// - `6` = Light ice crystals
87 /// - `7` = Moderate ice crystals
88 /// - `8` = Heavy ice crystals
89 /// - `9` = None if Observation Indicator element equals `0`, or else unknown or missing if Observation Indicator element equals `9`
90 /// ### Notes:
91 /// Beginning in April 1963, any occurrence of ice crystals is recorded as a `7`.
92 pub snow: u8,
93
94 /// Occurrence of Snow Showers, Snow Squalls, or Snow Grains
95 /// ### Definition:
96 /// - `0` = Light snow
97 /// - `1` = Moderate snow showers
98 /// - `2` = Heavy snow showers
99 /// - `3` = Light snow squall
100 /// - `4` = Moderate snow squall
101 /// - `5` = Heavy snow squall
102 /// - `6` = Light snow grains
103 /// - `7` = Moderate snow grains
104 /// - `9` = None if Observation Indicator element equals `0`, or else unknown or missing if Observation Indicator element equals `9`
105 pub snow_showers: u8,
106
107 /// Occurrence of Sleet, Sleet Showers, or Hail
108 /// ### Definition:
109 /// - `0` = Light ice pellet showers
110 /// - `1` = Moderate ice pellet showers
111 /// - `2` = Heavy ice pellet showers
112 /// - `4` = Hail
113 /// - `9` = None if Observation Indicator element equals `0`, or else unknown or missing if Observation Indicator element equals `9`=
114 /// > Notes: Prior to April 1970, ice pellets were coded as sleet. Beginning in April 1970, sleet and small hail were redefined as ice pellets and are coded as `0`, `1`, or `2`.
115 pub sleet: u8,
116
117 /// Occurrence of Fog, Blowing Dust, or Blowing Sand
118 /// ### Definition:
119 /// - `0` = Fog
120 /// - `1` = Ice fog
121 /// - `2` = Ground fog
122 /// - `3` = Blowing dust
123 /// - `4` = Blowing sand
124 /// - `5` = Heavy fog
125 /// - `6` = Glaze (beginning 1984)
126 /// - `7` = Heavy ice fog (beginning 1984)
127 /// - `8` = Heavy ground fog (beginning 1984)
128 /// - `9` = None if Observation Indicator element equals `0`, or else unknown or missing if Observation Indicator element equals `9`
129 /// > Notes: These values recorded only when visibility is less than 11 km.
130 pub fog: u8,
131
132 /// Occurrence of Smoke, Haze, Smoke and Haze, Blowing Snow, Blowing Spray, or Dust
133 /// ### Definition:
134 /// - `0` = Smoke
135 /// - `1` = Haze
136 /// - `2` = Smoke and haze
137 /// - `3` = Dust
138 /// - `4` = Blowing snow
139 /// - `5` = Blowing spray
140 /// - `6` = Dust storm (beginning 1984)
141 /// - `7` = Volcanic ash
142 /// - `9` = None if Observation Indicator element equals `0`, or else unknown or missing if Observation Indicator element equals `9`
143 /// > Notes: These values recorded only when visibility is less than 11 km.
144 pub smoke: u8,
145
146 /// Occurrence of Ice Pellets
147 /// ### Definition:
148 /// - `0` = Light ice pellets
149 /// - `1` = Moderate ice pellets
150 /// - `2` = Heavy ice pellets
151 /// - `9` = None if Observation Indicator element equals `0`, or else unknown or missing if Observation Indicator element equals `9`
152 pub ice_pellets: u8,
153}
154
155/// # EPW weather data
156///
157/// The weather data from the file is provided in a column-oriented format for efficient analysis.
158/// This library uses the convention of inserting NaN when a value is not available, rather than
159/// using the in-band magic numbers (e.g. 999) to signify missing data.
160///
161#[derive(Debug)]
162pub struct WeatherData {
163 /// Timestamps for the weather data samples
164 pub timestamp: Vec<DateTime<FixedOffset>>,
165
166 /// Data Source and validity flags. The format is not documented
167 pub flags: Vec<String>,
168
169 /// Dry bulb temperature in °C
170 pub dry_bulb_temperature: Vec<f64>,
171
172 /// Dew point temperature in °C
173 pub dew_point_temperature: Vec<f64>,
174
175 /// Relative humidity in % [0-100]
176 pub relative_humidity: Vec<f64>,
177
178 /// Atmospheric pressure in Pascals
179 pub atmospheric_pressure: Vec<f64>,
180
181 /// Extraterrestrial Horizontal Radiation in Wh/m²
182 pub extraterrestrial_horizontal_radiation: Vec<f64>,
183
184 /// Extraterrestrial Direct Normal Radiation in Wh/m²
185 pub extraterrestrial_direct_normal_radiation: Vec<f64>,
186
187 /// Horizontal Infrared Radiation in Wh/m²
188 pub horizontal_infrared_radiation_intensity: Vec<f64>,
189
190 /// Glob al Horizontal Radiation in Wh/m²
191 pub global_horizontal_radiation: Vec<f64>,
192
193 /// Direct Normal Radiation in Wh/m²
194 pub direct_normal_radiation: Vec<f64>,
195
196 /// Diffuse Horizontal Radiation in Wh/m²
197 pub diffuse_horizontal_radiation: Vec<f64>,
198
199 /// Global Horizontal Illuminance in lux
200 pub global_horizontal_illuminance: Vec<f64>,
201
202 /// Direct Normal Illuminance in lux
203 pub direct_normal_illuminance: Vec<f64>,
204
205 /// Diffuse Horizontal Illuminance in lux
206 pub diffuse_horizontal_illuminance: Vec<f64>,
207
208 /// Zenith Luminance in Cd/m²
209 pub zenith_luminance: Vec<f64>,
210
211 /// Wind direction in degrees [0-360]
212 pub wind_direction: Vec<f64>,
213
214 /// Wind speed in m/s
215 pub wind_speed: Vec<f64>,
216
217 /// Total sky cover
218 pub total_sky_cover: Vec<f64>,
219
220 /// Opaque sky cover
221 pub opaque_sky_cover: Vec<f64>,
222
223 /// Visibility in km
224 pub visibility: Vec<f64>,
225
226 /// Ceiling height in m
227 pub ceiling_height: Vec<f64>,
228
229 /// Whether present weather should be taken from the following field
230 pub present_weather_observation: Vec<bool>,
231
232 /// Present weather
233 pub present_weather_codes: Vec<PresentWeather>,
234
235 /// Precipitable water in mm
236 pub precipitable_water: Vec<f64>,
237
238 /// Aerosol optical depth in thousandths
239 pub aerosol_optical_depth: Vec<f64>,
240
241 /// Snow depth in cm
242 pub snow_depth: Vec<f64>,
243
244 /// Days since last snowfall
245 pub days_since_last_snowfall: Vec<f64>,
246
247 /// Albedo
248 pub albedo: Vec<f64>,
249
250 /// Liquid precipitation depth in mm
251 pub liquid_precipitation_depth: Vec<f64>,
252
253 /// Liquid precipitation quantity in hours
254 pub liquid_precipitation_quantity: Vec<f64>,
255}
256
257#[cfg(feature = "polars")]
258pub mod polars {
259 use super::WeatherData;
260 use polars::prelude::*;
261
262 impl WeatherData {
263 pub fn to_dataframe(&self) -> Result<DataFrame, PolarsError> {
264 let millisecond_timestamps: Vec<i64> = self
265 .timestamp
266 .iter()
267 .map(|dt| dt.timestamp_millis())
268 .collect();
269 let timestamp = match Series::new("timestamp".into(), millisecond_timestamps)
270 .cast(&DataType::Datetime(TimeUnit::Milliseconds, None))
271 {
272 Ok(ts) => ts,
273 Err(e) => return Err(e),
274 };
275
276 let series_length = self.present_weather_codes.len();
277 let mut present_thunderstorm: Vec<u8> = Vec::with_capacity(series_length);
278 let mut present_rain: Vec<u8> = Vec::with_capacity(series_length);
279 let mut present_rain_squalls: Vec<u8> = Vec::with_capacity(series_length);
280 let mut present_snow: Vec<u8> = Vec::with_capacity(series_length);
281 let mut present_snow_showers: Vec<u8> = Vec::with_capacity(series_length);
282 let mut present_sleet: Vec<u8> = Vec::with_capacity(series_length);
283 let mut present_fog: Vec<u8> = Vec::with_capacity(series_length);
284 let mut present_smoke: Vec<u8> = Vec::with_capacity(series_length);
285 let mut present_ice_pellets: Vec<u8> = Vec::with_capacity(series_length);
286
287 for pw in &self.present_weather_codes {
288 present_thunderstorm.push(pw.thunderstorm);
289 present_rain.push(pw.rain);
290 present_rain_squalls.push(pw.rain_squalls);
291 present_snow.push(pw.snow);
292 present_snow_showers.push(pw.snow_showers);
293 present_sleet.push(pw.sleet);
294 present_fog.push(pw.fog);
295 present_smoke.push(pw.smoke);
296 present_ice_pellets.push(pw.ice_pellets);
297 }
298
299 match df!(
300 "timestamp" => timestamp,
301 "dry_bulb_temperature" => &self.dry_bulb_temperature,
302 "dew_point_temperature" => &self.dew_point_temperature,
303 "relative_humidity" => &self.relative_humidity,
304 "atmospheric_pressure" => &self.atmospheric_pressure,
305 "extraterrestrial_horizontal_radiation" => &self.extraterrestrial_horizontal_radiation,
306 "extraterrestrial_direct_normal_radiation" => &self.extraterrestrial_direct_normal_radiation,
307 "horizontal_infrared_radiation_intensity" => &self.horizontal_infrared_radiation_intensity,
308 "global_horizontal_radiation" => &self.global_horizontal_radiation,
309 "direct_normal_radiation" => &self.direct_normal_radiation,
310 "diffuse_horizontal_radiation" => &self.diffuse_horizontal_radiation,
311 "global_horizontal_illuminance" => &self.global_horizontal_illuminance,
312 "direct_normal_illuminance" => &self.direct_normal_illuminance,
313 "diffuse_horizontal_illuminance" => &self.diffuse_horizontal_illuminance,
314 "zenith_luminance" => &self.zenith_luminance,
315 "wind_direction" => &self.wind_direction,
316 "wind_speed" => &self.wind_speed,
317 "total_sky_cover" => &self.total_sky_cover,
318 "opaque_sky_cover" => &self.opaque_sky_cover,
319 "visibility" => &self.visibility,
320 "ceiling_height" => &self.ceiling_height,
321 "present_weather_observation" => &self.present_weather_observation,
322 "present_thunderstorm" => &present_thunderstorm,
323 "present_rain" => &present_rain,
324 "present_rain_squalls" => &present_rain_squalls,
325 "present_snow" => &present_snow,
326 "present_snow_showers" => &present_snow_showers,
327 "present_sleet" => &present_sleet,
328 "present_fog" => &present_fog,
329 "present_smoke" => &present_smoke,
330 "present_ice_pellets" => &present_ice_pellets,
331 "precipitable_water" => &self.precipitable_water,
332 "aerosol_optical_depth" => &self.aerosol_optical_depth,
333 "snow_depth" => &self.snow_depth,
334 "days_since_last_snowfall" => &self.days_since_last_snowfall,
335 "albedo" => &self.albedo,
336 "liquid_precipitation_depth" => &self.liquid_precipitation_depth,
337 "liquid_precipitation_quantity" => &self.liquid_precipitation_quantity,
338 ) {
339 Ok(df) => Ok(df),
340 Err(e) => Err(e),
341 }
342 }
343 }
344}