From 62b35531548700e5d8c56a8241ace38250ac8cd1 Mon Sep 17 00:00:00 2001
From: Le Roux Erwan <erwan.le-roux@irstea.fr>
Date: Sat, 18 Apr 2020 15:27:10 +0200
Subject: [PATCH] [refactor] improve test coverage for
 abstract_gev_trend_test.py and safran_variable.py

---
 .../scm_models_data/safran/safran.py          | 11 +++--
 .../scm_models_data/safran/safran_variable.py |  8 ++--
 extreme_trend/abstract_gev_trend_test.py      | 48 +++----------------
 ...bstract_comparison_non_stationary_model.py | 13 ++---
 ..._result.py => main_constrasting_result.py} |  0
 .../test_meteo_france_data/test_SCM_study.py  |  9 +++-
 .../test_meteo_france_data/test_variables.py  | 26 ++++++++++
 .../test_visualization.py                     | 10 ++++
 .../test_gev/test_gev_params.py               | 10 ++++
 .../test_contrasting/test_two_fold_fit.py     |  1 +
 10 files changed, 77 insertions(+), 59 deletions(-)
 rename projects/contrasting_trends_in_snow_loads/spatial trends/{main_result.py => main_constrasting_result.py} (100%)
 create mode 100644 test/test_extreme_data/test_meteo_france_data/test_visualization.py

diff --git a/extreme_data/meteo_france_data/scm_models_data/safran/safran.py b/extreme_data/meteo_france_data/scm_models_data/safran/safran.py
index ac0024d7..81a95f70 100644
--- a/extreme_data/meteo_france_data/scm_models_data/safran/safran.py
+++ b/extreme_data/meteo_france_data/scm_models_data/safran/safran.py
@@ -12,12 +12,15 @@ from extreme_data.meteo_france_data.scm_models_data.safran.safran_variable impor
 
 
 class Safran(AbstractStudy):
+    SAFRAN_VARIABLES = [SafranSnowfallVariable,
+                        SafranRainfallVariable,
+                        SafranTemperatureVariable,
+                        SafranTotalPrecipVariable,
+                        SafranNormalizedPrecipitationRateVariable,
+                        SafranNormalizedPrecipitationRateOnWetDaysVariable]
 
     def __init__(self, variable_class: type, *args, **kwargs):
-        assert variable_class in [SafranSnowfallVariable, SafranRainfallVariable, SafranTemperatureVariable,
-                                  SafranTotalPrecipVariable,
-                                  SafranNormalizedPrecipitationRateVariable,
-                                  SafranNormalizedPrecipitationRateOnWetDaysVariable]
+        assert variable_class in self.SAFRAN_VARIABLES
         super().__init__(variable_class, *args, **kwargs)
         self.model_name = 'Safran'
 
diff --git a/extreme_data/meteo_france_data/scm_models_data/safran/safran_variable.py b/extreme_data/meteo_france_data/scm_models_data/safran/safran_variable.py
index 66dcc8f5..55d8d7b5 100644
--- a/extreme_data/meteo_france_data/scm_models_data/safran/safran_variable.py
+++ b/extreme_data/meteo_france_data/scm_models_data/safran/safran_variable.py
@@ -79,7 +79,7 @@ class SafranRainfallVariable(SafranSnowfallVariable):
 class SafranTotalPrecipVariable(AbstractVariable):
     NAME = 'Precipitation'
 
-    def __init__(self, snow_variable_array, rain_variable_array, nb_consecutive_days):
+    def __init__(self, snow_variable_array, rain_variable_array, nb_consecutive_days=3):
         super().__init__(None)
         snow_precipitation = SafranSnowfallVariable(snow_variable_array, nb_consecutive_days)
         rain_precipitation = SafranRainfallVariable(rain_variable_array, nb_consecutive_days)
@@ -98,9 +98,7 @@ class SafranTotalPrecipVariable(AbstractVariable):
 class SafranNormalizedPrecipitationRateVariable(AbstractVariable):
     NAME = 'Normalized Precip'
 
-
-
-    def __init__(self, temperature_variable_array, snow_variable_array, rain_variable_array, nb_consecutive_days):
+    def __init__(self, temperature_variable_array, snow_variable_array, rain_variable_array, nb_consecutive_days=3):
         super().__init__(None)
         temperature = SafranTemperatureVariable(temperature_variable_array)
         total_precipitation = SafranTotalPrecipVariable(snow_variable_array, rain_variable_array, nb_consecutive_days)
