2424import logging
2525import numpy as np
2626import matplotlib .pyplot as plt
27- from matplotlib .patches import Rectangle
27+ from matplotlib .patches import Rectangle , FancyArrowPatch
2828from tabulate import tabulate
2929
3030from climada .engine .impact import Impact
@@ -197,7 +197,7 @@ def calc(self, hazard, entity, haz_future=None, ent_future=None, \
197197 self ._print_results ()
198198
199199 def plot_cost_benefit (self ):
200- """ Plot cost-benefit graph. Call after calc()
200+ """ Plot cost-benefit graph. Call after calc().
201201
202202 Returns:
203203 matplotlib.figure.Figure, matplotlib.axes._subplots.AxesSubplot
@@ -238,13 +238,13 @@ def plot_cost_benefit(self):
238238 return fig , axis
239239
240240 def plot_event_view (self , return_per = (10 , 25 , 100 )):
241- """ Plot averted damages for return periods. Call after calc()
241+ """ Plot averted damages for return periods. Call after calc().
242242
243243 Returns:
244244 matplotlib.figure.Figure, matplotlib.axes._subplots.AxesSubplot
245245 """
246246 if not self .imp_meas_future :
247- LOGGER .error ('Compute COstBenefit .calc() first' )
247+ LOGGER .error ('Compute CostBenefit .calc() first' )
248248 raise ValueError
249249 fig , axis = plt .subplots (1 , 1 )
250250 avert_rp = dict ()
@@ -274,8 +274,9 @@ def plot_event_view(self, return_per=(10, 25, 100)):
274274 return fig , axis
275275
276276 def plot_waterfall (self , hazard , entity , haz_future , ent_future ,
277- risk_func = risk_aai_agg , imp_time_depen = 1 ):
278- """ Plot waterfall graph. Can be called before and after calc()
277+ risk_func = risk_aai_agg ):
278+ """ Plot waterfall graph with given risk metric. Can be called before
279+ and after calc().
279280
280281 Parameters:
281282 hazard (Hazard): hazard
@@ -285,8 +286,6 @@ def plot_waterfall(self, hazard, entity, haz_future, ent_future,
285286 ent_future (Entity): entity in the future
286287 risk_func (func, optional): function describing risk measure given
287288 an Impact. Default: average annual impact (aggregated).
288- imp_time_depen (float, optional): parameter which represent time
289- evolution of impact. Default: 1 (linear).
290289
291290 Returns:
292291 matplotlib.figure.Figure, matplotlib.axes._subplots.AxesSubplot
@@ -315,28 +314,102 @@ def plot_waterfall(self, hazard, entity, haz_future, ent_future,
315314 norm_fact , norm_name = self ._norm_values (curr_risk )
316315
317316 # current situation
318- risk_future = curr_risk
317+ LOGGER .info ('Risk at {:d}: {:.3e}' .format (self .present_year , curr_risk ))
318+
319+ # changing future
320+ # socio-economic dev
321+ imp = Impact ()
322+ imp .calc (ent_future .exposures , ent_future .impact_funcs , hazard )
323+ risk_dev = risk_func (imp )
324+ # current situation
325+ LOGGER .info ('Risk with development at {:d}: {:.3e}' .format (self .future_year ,
326+ risk_dev ))
327+
328+ # socioecon + cc
329+ LOGGER .info ('Risk with development and climate change at {:d}: {:.3e}' .\
330+ format (self .future_year , fut_risk ))
331+
332+ axis .bar (1 , curr_risk / norm_fact )
333+ axis .text (1 , curr_risk / norm_fact , str (int (round (curr_risk / norm_fact ))), \
334+ horizontalalignment = 'center' , verticalalignment = 'bottom' , \
335+ fontsize = 12 , color = 'k' )
336+ axis .bar (2 , height = (risk_dev - curr_risk )/ norm_fact , bottom = curr_risk / norm_fact )
337+ axis .text (2 , curr_risk / norm_fact + (risk_dev - curr_risk )/ norm_fact / 2 , \
338+ str (int (round ((risk_dev - curr_risk )/ norm_fact ))), \
339+ horizontalalignment = 'center' , verticalalignment = 'center' , fontsize = 12 , color = 'k' )
340+ axis .bar (3 , height = (fut_risk - risk_dev )/ norm_fact , bottom = risk_dev / norm_fact )
341+ axis .text (3 , risk_dev / norm_fact + (fut_risk - risk_dev )/ norm_fact / 2 , \
342+ str (int (round ((fut_risk - risk_dev )/ norm_fact ))), \
343+ horizontalalignment = 'center' , verticalalignment = 'center' , fontsize = 12 , color = 'k' )
344+ axis .bar (4 , height = fut_risk / norm_fact )
345+ axis .text (4 , fut_risk / norm_fact , str (int (round (fut_risk / norm_fact ))), \
346+ horizontalalignment = 'center' , verticalalignment = 'bottom' , \
347+ fontsize = 12 , color = 'k' )
348+ plt .xticks (np .arange (4 )+ 1 , ['Risk ' + str (self .present_year ), \
349+ 'Economic \n development' , 'Climate \n change' , 'Risk ' + str (self .future_year )])
350+ axis .set_ylabel ('Impact (' + self .unit + ' ' + norm_name + ')' )
351+ axis .set_title ('Risk at {:d} and {:d}' .format (self .present_year , self .future_year ))
352+ return fig , axis
353+
354+ def plot_waterfall_accumulated (self , hazard , entity , haz_future , ent_future ,
355+ risk_func = risk_aai_agg , imp_time_depen = 1 ,
356+ plot_arrow = True ):
357+ """ Plot waterfall graph with accumulated values from present to future
358+ year. Call after calc(). Provide same risk_func and imp_time_depen as
359+ in calc.
360+
361+ Parameters:
362+ hazard (Hazard): hazard
363+ entity (Entity): entity
364+ haz_future (Hazard): hazard in the future (future year provided at
365+ ent_future)
366+ ent_future (Entity): entity in the future
367+ risk_func (func, optional): function describing risk measure given
368+ an Impact. Default: average annual impact (aggregated).
369+ imp_time_depen (float, optional): parameter which represent time
370+ evolution of impact. Default: 1 (linear).
371+ plot_arrow (bool, optional): plot adaptation arrow
372+
373+ Returns:
374+ matplotlib.figure.Figure, matplotlib.axes._subplots.AxesSubplot
375+ """
376+ if not self .imp_meas_future or not self .imp_meas_present :
377+ LOGGER .error ('Compute CostBenefit.calc() first' )
378+ raise ValueError
379+ if ent_future .exposures .ref_year == entity .exposures .ref_year :
380+ LOGGER .error ('Same reference years for future and present entities.' )
381+ raise ValueError
382+
383+ self .present_year = entity .exposures .ref_year
384+ self .future_year = ent_future .exposures .ref_year
385+
386+ # current situation
387+ curr_risk = self .imp_meas_present ['no measure' ]['risk' ]
319388 time_dep = self ._time_dependency_array ()
320- risk_curr = self ._npv_unaverted_impact (risk_future , entity .disc_rates ,
389+ risk_curr = self ._npv_unaverted_impact (curr_risk , entity .disc_rates ,
321390 time_dep )
322- LOGGER .info ('Risk function at {:d}: {:.3e}' .format (self .present_year ,
323- risk_future ))
391+ LOGGER .info ('Current total risk at {:d}: {:.3e}' .format (self .future_year ,
392+ risk_curr ))
324393
325394 # changing future
326395 time_dep = self ._time_dependency_array (imp_time_depen )
327396 # socio-economic dev
328397 imp = Impact ()
329398 imp .calc (ent_future .exposures , ent_future .impact_funcs , hazard )
330- risk_future = risk_func (imp )
331- risk_dev = self ._npv_unaverted_impact (risk_future , entity .disc_rates ,
399+ risk_dev = self ._npv_unaverted_impact (risk_func (imp ), entity .disc_rates ,
332400 time_dep , curr_risk )
401+ LOGGER .info ('Total risk with development at {:d}: {:.3e}' .format ( \
402+ self .future_year , risk_dev ))
403+
333404 # socioecon + cc
334- risk_future = fut_risk
335- risk_tot = self ._npv_unaverted_impact (risk_future , entity .disc_rates ,
336- time_dep , curr_risk )
337- LOGGER .info ('Risk function at {:d}: {:.3e}' .format (self .future_year ,
338- risk_future ))
405+ risk_tot = self ._npv_unaverted_impact (self .imp_meas_future ['no measure' ]['risk' ], \
406+ entity .disc_rates , time_dep , curr_risk )
407+ LOGGER .info ('Total risk with development and climate change at {:d}: {:.3e}' .\
408+ format (self .future_year , risk_tot ))
339409
410+ # plot
411+ fig , axis = plt .subplots (1 , 1 )
412+ norm_fact , norm_name = self ._norm_values (curr_risk )
340413 axis .bar (1 , risk_curr / norm_fact )
341414 axis .text (1 , risk_curr / norm_fact , str (int (round (risk_curr / norm_fact ))), \
342415 horizontalalignment = 'center' , verticalalignment = 'bottom' , \
@@ -349,16 +422,27 @@ def plot_waterfall(self, hazard, entity, haz_future, ent_future,
349422 axis .text (3 , risk_dev / norm_fact + (risk_tot - risk_dev )/ norm_fact / 2 , \
350423 str (int (round ((risk_tot - risk_dev )/ norm_fact ))), \
351424 horizontalalignment = 'center' , verticalalignment = 'center' , fontsize = 12 , color = 'k' )
352- axis .bar (4 , height = risk_tot / norm_fact )
425+ bar_4 = axis .bar (4 , height = risk_tot / norm_fact )
353426 axis .text (4 , risk_tot / norm_fact , str (int (round (risk_tot / norm_fact ))), \
354427 horizontalalignment = 'center' , verticalalignment = 'bottom' , \
355428 fontsize = 12 , color = 'k' )
429+
430+ if plot_arrow :
431+ bar_bottom , bar_top = bar_4 [0 ].get_bbox ().get_points ()
432+ axis .text (bar_top [0 ] - (bar_top [0 ]- bar_bottom [0 ])/ 2 , bar_top [1 ],
433+ "Averted" , ha = "center" , va = "top" , rotation = 270 , size = 15 )
434+ arrow_len = min (np .array (list (self .benefit .values ())).sum ()/ norm_fact ,
435+ risk_tot / norm_fact )
436+ axis .add_patch (FancyArrowPatch ((bar_top [0 ] - (bar_top [0 ]- bar_bottom [0 ])/ 2 , \
437+ bar_top [1 ]), (bar_top [0 ]- (bar_top [0 ]- bar_bottom [0 ])/ 2 , \
438+ risk_tot / norm_fact - arrow_len ), mutation_scale = 100 , color = 'k' , \
439+ alpha = 0.4 ))
440+
356441 plt .xticks (np .arange (4 )+ 1 , ['Risk ' + str (self .present_year ), \
357442 'Economic \n development' , 'Climate \n change' , 'Risk ' + str (self .future_year )])
358443 axis .set_ylabel ('Impact (' + self .unit + ' ' + norm_name + ')' )
359- axis .set_title ('Total accumulated damage from {:d} to {:d}' .format (
360- self .present_year , self .future_year ))
361-
444+ axis .set_title ('Total accumulated impact from {:d} to {:d}' .format ( \
445+ self .present_year , self .future_year ))
362446 return fig , axis
363447
364448 def _calc_impact_measures (self , hazard , exposures , meas_set , imp_fun_set , \
0 commit comments