5050DATA_FOLDER .mkdir (exist_ok = True )
5151
5252
53- @pytest .fixture (autouse = True )
54- def exposure_fixture (n_exp = 50 ):
53+ @pytest .fixture (params = [50 , 1 , 0 ])
54+ def exposure (request ):
55+ n_exp = request .param
5556 lats = np .linspace (- 10 , 10 , n_exp )
5657 lons = np .linspace (- 10 , 10 , n_exp )
5758 data = gpd .GeoDataFrame (
@@ -67,19 +68,19 @@ def exposure_fixture(n_exp=50):
6768 return exposures
6869
6970
70- @pytest .fixture ( autouse = True )
71- def hazard_fixture ( exposure_fixture ):
71+ @pytest .fixture
72+ def hazard ( exposure ):
7273 n_events = 10
7374 centroids = Centroids (
74- lat = exposure_fixture .gdf .geometry .x ,
75- lon = exposure_fixture .gdf .geometry .y ,
75+ lat = exposure .gdf .geometry .x ,
76+ lon = exposure .gdf .geometry .y ,
7677 )
7778 intensity = sparse .csr_matrix (
78- np .ones ((n_events , exposure_fixture .gdf .shape [0 ])) * 50
79+ np .ones ((n_events , exposure .gdf .shape [0 ])) * 50
7980 ) # uniform intensity
8081 haz = Hazard ()
8182 haz .event_id = np .arange (n_events )
82- haz .event_name = haz .event_id
83+ haz .event_name = haz .event_id . tolist ()
8384 haz .haz_type = "TC"
8485 haz .date = haz .event_id
8586 haz .frequency_unit = "m/s"
@@ -89,24 +90,28 @@ def hazard_fixture(exposure_fixture):
8990 return haz
9091
9192
92- @pytest .fixture ( autouse = True )
93- def hazard_forecast_fixture ( hazard_fixture ):
94- n_events = hazard_fixture .size
93+ @pytest .fixture
94+ def hazard_forecast ( hazard ):
95+ n_events = hazard .size
9596 lead_time = pd .timedelta_range ("1h" , periods = n_events ).to_numpy ()
96- member = np .arange (10 )
97+ member = np .arange (n_events )
9798 haz_fc = HazardForecast .from_hazard (
98- hazard = hazard_fixture ,
99+ hazard = hazard ,
99100 lead_time = lead_time ,
100101 member = member ,
101102 )
102103 return haz_fc
103104
104105
105- @pytest .fixture ( autouse = True )
106- def impact_func_set_fixture ( exposure_fixture , hazard_fixture ):
106+ @pytest .fixture
107+ def impact_func_set ( exposure , hazard ):
107108 step_impf = ImpactFunc ()
108- step_impf .id = exposure_fixture .data [f"impf_{ hazard_fixture .haz_type } " ].unique ()[0 ]
109- step_impf .haz_type = hazard_fixture .haz_type
109+ step_impf .id = 1
110+ try :
111+ step_impf .id = exposure .data [f"impf_{ hazard .haz_type } " ].unique ()[0 ]
112+ except IndexError :
113+ pass
114+ step_impf .haz_type = hazard .haz_type
110115 step_impf .name = "fixture step function"
111116 step_impf .intensity_unit = ""
112117 step_impf .intensity = np .array ([0 , 0.495 , 0.4955 , 0.5 , 1 , 10 ])
@@ -115,18 +120,12 @@ def impact_func_set_fixture(exposure_fixture, hazard_fixture):
115120 return ImpactFuncSet ([step_impf ])
116121
117122
118- @pytest .fixture (autouse = True )
119- def impact_calc_fixture (exposure_fixture , hazard_fixture , impact_func_set_fixture ):
120- imp_mat = np .ones (
121- (
122- len (hazard_fixture .event_id ),
123- exposure_fixture .gdf .shape [0 ],
124- exposure_fixture .gdf .shape [0 ],
125- )
126- )
127- aai_agg = np .sum (exposure_fixture .gdf ["value" ]) * hazard_fixture .frequency [0 ]
128- eai_exp = np .ones (exposure_fixture .gdf .shape [0 ]) * hazard_fixture .frequency [0 ]
129- at_event = np .ones (hazard_fixture .size ) * np .sum (exposure_fixture .gdf ["value" ])
123+ @pytest .fixture
124+ def impact_calc (exposure , hazard ):
125+ imp_mat = np .ones ((len (hazard .event_id ), exposure .gdf .shape [0 ]))
126+ aai_agg = np .sum (exposure .gdf ["value" ]) * hazard .frequency [0 ]
127+ eai_exp = np .ones (exposure .gdf .shape [0 ]) * hazard .frequency [0 ]
128+ at_event = np .ones (hazard .size ) * np .sum (exposure .gdf ["value" ])
130129 return {
131130 "imp_mat" : imp_mat ,
132131 "aai_agg" : aai_agg ,
@@ -135,17 +134,18 @@ def impact_calc_fixture(exposure_fixture, hazard_fixture, impact_func_set_fixtur
135134 }
136135
137136
138- def check_impact (self , imp , haz , exp , aai_agg , eai_exp , at_event , imp_mat_array = None ):
137+ def check_impact (imp , haz , exp , aai_agg , eai_exp , at_event , imp_mat_array = None ):
139138 """Test properties of impacts"""
140- self .assertEqual (len (haz .event_id ), len (imp .at_event ))
141- self .assertIsInstance (imp , Impact )
139+ # NOTE: Correctly compares NaNs!
140+ assert len (haz .event_id ) == len (imp .at_event )
141+ assert isinstance (imp , Impact )
142142 np .testing .assert_allclose (imp .coord_exp [:, 0 ], exp .latitude )
143143 np .testing .assert_allclose (imp .coord_exp [:, 1 ], exp .longitude )
144- self . assertAlmostEqual (imp .aai_agg , aai_agg , 3 )
144+ np . testing . assert_allclose (imp .aai_agg , aai_agg , rtol = 1e- 3 )
145145 np .testing .assert_allclose (imp .eai_exp , eai_exp , rtol = 1e-5 )
146146 np .testing .assert_allclose (imp .at_event , at_event , rtol = 1e-5 )
147147 if imp_mat_array is not None :
148- np .testing .assert_allclose (imp .imp_mat .toarray (). ravel () , imp_mat_array . ravel () )
148+ np .testing .assert_allclose (imp .imp_mat .todense () , imp_mat_array )
149149
150150
151151class TestImpactCalc (unittest .TestCase ):
@@ -389,7 +389,7 @@ def test_calc_impact_RF_pass(self):
389389 ]
390390 )
391391 # fmt: on
392- check_impact (self , impact , haz , exp , aai_agg , eai_exp , at_event , imp_mat_array )
392+ check_impact (impact , haz , exp , aai_agg , eai_exp , at_event , imp_mat_array )
393393
394394 def test_empty_impact (self ):
395395 """Check that empty impact is returned if no centroids match the exposures"""
@@ -400,11 +400,11 @@ def test_empty_impact(self):
400400 aai_agg = 0.0
401401 eai_exp = np .zeros (len (exp .gdf ))
402402 at_event = np .zeros (HAZ .size )
403- check_impact (self , impact , HAZ , exp , aai_agg , eai_exp , at_event , None )
403+ check_impact (impact , HAZ , exp , aai_agg , eai_exp , at_event , None )
404404
405405 impact = icalc .impact (save_mat = True , assign_centroids = False )
406406 imp_mat_array = sparse .csr_matrix ((HAZ .size , len (exp .gdf ))).toarray ()
407- check_impact (self , impact , HAZ , exp , aai_agg , eai_exp , at_event , imp_mat_array )
407+ check_impact (impact , HAZ , exp , aai_agg , eai_exp , at_event , imp_mat_array )
408408
409409 def test_single_event_impact (self ):
410410 """Check impact for single event"""
@@ -414,11 +414,11 @@ def test_single_event_impact(self):
414414 aai_agg = 0.0
415415 eai_exp = np .zeros (len (ENT .exposures .gdf ))
416416 at_event = np .array ([0 ])
417- check_impact (self , impact , haz , ENT .exposures , aai_agg , eai_exp , at_event , None )
417+ check_impact (impact , haz , ENT .exposures , aai_agg , eai_exp , at_event , None )
418418 impact = icalc .impact (save_mat = True , assign_centroids = False )
419419 imp_mat_array = sparse .csr_matrix ((haz .size , len (ENT .exposures .gdf ))).toarray ()
420420 check_impact (
421- self , impact , haz , ENT .exposures , aai_agg , eai_exp , at_event , imp_mat_array
421+ impact , haz , ENT .exposures , aai_agg , eai_exp , at_event , imp_mat_array
422422 )
423423
424424 def test_calc_impact_save_mat_pass (self ):
@@ -692,70 +692,47 @@ def test_single_exp_zero_mdr(self):
692692 imp = ImpactCalc (exp , impf_set , haz ).impact (
693693 assign_centroids = False , save_mat = True
694694 )
695- check_impact (self , imp , haz , exp , aai_agg , eai_exp , at_event , at_event )
695+ check_impact (imp , haz , exp , aai_agg , eai_exp , at_event , at_event )
696696
697697
698698class TestImpactCalcForecast :
699699 """Test impact calc for forecast hazard"""
700700
701- def test_impactForecast_type (
701+ @pytest .fixture
702+ def impact_calc_forecast (self , impact_calc ):
703+ """Write NaNs to attributes that are not used"""
704+ impact_calc ["aai_agg" ] = np .full_like (impact_calc ["aai_agg" ], np .nan )
705+ impact_calc ["eai_exp" ] = np .full_like (impact_calc ["eai_exp" ], np .nan )
706+
707+ def test_impact_forecast (
702708 self ,
703- exposure_fixture ,
704- hazard_forecast_fixture ,
705- impact_func_set_fixture ,
706- impact_calc_fixture ,
709+ exposure ,
710+ hazard_forecast ,
711+ impact_func_set ,
712+ impact_calc ,
713+ impact_calc_forecast ,
707714 ):
708715 """Test that ImpactForecast is returned correctly"""
709- impact = ImpactCalc (
710- exposure_fixture , impact_func_set_fixture , hazard_forecast_fixture
711- ). impact ( assign_centroids = True , save_mat = True )
716+ impact = ImpactCalc (exposure , impact_func_set , hazard_forecast ). impact (
717+ assign_centroids = True , save_mat = True
718+ )
712719 # check that impact is indeed ImpactForecast
720+ impact_calc ["imp_mat_array" ] = impact_calc .pop ("imp_mat" )
721+ check_impact (imp = impact , haz = hazard_forecast , exp = exposure , ** impact_calc )
713722 assert isinstance (impact , ImpactForecast )
714- np .testing .assert_array_equal (
715- impact .lead_time , hazard_forecast_fixture .lead_time
716- )
717- assert impact .lead_time .dtype == hazard_forecast_fixture .lead_time .dtype
718- np .testing .assert_array_equal (impact .member , hazard_forecast_fixture .member )
723+ np .testing .assert_array_equal (impact .lead_time , hazard_forecast .lead_time )
724+ assert impact .lead_time .dtype == hazard_forecast .lead_time .dtype
725+ np .testing .assert_array_equal (impact .member , hazard_forecast .member )
719726
720727 def test_impact_forecast_empty_impmat_error (
721- self , hazard_forecast_fixture , exposure_fixture , impact_func_set_fixture
728+ self , hazard_forecast , exposure , impact_func_set
722729 ):
723730 """Test that error is raised when trying to compute impact forecast
724731 without saving impact matrix
725732 """
726- icalc = ImpactCalc (
727- exposure_fixture , impact_func_set_fixture , hazard_forecast_fixture
728- )
729- no_impmat_exception = (
730- "Saving impact matrix is required when using HazardForecast."
731- "Please set save_mat=True."
732- )
733- with pytest .raises (ValueError ) as cm :
733+ icalc = ImpactCalc (exposure , impact_func_set , hazard_forecast )
734+ with pytest .raises (ValueError , match = "Saving impact matrix is required" ):
734735 icalc .impact (assign_centroids = True , save_mat = False )
735- assert no_impmat_exception == str (cm .value )
736-
737- def test_impact_forecast_blocked_nonsense_attrs (
738- self , hazard_forecast_fixture , exposure_fixture , impact_func_set_fixture
739- ):
740- """Test that nonsense attributes are blocked when computing impact forecast"""
741- lead_time = hazard_forecast_fixture .lead_time
742- member = hazard_forecast_fixture .member
743-
744- impact = ImpactCalc (
745- exposure_fixture , impact_func_set_fixture , hazard_forecast_fixture
746- ).impact (assign_centroids = True , save_mat = True )
747- assert np .isnan (impact .aai_agg )
748- assert np .all (np .isnan (impact .eai_exp ))
749- assert impact .eai_exp .shape == (len (exposure_fixture .gdf ),)
750-
751- # test that aai_agg and eai_exp are also nan when 0-size exp
752- empty_exp = exposure_fixture (n_exp = 0 )
753- impact_empty = ImpactCalc (
754- exposure_fixture , impact_func_set_fixture , hazard_forecast_fixture
755- ).impact (assign_centroids = True , save_mat = True )
756- assert np .isnan (impact_empty .aai_agg )
757- assert np .all (np .isnan (impact_empty .eai_exp ))
758- assert impact_empty .eai_exp .shape == (len (empty_exp .gdf ),)
759736
760737
761738class TestImpactMatrixCalc (unittest .TestCase ):
0 commit comments