@@ -119,7 +117,7 @@ class SafranNormalizedPrecipitationRateVariable(AbstractVariable):
 
 class SafranNormalizedPrecipitationRateOnWetDaysVariable(SafranNormalizedPrecipitationRateVariable):
 
-    def __init__(self, temperature_variable_array, snow_variable_array, rain_variable_array, nb_consecutive_days):
+    def __init__(self, temperature_variable_array, snow_variable_array, rain_variable_array, nb_consecutive_days=3):
         super().__init__(temperature_variable_array, snow_variable_array, rain_variable_array, nb_consecutive_days)
         total_precipitation = SafranTotalPrecipVariable(snow_variable_array, rain_variable_array, nb_consecutive_days)
         mask_for_nan_values = total_precipitation.daily_time_serie_array < 0.01
diff --git a/extreme_trend/abstract_gev_trend_test.py b/extreme_trend/abstract_gev_trend_test.py
index 5cbfa164..bcee5a5e 100644
--- a/extreme_trend/abstract_gev_trend_test.py
+++ b/extreme_trend/abstract_gev_trend_test.py
@@ -37,28 +37,16 @@ class AbstractGevTrendTest(object):
         self.fit_method = fit_method
         # Load observations, coordinates and datasets
         self.coordinates, self.dataset = load_temporal_coordinates_and_dataset(self.maxima, self.years)
-        # By default crashed boolean is False
-        self.crashed = False
-        try:
-            pass
-        except SafeRunException:
-            self.crashed = True
 
     @cached_property
     def constrained_estimator(self):
-        try:
-            return fitted_linear_margin_estimator(self.constrained_model_class, self.coordinates, self.dataset,
-                                                  self.starting_year, self.fit_method)
-        except SafeRunException:
-            self.crashed = True
+        return fitted_linear_margin_estimator(self.constrained_model_class, self.coordinates, self.dataset,
+                                              self.starting_year, self.fit_method)
 
     @cached_property
     def unconstrained_estimator(self):
