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}