1 ## Set of Functions to process and format fuel moisture model inputs
2 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 import numpy
as np
, random
5 from numpy
.random
import rand
6 from MesoPy
import Meso
8 import matplotlib
.pyplot
as plt
9 from moisture_models
import model_decay
, model_moisture
10 from datetime
import datetime
, timedelta
14 verbose
= False ## Must be declared in environment
17 for s
in args
[:(len(args
)-1)]:
21 def to_json(dic
,filename
):
22 print('writing ',filename
)
25 type_nparray
=type(np
.array([]))
27 if type(dic
[i
]) == type_nparray
:
28 new
[i
]=dic
[i
].tolist() # because numpy.ndarray is not serializable
31 # print('i',type(new[i]))
32 json
.dump(new
,open(filename
,'w'),indent
=4)
34 def from_json(filename
):
35 print('reading ',filename
)
36 dic
=json
.load(open(filename
,'r'))
39 if type(dic
[i
]) is list:
40 new
[i
]=np
.array(dic
[i
]) # because ndarray is not serializable
46 # Function to simulate moisture data and equilibrium for model testing
47 def create_synthetic_data(days
=20,power
=4,data_noise
=0.02,process_noise
=0.0,DeltaE
=0.0):
50 hour
= np
.array(range(hours
))
51 day
= np
.array(range(hours
))/24.
53 # artificial equilibrium data
54 E
= 100.0*np
.power(np
.sin(np
.pi
*day
),4) # diurnal curve
58 m_f
[0] = 0.1 # initial FMC
60 for t
in range(hours
-1):
61 m_f
[t
+1] = max(0.,model_decay(m_f
[t
],E
[t
]) + random
.gauss(0,process_noise
) )
62 data
= m_f
+ np
.random
.normal(loc
=0,scale
=data_noise
,size
=hours
)
64 return E
,m_f
,data
,hour
,h2
,DeltaE
66 # the following input or output dictionary with all model data and variables
68 def check_data_array(dat
,h
,a
,s
):
71 print("array %s %s length %i min %s max %s %s" % (a
,s
,len(ar
),min(ar
),max(ar
),type(ar
)))
74 print('Warning: len(%a) < %i' % (a
,ho
))
77 print(a
+ ' not present')
79 def check_data_scalar(dat
,a
):
81 print('%s = %s' % (a
,dat
[a
]),' ',type(dat
[a
]))
83 print(a
+ ' not present' )
85 def check_data(dat
,hours
=None):
86 check_data_scalar(dat
,'hours')
87 check_data_scalar(dat
,'h2')
91 print('specified hours=%s for check_data' % hours
)
92 check_data_array(dat
,hours
,'E','drying equilibrium (%)')
93 check_data_array(dat
,hours
,'Ed','drying equilibrium (%)')
94 check_data_array(dat
,hours
,'Ew','wetting equilibrium (%)')
95 check_data_array(dat
,hours
,'Ec','equilibrium equilibrium (%)')
96 check_data_array(dat
,hours
,'rain','rain intensity (mm/h)')
97 check_data_array(dat
,hours
,'fm','RAWS fuel moisture data (%)')
98 check_data_array(dat
,hours
,'m','fuel moisture estimate (%)')
99 check_data_array(dat
,hours
,'m_rnn','RNN fuel moisture estimate (%)')
100 check_data_array(dat
,hours
,'m_kf','KF fuel moisture estimate (%)')
103 def synthetic_data(days
=20,power
=4,data_noise
=0.02,process_noise
=0.0,
104 DeltaE
=0.0,Emin
=5,Emax
=30,p_rain
=0.01,max_rain
=10.0):
107 hour
= np
.array(range(hours
))
108 day
= np
.array(range(hours
))/24.
109 # artificial equilibrium data
110 E
= np
.power(np
.sin(np
.pi
*day
),power
) # diurnal curve betwen 0 and 1
111 E
= Emin
+(Emax
- Emin
)*E
114 Ew
=np
.maximum(E
-0.5,0)
115 rain
= np
.multiply(rand(hours
) < p_rain
, rand(hours
)*max_rain
)
117 m_f
= np
.zeros(hours
)
118 m_f
[0] = 0.1 # initial FMC
120 for t
in range(hours
-1):
121 m_f
[t
+1] = max(0.,model_moisture(m_f
[t
],Ed
[t
-1],Ew
[t
-1],rain
[t
-1]) + random
.gauss(0,process_noise
))
122 m_f
= m_f
+ np
.random
.normal(loc
=0,scale
=data_noise
,size
=hours
)
123 dat
= {'E':E
,'Ew':Ew
,'Ed':Ed
,'m_f':m_f
,'hours':hours
,'h2':h2
,'DeltaE':DeltaE
,'rain':rain
,'title':'Synthetic data'}
127 def plot_one(hmin
,hmax
,dat
,name
,linestyle
,c
,label
,type='plot'):
128 # helper for plot_data
135 hour
= np
.array(range(hmin
,hmax
))
137 plt
.plot(hour
,dat
[name
][hmin
:hmax
],linestyle
=linestyle
,c
=c
,label
=label
)
138 elif type=='scatter':
139 plt
.scatter(hour
,dat
[name
][hmin
:hmax
],linestyle
=linestyle
,c
=c
,label
=label
)
141 def plot_data(dat
,title
=None,title2
=None,hmin
=None,hmax
=None):
146 hmax
= max(hmax
, dat
['hours'])
147 plt
.figure(figsize
=(16,4))
148 plot_one(hmin
,hmax
,dat
,'E',linestyle
='--',c
='r',label
='equilibrium')
149 plot_one(hmin
,hmax
,dat
,'Ed',linestyle
='--',c
='r',label
='drying equilibrium')
150 plot_one(hmin
,hmax
,dat
,'Ew',linestyle
='--',c
='b',label
='wetting equilibrium')
151 plot_one(hmin
,hmax
,dat
,'m_f',linestyle
='-',c
='g',label
='FMC truth')
152 plot_one(hmin
,hmax
,dat
,'fm',linestyle
=':',c
='b',label
='FMC observation')
153 plot_one(hmin
,hmax
,dat
,'m',linestyle
='-',c
='k',label
='FMC estimate')
154 plot_one(hmin
,hmax
,dat
,'Ec',linestyle
='-',c
='g',label
='equilibrium correction')
155 plot_one(hmin
,hmax
,dat
,'rain',linestyle
='-',c
='b',label
='rain intensity')
156 if title
is not None:
160 # print('title',type(t),t)
161 if title2
is not None:
164 plt
.xlabel('Time (hours)')
166 plt
.ylabel('FMC (%) / Rain mm/h')
168 plt
.ylabel('Fuel moisture content (%)')
171 # Calculate mean squared error
173 return ((a
- b
)**2).mean()
175 # Calculate mean absolute error
177 return ((a
- b
).__abs
__()).mean()
184 print('Training MSE: ' + str(np
.round(mse(m
[:h2
], fm
[:h2
]), 4)))
185 print('Prediction MSE: ' + str(np
.round(mse(m
[h2
:hours
], fm
[h2
:hours
]), 4)))
187 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
189 ## RAWS Data Functions
191 def format_raws(stn
, fixnames
= True):
192 raws_dat
= stn
['OBSERVATIONS']
194 # Convert to Numpy arrays, check data type for floats
195 for key
in [*stn
['OBSERVATIONS'].keys()]:
196 if type(stn
['OBSERVATIONS'][key
][0]) is float:
197 raws_dat
[key
] = np
.array(stn
['OBSERVATIONS'][key
], dtype
= 'float64')
199 raws_dat
[key
] = np
.array(stn
['OBSERVATIONS'][key
])
202 raws_dat
['air_temp_set_1'] = raws_dat
['air_temp_set_1'] + 273.15 ## convert C to K
203 if 'precip_accum_set_1' in raws_dat
.keys():
204 raws_dat
['precip_accum_set_1'] = format_precip(raws_dat
['precip_accum_set_1']) ## format precip data, accumulated to hourly
207 # Calculate Equilibrium Temps
208 raws_dat
['Ed'] = 0.924*raws_dat
['relative_humidity_set_1']**0.679 + 0.000499*np
.exp(0.1*raws_dat
['relative_humidity_set_1']) + 0.18*(21.1 + 273.15 - raws_dat
['air_temp_set_1'])*(1 - np
.exp(-0.115*raws_dat
['relative_humidity_set_1']))
209 raws_dat
['Ew'] = 0.618*raws_dat
['relative_humidity_set_1']**0.753 + 0.000454*np
.exp(0.1*raws_dat
['relative_humidity_set_1']) + 0.18*(21.1 + 273.15 - raws_dat
['air_temp_set_1'])*(1 - np
.exp(-0.115*raws_dat
['relative_humidity_set_1']))
212 for key
in [*raws_dat
.keys()]:
213 if type(raws_dat
[key
][0]) is float:
214 raws_dat
[key
] = fixnan(raws_dat
[key
], 2)
219 'date_time': 'time', 'precip_accum': 'rain',
220 'fuel_moisture': 'fm', 'relative_humidity': 'rh',
221 'air_temp': 'temp', 'Ed': 'Ed', 'Ew': 'Ew'
223 old_keys
= [*raws_dat
.keys()]
224 old_keys
= [k
.replace("_set_1", "") for k
in old_keys
]
227 new_keys
.append(var_mapping
.get(key
, key
))
228 old_keys
= [*raws_dat
.keys()]
229 old_keys
= [k
.replace("_set_1", "") for k
in old_keys
]
232 new_keys
.append(var_mapping
.get(key
, key
))
233 raws_dat2
= dict(zip(new_keys
, list(raws_dat
.values())))
236 else: return raws_dat
238 def format_precip(precipa
):
239 rain
=np
.array(precipa
, dtype
= 'float64')
240 rain
= np
.diff(rain
) # first difference to convert accumulated to hourly
241 rain
= np
.insert(rain
, 0, [np
.NaN
]) # add NaN entry to account for diff
242 rain
[rain
> 1000] = np
.NaN
# filter out erroneously high
243 rain
[rain
< 0] = np
.NaN
# filter out negative, results from diff function after precipa goes to zero
249 for i
in np
.where(np
.isnan(a
)):
250 a
[i
]=0.5*(a
[i
-1]+a
[i
+1])
251 if not any(np
.isnan(a
)):
255 def retrieve_raws(mes
, stid
, raws_vars
, time1
, time2
):
256 meso_ts
= mes
.timeseries(time1
, time2
,
257 stid
=stid
, vars=raws_vars
)
258 station
= meso_ts
['STATION'][0]
260 raws_dat
= format_raws(station
)
262 return station
, raws_dat
264 def raws_data(start
=None, hours
=None, h2
=None, stid
=None,meso_token
=None):
267 # hours legth of the period
268 # h2 (optional) length of the training period
269 # stid the station id
271 time_end
= datetime
.strptime(start
, "%Y%m%d%H%M") + timedelta(hours
= hours
+1) # end time, plus a buffer to control for time shift
272 time_end
= str(int(time_end
.strftime("%Y%m%d%H%M")))
273 print('data_raws: Time Parameters:')
275 print('Time Start:', datetime
.strptime(time_start
, "%Y%m%d%H%M").strftime("%Y/%M/%d %H:%M"))
276 print('Time End:', datetime
.strptime(time_end
, "%Y%m%d%H%M").strftime("%Y/%M/%d %H:%M"))
277 print('Total Runtime:', hours
, 'hours')
278 print('Training Time:', h2
, 'hours')
280 raws_vars
='air_temp,relative_humidity,precip_accum,fuel_moisture'
282 station
, raws_dat
= retrieve_raws(m
, stid
, raws_vars
, time_start
, time_end
)
283 raws_dat
['title']='RAWS data station ' + stid
284 raws_dat
.update({'hours':hours
,'h2':h2
,'station':station
})
287 print('Station ID:', station
['STID'])
288 print('Lat / Lon:', station
['LATITUDE'],', ',station
['LONGITUDE'])
289 if(station
['QC_FLAGGED']): print('WARNING: station flagged for QC')
291 raws_dat
.update({'hours':hours
,'h2':h2
})
294 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~