-        try:
-            return fitted_linear_margin_estimator(self.unconstrained_model_class, self.coordinates, self.dataset,
+        return fitted_linear_margin_estimator(self.unconstrained_model_class, self.coordinates, self.dataset,
                                                   self.starting_year, self.fit_method)
-        except SafeRunException:
-            self.crashed = True
 
     # Likelihood ratio test
 
@@ -87,25 +75,15 @@ class AbstractGevTrendTest(object):
 
     @property
     def constrained_model_deviance(self):
-        if self.crashed:
-            return np.nan
-        else:
-            return self.constrained_estimator.result_from_model_fit.deviance
+        return self.constrained_estimator.result_from_model_fit.deviance
 
     @property
     def unconstrained_model_deviance(self):
         unconstrained_estimator = self.unconstrained_estimator
-        if self.crashed:
-            return np.nan
-        else:
-            return unconstrained_estimator.result_from_model_fit.deviance
+        return unconstrained_estimator.result_from_model_fit.deviance
 
     # Evolution of the GEV parameters and corresponding quantiles
 
-    @property
-    def test_sign(self) -> int:
-        return np.sign(self.time_derivative_of_return_level)
-
     def get_non_stationary_linear_coef(self, param_name: str):
         return self.unconstrained_estimator.function_from_fit.get_coef(param_name,
                                                                        AbstractCoordinates.COORDINATE_T)
@@ -116,12 +94,6 @@ class AbstractGevTrendTest(object):
         return self.unconstrained_estimator.function_from_fit.get_params(coordinate=np.array([1958]),
                                                                          is_transformed=False)
 
-    @cached_property
-    def constrained_estimator_gev_params(self) -> GevParams:
-        # Constant parameters correspond to any gev params
-        return self.constrained_estimator.function_from_fit.get_params(coordinate=np.array([1958]),
-                                                                       is_transformed=False)
-
     def time_derivative_times_years(self, nb_years):
         # Compute the slope strength
         slope = self._slope_strength()
@@ -131,10 +103,7 @@ class AbstractGevTrendTest(object):
 
     @property
     def time_derivative_of_return_level(self):
-        if self.crashed:
-            return 0.0
-        else:
-            return self.time_derivative_times_years(self.nb_years_for_quantile_evolution)
+        return self.time_derivative_times_years(self.nb_years_for_quantile_evolution)
 
     def relative_change_in_return_level(self, initial_year, final_year):
         return_level_values = []
@@ -169,10 +138,7 @@ class AbstractGevTrendTest(object):
 
     @property
     def test_trend_constant_quantile(self):
-        if self.crashed:
-            return 0.0
-        else:
-            return self.unconstrained_estimator_gev_params.quantile(p=self.quantile_level)
+        return self.unconstrained_estimator_gev_params.quantile(p=self.quantile_level)
 
     # Some class properties for display purpose
 
diff --git a/extreme_trend/trend_test_one_parameter/abstract_comparison_non_stationary_model.py b/extreme_trend/trend_test_one_parameter/abstract_comparison_non_stationary_model.py
index 56a6c6ce..cbf6789a 100644
--- a/extreme_trend/trend_test_one_parameter/abstract_comparison_non_stationary_model.py
+++ b/extreme_trend/trend_test_one_parameter/abstract_comparison_non_stationary_model.py
@@ -10,23 +10,20 @@ import numpy as np
 
 
 class AbstractComparisonNonStationaryModelOneParameter(GevTrendTestOneParameter):
-
-    @property
-    def test_sign(self) -> int:
-        # Test sign correspond to the difference between the 2 likelihoods
-        # Therefore, colors sum up which non stationary model explain best the data
-        return np.sign(self.likelihood_ratio)
+    pass
 
 
 class ComparisonAgainstMu(AbstractComparisonNonStationaryModelOneParameter, GevLocationAndScaleTrendTest):
 
-    def __init__(self, years, maxima, starting_year, quantile_level=EUROCODE_QUANTILE, fit_method=MarginFitMethod.extremes_fevd_mle):
+    def __init__(self, years, maxima, starting_year, quantile_level=EUROCODE_QUANTILE,
+                 fit_method=MarginFitMethod.extremes_fevd_mle):
         super().__init__(years, maxima, starting_year, constrained_model_class=NonStationaryLocationTemporalModel,
                          quantile_level=quantile_level, fit_method=fit_method)
 
 
 class ComparisonAgainstSigma(AbstractComparisonNonStationaryModelOneParameter, GevLocationAndScaleTrendTest):
 
-    def __init__(self, years, maxima, starting_year, quantile_level=EUROCODE_QUANTILE, fit_method=MarginFitMethod.extremes_fevd_mle):
+    def __init__(self, years, maxima, starting_year, quantile_level=EUROCODE_QUANTILE,
+                 fit_method=MarginFitMethod.extremes_fevd_mle):
         super().__init__(years, maxima, starting_year, constrained_model_class=NonStationaryScaleTemporalModel,
                          quantile_level=quantile_level, fit_method=fit_method)
diff --git a/projects/contrasting_trends_in_snow_loads/spatial trends/main_result.py b/projects/contrasting_trends_in_snow_loads/spatial trends/main_constrasting_result.py
similarity index 100%
rename from projects/contrasting_trends_in_snow_loads/spatial trends/main_result.py
rename to projects/contrasting_trends_in_snow_loads/spatial trends/main_constrasting_result.py
diff --git a/test/test_extreme_data/test_meteo_france_data/test_SCM_study.py b/test/test_extreme_data/test_meteo_france_data/test_SCM_study.py
index 71a468cf..cc218d4b 100644
--- a/test/test_extreme_data/test_meteo_france_data/test_SCM_study.py
+++ b/test/test_extreme_data/test_meteo_france_data/test_SCM_study.py
@@ -11,7 +11,7 @@ from extreme_data.meteo_france_data.scm_models_data.safran.safran import SafranS
     SafranPrecipitation, SafranSnowfall3Days, SafranRainfall3Days, SafranNormalizedPreciptationRateOnWetDays
 from extreme_data.meteo_france_data.scm_models_data.utils import SeasonForTheMaxima
 from extreme_data.meteo_france_data.scm_models_data.visualization.main_study_visualizer import \
-    study_iterator_global, SCM_STUDIES, ALL_ALTITUDES
+    study_iterator_global, SCM_STUDIES, ALL_ALTITUDES, SCM_STUDY_CLASS_TO_ABBREVIATION
 from root_utils import get_display_name_from_object_type
 
 
@@ -60,6 +60,12 @@ class TestSCMAllStudy(unittest.TestCase):
         for study_class in study_classes:
             study_class(altitude=altitude, year_min=year_min, year_max=year_max)
 
+    def test_variables(self):
+        for study_class in SCM_STUDY_CLASS_TO_ABBREVIATION.keys():
+            study = study_class(year_max=1959)
+            _ = study.year_to_annual_maxima[1959]
+        self.assertTrue(True)
+
 
 class TestSCMSafranNormalizedPrecipitationRateOnWetDays(unittest.TestCase):
 
@@ -101,6 +107,7 @@ class TestSCMSafranSnowfall(TestSCMStudy):
         self.assertEqual(all_daily_series.shape[1], 23)
         self.assertEqual(all_daily_series.shape[0], 22280)
 
+
 class TestSCMPrecipitation(TestSCMStudy):
 
     def setUp(self) -> None:
diff --git a/test/test_extreme_data/test_meteo_france_data/test_variables.py b/test/test_extreme_data/test_meteo_france_data/test_variables.py
index e69de29b..be047683 100644
--- a/test/test_extreme_data/test_meteo_france_data/test_variables.py
+++ b/test/test_extreme_data/test_meteo_france_data/test_variables.py
@@ -0,0 +1,26 @@
+import unittest
+
+import numpy as np
+
+from extreme_data.meteo_france_data.scm_models_data.safran.safran import SafranSnowfall, Safran
+from extreme_data.meteo_france_data.scm_models_data.safran.safran_variable import SafranTemperatureVariable
+
+
+class TestSafranVariables(unittest.TestCase):
+
+    def setUp(self) -> None:
+        super().setUp()
+        study = SafranSnowfall(year_max=1960)
+        self.dataset = study.year_to_dataset_ordered_dict[1959]
+
+    def test_variables(self):
+        for variable_class in Safran.SAFRAN_VARIABLES:
+            keywords = variable_class.keyword()
+            names = keywords if isinstance(keywords, list) else [keywords]
+            variable_arrays = [np.array(self.dataset.variables[name]) for name in names]
+            variable_class(*variable_arrays)
+        self.assertTrue(True)
+
+
+
+
diff --git a/test/test_extreme_data/test_meteo_france_data/test_visualization.py b/test/test_extreme_data/test_meteo_france_data/test_visualization.py
new file mode 100644
index 00000000..fcac9479
--- /dev/null
+++ b/test/test_extreme_data/test_meteo_france_data/test_visualization.py
@@ -0,0 +1,10 @@
+import unittest
+
+from extreme_data.meteo_france_data.scm_models_data.visualization.main_study_visualizer import \
+    SCM_STUDY_CLASS_TO_ABBREVIATION
+
+
+class TestVisualization(unittest.TestCase):
+    pass
+
+
diff --git a/test/test_extreme_fit/test_distribution/test_gev/test_gev_params.py b/test/test_extreme_fit/test_distribution/test_gev/test_gev_params.py
index c20b5782..bf4c2d33 100644
--- a/test/test_extreme_fit/test_distribution/test_gev/test_gev_params.py
+++ b/test/test_extreme_fit/test_distribution/test_gev/test_gev_params.py
@@ -17,6 +17,16 @@ class TestGevParams(unittest.TestCase):
         for quantile_name, p in gev_params.quantile_name_to_p.items():
             self.assertAlmostEqual(- 1 / np.log(p), quantile_dict[quantile_name])
 
+    def test_time_derivative_return_level(self):
+        p = 0.99
+        for mu1 in [-1, 0, 1]:
+            for sigma1 in [1, 10]:
+                for shape in [-1, 0, 1]:
+                    params = GevParams(loc=mu1, scale=sigma1, shape=shape)
+                    quantile = params.quantile(p)
+                    time_derivative = params.time_derivative_of_return_level(p, mu1, sigma1)
+                    self.assertEqual(quantile, time_derivative)
+
     def test_negative_scale(self):
         gev_params = GevParams(loc=1.0, shape=1.0, scale=-1.0)
         for p in [0.1, 0.5, 0.9]:
diff --git a/test/test_projects/test_contrasting/test_two_fold_fit.py b/test/test_projects/test_contrasting/test_two_fold_fit.py
index 27e7421a..7051fd87 100644
--- a/test/test_projects/test_contrasting/test_two_fold_fit.py
+++ b/test/test_projects/test_contrasting/test_two_fold_fit.py
@@ -37,6 +37,7 @@ class TestTwoFoldFit(unittest.TestCase):
             best_model_class = two_fold_fit.massif_name_to_best_model()['Vercors']
         except AssertionError as e:
             self.assertTrue(False, msg=e.__str__())
+            best_model_class = None
         self.assertEqual(best_model_class, LinearLocationAllDimsMarginModel)
 
 
-- 
GitLab