loone-data-prep 1.2.3__tar.gz → 1.2.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/PKG-INFO +1 -1
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/forecast_scripts/create_forecast_LOWs.py +73 -30
- loone_data_prep-1.2.4/loone_data_prep/forecast_scripts/weather_forecast.py +199 -0
- loone_data_prep-1.2.4/loone_data_prep/herbie_utils.py +29 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep.egg-info/PKG-INFO +1 -1
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep.egg-info/SOURCES.txt +1 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/pyproject.toml +1 -1
- loone_data_prep-1.2.3/loone_data_prep/forecast_scripts/weather_forecast.py +0 -155
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/LICENSE +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/README.md +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/GEOGLOWS_LOONE_DATA_PREP.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/LOONE_DATA_PREP.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/__init__.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/data_analyses_fns.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/flow_data/S65E_total.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/flow_data/__init__.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/flow_data/forecast_bias_correction.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/flow_data/get_forecast_flows.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/flow_data/get_inflows.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/flow_data/get_outflows.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/flow_data/hydro.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/forecast_scripts/Chla_merged.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/forecast_scripts/forecast_stages.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/forecast_scripts/get_Chla_predicted.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/forecast_scripts/get_NO_Loads_predicted.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/forecast_scripts/loone_q_predict.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/forecast_scripts/loone_wq_predict.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/forecast_scripts/predict_PI.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/forecast_scripts/trib_cond.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/utils.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/water_level_data/__init__.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/water_level_data/get_all.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/water_level_data/hydro.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/water_quality_data/__init__.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/water_quality_data/get_inflows.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/water_quality_data/get_lake_wq.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/water_quality_data/wq.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/weather_data/__init__.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/weather_data/get_all.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/weather_data/weather.py +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep.egg-info/dependency_links.txt +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep.egg-info/requires.txt +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep.egg-info/top_level.txt +0 -0
- {loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: loone_data_prep
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.4
|
|
4
4
|
Summary: Prepare data to run the LOONE model.
|
|
5
5
|
Author-email: Osama Tarabih <osamatarabih@usf.edu>
|
|
6
6
|
Maintainer-email: Michael Souffront <msouffront@aquaveo.com>, James Dolinar <jdolinar@aquaveo.com>
|
|
@@ -2,8 +2,11 @@ import os
|
|
|
2
2
|
from herbie import FastHerbie
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
import pandas as pd
|
|
5
|
-
from retry_requests import retry
|
|
5
|
+
from retry_requests import retry as retry_requests
|
|
6
|
+
from retry import retry
|
|
6
7
|
import warnings
|
|
8
|
+
from typing import Tuple
|
|
9
|
+
from loone_data_prep.herbie_utils import get_fast_herbie_object
|
|
7
10
|
|
|
8
11
|
|
|
9
12
|
def generate_wind_forecasts(output_dir):
|
|
@@ -26,7 +29,8 @@ def generate_wind_forecasts(output_dir):
|
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
today_str = datetime.today().strftime('%Y-%m-%d 00:00')
|
|
29
|
-
FH =
|
|
32
|
+
FH = get_fast_herbie_object(today_str)
|
|
33
|
+
print("FastHerbie initialized.")
|
|
30
34
|
dfs = []
|
|
31
35
|
|
|
32
36
|
variables = {
|
|
@@ -45,37 +49,20 @@ def generate_wind_forecasts(output_dir):
|
|
|
45
49
|
"latitude": [point.latitude]
|
|
46
50
|
})
|
|
47
51
|
|
|
52
|
+
# Loop through variables for current point and extract data
|
|
48
53
|
for var_key, var_name in variables.items():
|
|
54
|
+
# Get the current variable data at the current point
|
|
49
55
|
print(f" Variable: {var_key}")
|
|
56
|
+
try:
|
|
57
|
+
df, var_name_actual = _download_herbie_variable(FH, var_key, var_name, point_df)
|
|
58
|
+
except Exception as e:
|
|
59
|
+
print(f"Error processing {var_key} for Point {index + 1} ({point.latitude}, {point.longitude}): {e}")
|
|
60
|
+
print(f'Skipping {var_key}')
|
|
61
|
+
continue
|
|
50
62
|
|
|
51
|
-
#
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
# Extract point data
|
|
56
|
-
dsi = ds.herbie.pick_points(point_df, method="nearest")
|
|
57
|
-
|
|
58
|
-
# Get actual variable name
|
|
59
|
-
if var_name == "10u":
|
|
60
|
-
var_name_actual = "u10" # Map 10u to u10
|
|
61
|
-
elif var_name == "10v":
|
|
62
|
-
var_name_actual = "v10" # Map 10v to v10
|
|
63
|
-
elif var_name == "2t":
|
|
64
|
-
var_name_actual = "t2m" #TODO: check that this is correct
|
|
65
|
-
|
|
66
|
-
# Convert to DataFrame
|
|
67
|
-
time_series = dsi[var_name_actual].squeeze()
|
|
68
|
-
df = time_series.to_dataframe().reset_index()
|
|
69
|
-
|
|
70
|
-
# Handle datetime columns
|
|
71
|
-
if "valid_time" in df.columns:
|
|
72
|
-
df = df.rename(columns={"valid_time": "datetime"})
|
|
73
|
-
elif "step" in df.columns and "time" in dsi.coords:
|
|
74
|
-
df["datetime"] = dsi.time.values[0] + df["step"]
|
|
75
|
-
|
|
76
|
-
# Retain necessary columns
|
|
77
|
-
df = df[["datetime", var_name_actual]].drop_duplicates()
|
|
78
|
-
dfs.append((index, var_name_actual, df))
|
|
63
|
+
# Append the DataFrame and variable name to the list
|
|
64
|
+
if not df.empty:
|
|
65
|
+
dfs.append((index, var_name_actual, df))
|
|
79
66
|
|
|
80
67
|
# Merge and process data per point
|
|
81
68
|
results = {}
|
|
@@ -125,3 +112,59 @@ def generate_wind_forecasts(output_dir):
|
|
|
125
112
|
filepath = os.path.join(output_dir, airt_file_map[key])
|
|
126
113
|
df_airt.to_csv(filepath, index=False)
|
|
127
114
|
|
|
115
|
+
|
|
116
|
+
@retry(Exception, tries=5, delay=15, max_delay=60, backoff=2)
|
|
117
|
+
def _download_herbie_variable(fast_herbie_object: FastHerbie, variable_key: str, variable_name: str, point_df: pd.DataFrame) -> Tuple[pd.DataFrame, str]:
|
|
118
|
+
"""
|
|
119
|
+
Download a specific variable from the Herbie API.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
fast_herbie_object: An instance of the FastHerbie class.
|
|
123
|
+
variable_key: The key of the variable to download.
|
|
124
|
+
variable_name: The name of the variable to download.
|
|
125
|
+
point_df: A DataFrame containing the point of interest (longitude and latitude).
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
A DataFrame containing the downloaded variable data.
|
|
129
|
+
|
|
130
|
+
Example:
|
|
131
|
+
point_df = pd.DataFrame({"longitude": [-80.7934], "latitude": [27.1389]})
|
|
132
|
+
df, var_name_actual = _download_herbie_variable(FastHerbie('2020-05-16 00:00', model='ifs', fxx=range(0, 360, 3)), '10u', '10u', point_df)
|
|
133
|
+
"""
|
|
134
|
+
# Download and load dataset
|
|
135
|
+
fast_herbie_object.download(f":{variable_key}")
|
|
136
|
+
ds = fast_herbie_object.xarray(f":{variable_key}", backend_kwargs={"decode_timedelta": True})
|
|
137
|
+
|
|
138
|
+
# Extract point data
|
|
139
|
+
dsi = ds.herbie.pick_points(point_df, method="nearest")
|
|
140
|
+
|
|
141
|
+
# Close and delete the original dataset to free up resources
|
|
142
|
+
ds.close()
|
|
143
|
+
del ds
|
|
144
|
+
|
|
145
|
+
# Get actual variable name
|
|
146
|
+
if variable_name == "10u":
|
|
147
|
+
var_name_actual = "u10" # Map 10u to u10
|
|
148
|
+
elif variable_name == "10v":
|
|
149
|
+
var_name_actual = "v10" # Map 10v to v10
|
|
150
|
+
elif variable_name == "2t":
|
|
151
|
+
var_name_actual = "t2m" #TODO: check that this is correct
|
|
152
|
+
|
|
153
|
+
# Convert to DataFrame
|
|
154
|
+
time_series = dsi[var_name_actual].squeeze()
|
|
155
|
+
df = time_series.to_dataframe().reset_index()
|
|
156
|
+
|
|
157
|
+
# Handle datetime columns
|
|
158
|
+
if "valid_time" in df.columns:
|
|
159
|
+
df = df.rename(columns={"valid_time": "datetime"})
|
|
160
|
+
elif "step" in df.columns and "time" in dsi.coords:
|
|
161
|
+
df["datetime"] = dsi.time.values[0] + df["step"]
|
|
162
|
+
|
|
163
|
+
# Close and delete the intermediate dataset to free memory
|
|
164
|
+
dsi.close()
|
|
165
|
+
del dsi, time_series
|
|
166
|
+
|
|
167
|
+
# Retain necessary columns
|
|
168
|
+
df = df[["datetime", var_name_actual]].drop_duplicates()
|
|
169
|
+
|
|
170
|
+
return df, var_name_actual
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
from herbie import FastHerbie
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import openmeteo_requests
|
|
5
|
+
import argparse
|
|
6
|
+
import requests_cache
|
|
7
|
+
from retry_requests import retry as retry_requests
|
|
8
|
+
from retry import retry
|
|
9
|
+
import warnings
|
|
10
|
+
from loone_data_prep.herbie_utils import get_fast_herbie_object
|
|
11
|
+
|
|
12
|
+
warnings.filterwarnings("ignore", message="Will not remove GRIB file because it previously existed.")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def download_weather_forecast(file_path):
|
|
16
|
+
# Get today's date in the required format
|
|
17
|
+
today_str = datetime.today().strftime('%Y-%m-%d 00:00')
|
|
18
|
+
|
|
19
|
+
# Define variables to download and extract
|
|
20
|
+
variables = {
|
|
21
|
+
"10u": "10u",
|
|
22
|
+
"ssrd": "ssrd",
|
|
23
|
+
"tp": "tp",
|
|
24
|
+
"10v": "10v",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Initialize FastHerbie
|
|
28
|
+
FH = get_fast_herbie_object(today_str)
|
|
29
|
+
print("FastHerbie initialized.")
|
|
30
|
+
|
|
31
|
+
dfs = []
|
|
32
|
+
|
|
33
|
+
for var_key, var_name in variables.items():
|
|
34
|
+
# Download the current variable
|
|
35
|
+
print(f"Processing {var_key}...")
|
|
36
|
+
try:
|
|
37
|
+
df = _download_herbie_variable(FH, var_key, var_name)
|
|
38
|
+
except Exception as e:
|
|
39
|
+
print(f"Error processing {var_key}: {e}")
|
|
40
|
+
print(f'Skipping {var_key}')
|
|
41
|
+
continue
|
|
42
|
+
|
|
43
|
+
# Append to list
|
|
44
|
+
if not df.empty:
|
|
45
|
+
dfs.append(df)
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
# Merge all variables into a single DataFrame
|
|
49
|
+
final_df = dfs[0]
|
|
50
|
+
for df in dfs[1:]:
|
|
51
|
+
final_df = final_df.merge(df, on="datetime", how="outer")
|
|
52
|
+
print(final_df)
|
|
53
|
+
# Calculate wind speed
|
|
54
|
+
final_df["wind_speed"] = (final_df["u10"] ** 2 + final_df["v10"] ** 2) ** 0.5
|
|
55
|
+
|
|
56
|
+
#rainfall corrected: OLS Regression Equation: Corrected Forecast = 0.7247 * Forecast + 0.1853
|
|
57
|
+
final_df["tp_corrected"] = 0.7247 * final_df["tp"] + 0.1853
|
|
58
|
+
|
|
59
|
+
#wind speed correction: Corrected Forecast = 0.4167 * Forecast + 4.1868
|
|
60
|
+
final_df["wind_speed_corrected"] = 0.4167 * final_df["wind_speed"] + 4.1868
|
|
61
|
+
|
|
62
|
+
#radiation correction will need to be fixed because it was done on fdir instead of ssdr
|
|
63
|
+
#radiation corrected: Corrected Forecast = 0.0553 * Forecast - 0.0081
|
|
64
|
+
final_df["ssrd_corrected"] = 0.0553 * final_df["ssrd"] - 0.0081
|
|
65
|
+
except Exception as e:
|
|
66
|
+
print(f'Error correcting herbie weather data: {e}')
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
# Setup the Open-Meteo API client with cache and retry on error
|
|
70
|
+
cache_session = requests_cache.CachedSession('.cache', expire_after = 3600)
|
|
71
|
+
retry_session = retry_requests(cache_session, retries = 5, backoff_factor = 0.2)
|
|
72
|
+
openmeteo = openmeteo_requests.Client(session = retry_session)
|
|
73
|
+
|
|
74
|
+
# Make sure all required weather variables are listed here
|
|
75
|
+
# The order of variables in hourly or daily is important to assign them correctly below
|
|
76
|
+
url = "https://api.open-meteo.com/v1/forecast"
|
|
77
|
+
params = {
|
|
78
|
+
"latitude": 26.9690,
|
|
79
|
+
"longitude": -80.7976,
|
|
80
|
+
"hourly": "evapotranspiration",
|
|
81
|
+
"forecast_days": 16,
|
|
82
|
+
"models": "gfs_seamless"
|
|
83
|
+
}
|
|
84
|
+
responses = openmeteo.weather_api(url, params=params)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# Process first location. Add a for-loop for multiple locations or weather models
|
|
88
|
+
response = responses[0]
|
|
89
|
+
|
|
90
|
+
hourly = response.Hourly()
|
|
91
|
+
hourly_evapotranspiration = hourly.Variables(0).ValuesAsNumpy()
|
|
92
|
+
|
|
93
|
+
hourly_data = {"date": pd.date_range(
|
|
94
|
+
start = pd.to_datetime(hourly.Time(), unit = "s", utc = True),
|
|
95
|
+
end = pd.to_datetime(hourly.TimeEnd(), unit = "s", utc = True),
|
|
96
|
+
freq = pd.Timedelta(seconds = hourly.Interval()),
|
|
97
|
+
inclusive = "left"
|
|
98
|
+
)}
|
|
99
|
+
|
|
100
|
+
hourly_data["evapotranspiration"] = hourly_evapotranspiration
|
|
101
|
+
|
|
102
|
+
hourly_dataframe = pd.DataFrame(data = hourly_data)
|
|
103
|
+
|
|
104
|
+
# Convert datetime to date for merging
|
|
105
|
+
final_df['date'] = final_df['datetime']
|
|
106
|
+
# Ensure final_df['date'] is timezone-aware (convert to UTC)
|
|
107
|
+
final_df['date'] = pd.to_datetime(final_df['date'], utc=True)
|
|
108
|
+
|
|
109
|
+
# Ensure hourly_dataframe['date'] is also timezone-aware (convert to UTC)
|
|
110
|
+
hourly_dataframe['date'] = pd.to_datetime(hourly_dataframe['date'], utc=True)
|
|
111
|
+
|
|
112
|
+
# Merge while keeping only matching dates from final_df
|
|
113
|
+
merged_df = final_df.merge(hourly_dataframe, on='date', how='left')
|
|
114
|
+
|
|
115
|
+
# Print final combined DataFrame
|
|
116
|
+
merged_df.drop(columns=['date'], inplace=True)
|
|
117
|
+
# print(merged_df)
|
|
118
|
+
|
|
119
|
+
merged_df.to_csv(file_path, index=False)
|
|
120
|
+
except Exception as e:
|
|
121
|
+
print(f'Error retrieving openmeteo weather data: {e}')
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@retry(Exception, tries=5, delay=15, max_delay=60, backoff=2)
|
|
125
|
+
def _download_herbie_variable(fast_herbie_object: FastHerbie, variable_key: str, variable_name: str) -> pd.DataFrame:
|
|
126
|
+
"""
|
|
127
|
+
Download a specific variable from the Herbie API.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
fast_herbie_object: An instance of the FastHerbie class.
|
|
131
|
+
variable_key: The key of the variable to download.
|
|
132
|
+
variable_name: The name of the variable to download.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
A DataFrame containing the downloaded variable data.
|
|
136
|
+
|
|
137
|
+
Example:
|
|
138
|
+
df = _download_herbie_variable(FastHerbie('2020-05-16 00:00', model='ifs', fxx=range(0, 360, 3)), '10u', '10u')
|
|
139
|
+
"""
|
|
140
|
+
# Define point of interest
|
|
141
|
+
points = pd.DataFrame({"longitude": [-80.7976], "latitude": [26.9690]})
|
|
142
|
+
|
|
143
|
+
# Download and load the dataset
|
|
144
|
+
fast_herbie_object.download(f":{variable_key}")
|
|
145
|
+
ds = fast_herbie_object.xarray(f":{variable_key}", backend_kwargs={"decode_timedelta": True})
|
|
146
|
+
|
|
147
|
+
# Extract point data
|
|
148
|
+
dsi = ds.herbie.pick_points(points, method="nearest")
|
|
149
|
+
|
|
150
|
+
# Close and delete the original dataset to free up resources
|
|
151
|
+
ds.close()
|
|
152
|
+
del ds
|
|
153
|
+
|
|
154
|
+
# Extract the correct variable name dynamically
|
|
155
|
+
if variable_name == "10u":
|
|
156
|
+
var_name_actual = "u10" # Map 10u to u10
|
|
157
|
+
elif variable_name == "10v":
|
|
158
|
+
var_name_actual = "v10" # Map 10v to v10
|
|
159
|
+
else:
|
|
160
|
+
var_name_actual = variable_name # For ssrd and tp, use the same name
|
|
161
|
+
|
|
162
|
+
# Extract time series
|
|
163
|
+
time_series = dsi[var_name_actual].squeeze()
|
|
164
|
+
|
|
165
|
+
# Convert to DataFrame
|
|
166
|
+
df = time_series.to_dataframe().reset_index()
|
|
167
|
+
|
|
168
|
+
# Convert `valid_time` to datetime
|
|
169
|
+
if "valid_time" in df.columns:
|
|
170
|
+
df = df.rename(columns={"valid_time": "datetime"})
|
|
171
|
+
elif "step" in df.columns and "time" in dsi.coords:
|
|
172
|
+
df["datetime"] = dsi.time.values[0] + df["step"]
|
|
173
|
+
|
|
174
|
+
# Keep only datetime and variable of interest
|
|
175
|
+
df = df[["datetime", var_name_actual]].drop_duplicates()
|
|
176
|
+
|
|
177
|
+
# Print extracted data
|
|
178
|
+
# print(df)
|
|
179
|
+
|
|
180
|
+
# Clean up intermediate datasets to free memory
|
|
181
|
+
del dsi, time_series
|
|
182
|
+
|
|
183
|
+
return df
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def main():
|
|
187
|
+
# Set up command-line argument parsing
|
|
188
|
+
parser = argparse.ArgumentParser(description="Download and process weather forecast data.")
|
|
189
|
+
parser.add_argument("file_path", help="Path to save the resulting CSV file.")
|
|
190
|
+
|
|
191
|
+
# Parse the arguments
|
|
192
|
+
args = parser.parse_args()
|
|
193
|
+
|
|
194
|
+
# Call the function with the provided file path
|
|
195
|
+
download_weather_forecast(args.file_path)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
if __name__ == "__main__":
|
|
199
|
+
main()
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from retry import retry
|
|
2
|
+
from herbie import FastHerbie
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class NoGribFilesFoundError(Exception):
|
|
6
|
+
"""Raised when no GRIB files are found for the specified date/model run."""
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@retry(NoGribFilesFoundError, tries=5, delay=15, max_delay=60, backoff=2)
|
|
11
|
+
def get_fast_herbie_object(date: str) -> FastHerbie:
|
|
12
|
+
"""
|
|
13
|
+
Get a FastHerbie object for the specified date. Raises an exception when no GRIB files are found.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
date: pandas-parsable datetime string
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
A FastHerbie object configured for the specified date.
|
|
20
|
+
|
|
21
|
+
Raises:
|
|
22
|
+
NoGribFilesFoundError: If no GRIB files are found for the specified date.
|
|
23
|
+
"""
|
|
24
|
+
fast_herbie = FastHerbie([date], model="ifs", fxx=range(0, 360, 3))
|
|
25
|
+
|
|
26
|
+
if len(fast_herbie.file_exists) == 0:
|
|
27
|
+
raise NoGribFilesFoundError(f"No GRIB files found for the specified date {date}.")
|
|
28
|
+
|
|
29
|
+
return fast_herbie
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: loone_data_prep
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.4
|
|
4
4
|
Summary: Prepare data to run the LOONE model.
|
|
5
5
|
Author-email: Osama Tarabih <osamatarabih@usf.edu>
|
|
6
6
|
Maintainer-email: Michael Souffront <msouffront@aquaveo.com>, James Dolinar <jdolinar@aquaveo.com>
|
|
@@ -5,6 +5,7 @@ loone_data_prep/GEOGLOWS_LOONE_DATA_PREP.py
|
|
|
5
5
|
loone_data_prep/LOONE_DATA_PREP.py
|
|
6
6
|
loone_data_prep/__init__.py
|
|
7
7
|
loone_data_prep/data_analyses_fns.py
|
|
8
|
+
loone_data_prep/herbie_utils.py
|
|
8
9
|
loone_data_prep/utils.py
|
|
9
10
|
loone_data_prep.egg-info/PKG-INFO
|
|
10
11
|
loone_data_prep.egg-info/SOURCES.txt
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
from herbie import FastHerbie
|
|
2
|
-
from datetime import datetime
|
|
3
|
-
import pandas as pd
|
|
4
|
-
import openmeteo_requests
|
|
5
|
-
import argparse
|
|
6
|
-
import requests_cache
|
|
7
|
-
from retry_requests import retry
|
|
8
|
-
import warnings
|
|
9
|
-
|
|
10
|
-
warnings.filterwarnings("ignore", message="Will not remove GRIB file because it previously existed.")
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def download_weather_forecast (file_path):
|
|
14
|
-
# Get today's date in the required format
|
|
15
|
-
today_str = datetime.today().strftime('%Y-%m-%d 00:00')
|
|
16
|
-
|
|
17
|
-
# Define variables to download and extract
|
|
18
|
-
variables = {
|
|
19
|
-
"10u": "10u",
|
|
20
|
-
"ssrd": "ssrd",
|
|
21
|
-
"tp": "tp",
|
|
22
|
-
"10v": "10v",
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
# Define point of interest
|
|
26
|
-
points = pd.DataFrame({"longitude": [-80.7976], "latitude": [26.9690]})
|
|
27
|
-
|
|
28
|
-
# Initialize FastHerbie
|
|
29
|
-
FH = FastHerbie([today_str], model="ifs", fxx=range(0, 360, 3))
|
|
30
|
-
dfs = []
|
|
31
|
-
|
|
32
|
-
for var_key, var_name in variables.items():
|
|
33
|
-
print(f"Processing {var_key}...")
|
|
34
|
-
|
|
35
|
-
# Download and load the dataset
|
|
36
|
-
FH.download(f":{var_key}")
|
|
37
|
-
ds = FH.xarray(f":{var_key}", backend_kwargs={"decode_timedelta": True})
|
|
38
|
-
|
|
39
|
-
# Extract point data
|
|
40
|
-
dsi = ds.herbie.pick_points(points, method="nearest")
|
|
41
|
-
|
|
42
|
-
# Extract the correct variable name dynamically
|
|
43
|
-
if var_name == "10u":
|
|
44
|
-
var_name_actual = "u10" # Map 10u to u10
|
|
45
|
-
elif var_name == "10v":
|
|
46
|
-
var_name_actual = "v10" # Map 10v to v10
|
|
47
|
-
else:
|
|
48
|
-
var_name_actual = var_name # For ssrd and tp, use the same name
|
|
49
|
-
|
|
50
|
-
# Extract time series
|
|
51
|
-
time_series = dsi[var_name_actual].squeeze()
|
|
52
|
-
|
|
53
|
-
# Convert to DataFrame
|
|
54
|
-
df = time_series.to_dataframe().reset_index()
|
|
55
|
-
|
|
56
|
-
# Convert `valid_time` to datetime
|
|
57
|
-
if "valid_time" in df.columns:
|
|
58
|
-
df = df.rename(columns={"valid_time": "datetime"})
|
|
59
|
-
elif "step" in df.columns and "time" in dsi.coords:
|
|
60
|
-
df["datetime"] = dsi.time.values[0] + df["step"]
|
|
61
|
-
|
|
62
|
-
# Keep only datetime and variable of interest
|
|
63
|
-
df = df[["datetime", var_name_actual]].drop_duplicates()
|
|
64
|
-
|
|
65
|
-
# Append to list
|
|
66
|
-
dfs.append(df)
|
|
67
|
-
|
|
68
|
-
# Print extracted data
|
|
69
|
-
# print(df)
|
|
70
|
-
|
|
71
|
-
# Merge all variables into a single DataFrame
|
|
72
|
-
final_df = dfs[0]
|
|
73
|
-
for df in dfs[1:]:
|
|
74
|
-
final_df = final_df.merge(df, on="datetime", how="outer")
|
|
75
|
-
print(final_df)
|
|
76
|
-
# Calculate wind speed
|
|
77
|
-
final_df["wind_speed"] = (final_df["u10"] ** 2 + final_df["v10"] ** 2) ** 0.5
|
|
78
|
-
|
|
79
|
-
#rainfall corrected: OLS Regression Equation: Corrected Forecast = 0.7247 * Forecast + 0.1853
|
|
80
|
-
final_df["tp_corrected"] = 0.7247 * final_df["tp"] + 0.1853
|
|
81
|
-
|
|
82
|
-
#wind speed correction: Corrected Forecast = 0.4167 * Forecast + 4.1868
|
|
83
|
-
final_df["wind_speed_corrected"] = 0.4167 * final_df["wind_speed"] + 4.1868
|
|
84
|
-
|
|
85
|
-
#radiation correction will need to be fixed because it was done on fdir instead of ssdr
|
|
86
|
-
#radiation corrected: Corrected Forecast = 0.0553 * Forecast - 0.0081
|
|
87
|
-
final_df["ssrd_corrected"] = 0.0553 * final_df["ssrd"] - 0.0081
|
|
88
|
-
|
|
89
|
-
# Setup the Open-Meteo API client with cache and retry on error
|
|
90
|
-
cache_session = requests_cache.CachedSession('.cache', expire_after = 3600)
|
|
91
|
-
retry_session = retry(cache_session, retries = 5, backoff_factor = 0.2)
|
|
92
|
-
openmeteo = openmeteo_requests.Client(session = retry_session)
|
|
93
|
-
|
|
94
|
-
# Make sure all required weather variables are listed here
|
|
95
|
-
# The order of variables in hourly or daily is important to assign them correctly below
|
|
96
|
-
url = "https://api.open-meteo.com/v1/forecast"
|
|
97
|
-
params = {
|
|
98
|
-
"latitude": 26.9690,
|
|
99
|
-
"longitude": -80.7976,
|
|
100
|
-
"hourly": "evapotranspiration",
|
|
101
|
-
"forecast_days": 16,
|
|
102
|
-
"models": "gfs_seamless"
|
|
103
|
-
}
|
|
104
|
-
responses = openmeteo.weather_api(url, params=params)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
# Process first location. Add a for-loop for multiple locations or weather models
|
|
108
|
-
response = responses[0]
|
|
109
|
-
|
|
110
|
-
hourly = response.Hourly()
|
|
111
|
-
hourly_evapotranspiration = hourly.Variables(0).ValuesAsNumpy()
|
|
112
|
-
|
|
113
|
-
hourly_data = {"date": pd.date_range(
|
|
114
|
-
start = pd.to_datetime(hourly.Time(), unit = "s", utc = True),
|
|
115
|
-
end = pd.to_datetime(hourly.TimeEnd(), unit = "s", utc = True),
|
|
116
|
-
freq = pd.Timedelta(seconds = hourly.Interval()),
|
|
117
|
-
inclusive = "left"
|
|
118
|
-
)}
|
|
119
|
-
|
|
120
|
-
hourly_data["evapotranspiration"] = hourly_evapotranspiration
|
|
121
|
-
|
|
122
|
-
hourly_dataframe = pd.DataFrame(data = hourly_data)
|
|
123
|
-
|
|
124
|
-
# Convert datetime to date for merging
|
|
125
|
-
final_df['date'] = final_df['datetime']
|
|
126
|
-
# Ensure final_df['date'] is timezone-aware (convert to UTC)
|
|
127
|
-
final_df['date'] = pd.to_datetime(final_df['date'], utc=True)
|
|
128
|
-
|
|
129
|
-
# Ensure hourly_dataframe['date'] is also timezone-aware (convert to UTC)
|
|
130
|
-
hourly_dataframe['date'] = pd.to_datetime(hourly_dataframe['date'], utc=True)
|
|
131
|
-
|
|
132
|
-
# Merge while keeping only matching dates from final_df
|
|
133
|
-
merged_df = final_df.merge(hourly_dataframe, on='date', how='left')
|
|
134
|
-
|
|
135
|
-
# Print final combined DataFrame
|
|
136
|
-
merged_df.drop(columns=['date'], inplace=True)
|
|
137
|
-
# print(merged_df)
|
|
138
|
-
|
|
139
|
-
merged_df.to_csv(file_path, index=False)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def main():
|
|
143
|
-
# Set up command-line argument parsing
|
|
144
|
-
parser = argparse.ArgumentParser(description="Download and process weather forecast data.")
|
|
145
|
-
parser.add_argument("file_path", help="Path to save the resulting CSV file.")
|
|
146
|
-
|
|
147
|
-
# Parse the arguments
|
|
148
|
-
args = parser.parse_args()
|
|
149
|
-
|
|
150
|
-
# Call the function with the provided file path
|
|
151
|
-
download_weather_forecast(args.file_path)
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if __name__ == "__main__":
|
|
155
|
-
main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/flow_data/get_forecast_flows.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/forecast_scripts/Chla_merged.py
RENAMED
|
File without changes
|
{loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/forecast_scripts/forecast_stages.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/forecast_scripts/loone_q_predict.py
RENAMED
|
File without changes
|
{loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/forecast_scripts/loone_wq_predict.py
RENAMED
|
File without changes
|
{loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/forecast_scripts/predict_PI.py
RENAMED
|
File without changes
|
{loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/forecast_scripts/trib_cond.py
RENAMED
|
File without changes
|
|
File without changes
|
{loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/water_level_data/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/water_quality_data/__init__.py
RENAMED
|
File without changes
|
{loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/water_quality_data/get_inflows.py
RENAMED
|
File without changes
|
{loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep/water_quality_data/get_lake_wq.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{loone_data_prep-1.2.3 → loone_data_prep-1.2.4}/loone_data_prep.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|