99import copy
1010import logging
1111import datetime as dt
12+ import itertools
13+ import warnings
1214import numpy as np
1315from scipy import sparse
16+ from pathos .multiprocessing import ProcessingPool as Pool
1417
1518from climada .hazard .tag import Tag as TagHazard
1619from climada .hazard .centroids .base import Centroids
1720from climada .hazard .source import READ_SET
1821from climada .util .files_handler import to_list , get_file_names
19- import climada .util .plot as plot
22+ import climada .util .plot as u_plot
2023import climada .util .checker as check
24+ import climada .util .dates_times as u_dt
2125
2226LOGGER = logging .getLogger (__name__ )
2327
2731 }
2832""" Supported files format to read from """
2933
30- class Hazard (object ):
34+ class Hazard ():
3135 """Contains events of some hazard type defined at centroids. Loads from
3236 files with format defined in FILE_EXT.
3337
@@ -107,7 +111,19 @@ def __init__(self, haz_type='NA', file_name='', description='', \
107111 >>> centr = Centroids(HAZ_DEMO_MAT, 'Centroids demo')
108112 >>> haz = Hazard('TC', HAZ_DEMO_MAT, 'Demo hazard.', centr)
109113 """
110- self .clear ()
114+ self .tag = TagHazard ()
115+ self .units = 'NA'
116+ self .centroids = Centroids ()
117+ # following values are defined for each event
118+ self .event_id = np .array ([], int )
119+ self .frequency = np .array ([], float )
120+ self .event_name = list ()
121+ self .date = np .array ([], int )
122+ self .orig = np .array ([], bool )
123+ # following values are defined for each event and centroid
124+ self .intensity = sparse .csr_matrix (np .empty ((0 , 0 ))) # events x centroids
125+ self .fraction = sparse .csr_matrix (np .empty ((0 , 0 ))) # events x centroids
126+
111127 if '.' in haz_type and file_name == '' :
112128 LOGGER .error ("Provide hazard type." )
113129 raise ValueError
@@ -121,16 +137,14 @@ def clear(self):
121137 """Reinitialize attributes."""
122138 self .tag = TagHazard ()
123139 self .units = 'NA'
124- # following values are defined for each event
125140 self .centroids = Centroids ()
126- self .event_id = np .array ([], int )
127- self .frequency = np .array ([])
128- self .event_name = list ()
129- self .date = np .array ([], int )
130- self .orig = np .array ([], bool )
131- # following values are defined for each event and centroid
132- self .intensity = sparse .csr_matrix (np .empty ((0 , 0 ))) # events x centroids
133- self .fraction = sparse .csr_matrix (np .empty ((0 , 0 ))) # events x centroids
141+ for (var_name , var_val ) in self .__dict__ .items ():
142+ if isinstance (var_val , np .ndarray ) and var_val .ndim == 1 :
143+ setattr (self , var_name , np .array ([]))
144+ elif isinstance (var_val , sparse .csr_matrix ):
145+ setattr (self , var_name , sparse .csr_matrix (np .empty ((0 , 0 ))))
146+ elif isinstance (var_val , list ):
147+ setattr (self , var_name , list ())
134148
135149 def check (self ):
136150 """Check if the attributes contain consistent data.
@@ -167,27 +181,93 @@ def read(self, files, description='', centroids=None, var_names=None):
167181 var_list ):
168182 self .append (self ._read_one (file , haz_type , desc , centr , var ))
169183
170- def plot_rp_intensity (self , return_periods = (25 , 50 , 100 , 250 ), orig = False ,
171- ** kwargs ):
172- """Compute and plot hazard intensity maps for different return periods.
184+ def select (self , date = None , orig = None ):
185+ """Select events within provided date and/or historical or synthetical.
186+ Frequency of the events may need to be recomputed!
187+
188+ Parameters:
189+ date (tuple(str or int), optional): (initial date, final date) in
190+ string ISO format or datetime ordinal integer
191+ orig (bool, optional): select only historical (True) or only
192+ synthetic (False)
193+
194+ Returns:
195+ Hazard or children
196+ """
197+ haz = self .__class__ ()
198+ sel_idx = np .ones (self .event_id .size , bool )
199+
200+ if isinstance (date , tuple ):
201+ date_ini , date_end = date [0 ], date [1 ]
202+ if isinstance (date_ini , str ):
203+ date_ini = u_dt .str_to_date (date [0 ])
204+ date_end = u_dt .str_to_date (date [1 ])
205+
206+ sel_idx = np .logical_and (date_ini <= self .date ,
207+ self .date <= date_end )
208+ if not np .any (sel_idx ):
209+ LOGGER .info ('No hazard in date range %s.' , date )
210+ return None
211+
212+ if isinstance (orig , bool ):
213+ sel_idx = np .logical_and (sel_idx , self .orig .astype (bool ) == orig )
214+ if not np .any (sel_idx ):
215+ LOGGER .info ('No hazard with %s tracks.' , str (orig ))
216+ return None
217+
218+ sel_idx = np .argwhere (sel_idx ).squeeze ()
219+ for (var_name , var_val ) in self .__dict__ .items ():
220+ if isinstance (var_val , np .ndarray ) and var_val .ndim == 1 :
221+ setattr (haz , var_name , var_val [sel_idx ])
222+ elif isinstance (var_val , sparse .csr_matrix ):
223+ setattr (haz , var_name , var_val [sel_idx , :])
224+ elif isinstance (var_val , list ):
225+ setattr (haz , var_name , [var_val [idx ] for idx in sel_idx ])
226+ else :
227+ setattr (haz , var_name , var_val )
228+
229+ return haz
230+
231+ def local_exceedance_inten (self , return_periods = (25 , 50 , 100 , 250 )):
232+ """ Compute exceedance intensity map for given return periods.
233+
234+ Parameters:
235+ return_periods (np.array): return periods to consider
236+
237+ Returns:
238+ np.array
239+ """
240+ LOGGER .info ('Computing exceedance intenstiy map for return periods: %s' ,
241+ return_periods )
242+ cen_pos = range (self .intensity .shape [1 ])
243+ inten_stats = np .zeros ((len (return_periods ), len (cen_pos )))
244+ chunksize = min (len (cen_pos ), 1000 )
245+ for cen_idx , inten_loc in enumerate (Pool ().map (self ._loc_return_inten ,\
246+ itertools .repeat (return_periods , len (cen_pos )), cen_pos , \
247+ chunksize = chunksize )):
248+ inten_stats [:, cen_idx ] = inten_loc
249+ return inten_stats
250+
251+ def plot_rp_intensity (self , return_periods = (25 , 50 , 100 , 250 ), ** kwargs ):
252+ """Compute and plot hazard exceedance intensity maps for different
253+ return periods. Calls local_exceedance_inten.
173254
174255 Parameters:
175256 return_periods (tuple(int), optional): return periods to consider
176- orig (bool, optional): if true, only historical events considered
177257 kwargs (optional): arguments for pcolormesh matplotlib function
178258 used in event plots
179259
180260 Returns:
181261 matplotlib.figure.Figure, matplotlib.axes._subplots.AxesSubplot,
182262 np.ndarray (return_periods.size x num_centroids)
183263 """
184- inten_stats = self ._compute_stats (np .array (return_periods ), orig )
264+ inten_stats = self .local_exceedance_inten (np .array (return_periods ))
185265 colbar_name = 'Wind intensity (' + self .units + ')'
186266 title = list ()
187267 for ret in return_periods :
188268 title .append ('Return period: ' + str (ret ) + ' years' )
189- fig , axis = plot .geo_im_from_array (inten_stats , self .centroids .coord , \
190- colbar_name , title , ** kwargs )
269+ fig , axis = u_plot .geo_im_from_array (inten_stats , self .centroids .coord ,
270+ colbar_name , title , ** kwargs )
191271 return fig , axis , inten_stats
192272
193273 def plot_intensity (self , event = None , centr = None , ** kwargs ):
@@ -218,7 +298,7 @@ def plot_intensity(self, event=None, centr=None, **kwargs):
218298 if isinstance (event , str ):
219299 event = self .get_event_id (event )
220300 return self ._event_plot (event , self .intensity , col_label , ** kwargs )
221- elif centr is not None :
301+ if centr is not None :
222302 if isinstance (centr , tuple ):
223303 centr = self .centroids .get_nearest_id (centr [0 ], centr [1 ])
224304 return self ._centr_plot (centr , self .intensity , col_label )
@@ -254,7 +334,7 @@ def plot_fraction(self, event=None, centr=None, **kwargs):
254334 if isinstance (event , str ):
255335 event = self .get_event_id (event )
256336 return self ._event_plot (event , self .fraction , col_label , ** kwargs )
257- elif centr is not None :
337+ if centr is not None :
258338 if isinstance (centr , tuple ):
259339 centr = self .centroids .get_nearest_id (centr [0 ], centr [1 ])
260340 return self ._centr_plot (centr , self .fraction , col_label )
@@ -283,7 +363,7 @@ def get_event_name(self, event_id):
283363 """"Get the name of an event id.
284364
285365 Parameters:
286- event_id (id ): id of the event
366+ event_id (int ): id of the event
287367
288368 Returns:
289369 str
@@ -309,15 +389,15 @@ def get_event_date(self, event=None):
309389 list(str)
310390 """
311391 if event is None :
312- l_dates = [self . _date_to_str (date ) for date in self .date ]
392+ l_dates = [u_dt . date_to_str (date ) for date in self .date ]
313393 elif isinstance (event , str ):
314394 ev_ids = self .get_event_id (event )
315- l_dates = [self . _date_to_str (self .date [ \
395+ l_dates = [u_dt . date_to_str (self .date [ \
316396 np .argwhere (self .event_id == ev_id )[0 ][0 ]]) \
317397 for ev_id in ev_ids ]
318398 else :
319399 ev_idx = np .argwhere (self .event_id == event )[0 ][0 ]
320- l_dates = [self . _date_to_str (self .date [ev_idx ])]
400+ l_dates = [u_dt . date_to_str (self .date [ev_idx ])]
321401 return l_dates
322402
323403 def calc_year_set (self ):
@@ -357,8 +437,8 @@ def append(self, hazard):
357437 elif hazard .units == 'NA' :
358438 LOGGER .info ("Appended hazard does not have units." )
359439 elif self .units != hazard .units :
360- LOGGER .error ("Hazards with different units can't be appended" \
361- + ": %s != %s." , self .units , hazard .units )
440+ LOGGER .error ("Hazards with different units can't be appended: "
441+ " %s != %s." , self .units , hazard .units )
362442 raise ValueError
363443
364444 self .tag .append (hazard .tag )
@@ -483,13 +563,8 @@ def _append_haz_cent(self, centroids):
483563 cen_self = cen_self_sort [cen_self ]
484564 return cen_self , cen_haz
485565
486- @staticmethod
487- def _date_to_str (date ):
488- """ Compute date string from input datetime ordinal int. """
489- return dt .date .fromordinal (date ).isoformat ()
490-
491- @staticmethod
492- def _read_one (file_name , haz_type , description = '' , centroids = None , \
566+ @classmethod
567+ def _read_one (cls , file_name , haz_type , description = '' , centroids = None , \
493568 var_names = None ):
494569 """ Read hazard, and centroids if not provided, from input file.
495570
@@ -504,10 +579,10 @@ def _read_one(file_name, haz_type, description='', centroids=None, \
504579 ValueError, KeyError
505580
506581 Returns:
507- Hazard
582+ Hazard or children
508583 """
509584 LOGGER .info ('Reading file: %s' , file_name )
510- new_haz = Hazard ()
585+ new_haz = cls ()
511586 new_haz .tag = TagHazard (haz_type , file_name , description )
512587
513588 extension = os .path .splitext (file_name )[1 ]
@@ -597,8 +672,8 @@ def _event_plot(self, event_id, mat_var, col_name, **kwargs):
597672 array_val .append (im_val )
598673 l_title .append (title )
599674
600- return plot .geo_im_from_array (array_val , self .centroids .coord ,
601- col_name , l_title , ** kwargs )
675+ return u_plot .geo_im_from_array (array_val , self .centroids .coord ,
676+ col_name , l_title , ** kwargs )
602677
603678 def _centr_plot (self , centr_id , mat_var , col_name ):
604679 """"Plot a centroid of the input matrix.
@@ -639,51 +714,27 @@ def _centr_plot(self, centr_id, mat_var, col_name):
639714 array_val = np .max (mat_var , axis = 1 ).todense ()
640715 title = '%s max intensity at each event' % self .tag .haz_type
641716
642- graph = plot .Graph2D (title )
717+ graph = u_plot .Graph2D (title )
643718 graph .add_subplot ('Event number' , col_name )
644719 graph .add_curve (range (len (array_val )), array_val , 'b' )
645720 graph .set_x_lim (range (len (array_val )))
646721 return graph .get_elems ()
647722
648- def _compute_stats (self , return_periods , orig = False ):
649- """ Compute intensity map for given return periods.
650-
651- Parameters:
652- return_periods (np.array): return periods to consider
653- orig (bool, optional): if true, only historical events considered
654-
655- Returns:
656- np.array
657- """
658- inten_stats = np .zeros ((len (return_periods ), self .intensity .shape [1 ]))
659- inten = self .intensity
660- freq = self .frequency
661- if orig :
662- inten = inten [self .orig , :]
663- freq = freq [self .orig ] * self .event_id .size / \
664- self .orig .nonzero ()[0 ].size
665- for cen_pos in range (self .intensity .shape [1 ]):
666- inten_loc = self ._loc_return_inten (return_periods , cen_pos ,
667- inten , freq )
668- inten_stats [:, cen_pos ] = inten_loc
669- return inten_stats
670-
671- def _loc_return_inten (self , return_periods , cen_pos , inten , freq ):
723+ def _loc_return_inten (self , return_periods , cen_pos ):
672724 """ Compute local exceedence intensity for given return period.
673725
674726 Parameters:
675727 return_periods (np.array): return periods to consider
676728 cen_pos (int): centroid position
677- inten (sparse.csr_matrix): intensity of the events at centroids
678- freq (np.array): events frequncy
679729 Returns:
680730 np.array
681731 """
682- inten_pos = np .argwhere (inten [:, cen_pos ] >
732+ inten_pos = np .argwhere (self . intensity [:, cen_pos ] >
683733 self .intensity_thres )[:, 0 ]
684734 if inten_pos .size == 0 :
685735 return np .zeros ((return_periods .size , ))
686- inten_nz = np .asarray (inten [inten_pos , cen_pos ].todense ()).squeeze ()
736+ inten_nz = np .asarray (self .intensity [inten_pos , cen_pos ].todense ()). \
737+ squeeze ()
687738 sort_pos = inten_nz .argsort ()[::- 1 ]
688739 try :
689740 inten_sort = inten_nz [sort_pos ]
@@ -692,10 +743,12 @@ def _loc_return_inten(self, return_periods, cen_pos, inten, freq):
692743 inten_sort = np .array ([inten_nz ])
693744 else :
694745 raise err
695- freq_sort = freq [inten_pos [sort_pos ]]
746+ freq_sort = self . frequency [inten_pos [sort_pos ]]
696747 np .cumsum (freq_sort , out = freq_sort )
697748 try :
698- pol_coef = np .polyfit (np .log (freq_sort ), inten_sort , deg = 1 )
749+ with warnings .catch_warnings ():
750+ warnings .simplefilter ("ignore" )
751+ pol_coef = np .polyfit (np .log (freq_sort ), inten_sort , deg = 1 )
699752 except ValueError :
700753 pol_coef = np .polyfit (np .log (freq_sort ), inten_sort , deg = 0 )
701754 inten_fit = np .polyval (pol_coef , np .log (1 / return_periods ))
0 commit comments