From 1a6c3690856924390c60360fac32487e9dcaf475 Mon Sep 17 00:00:00 2001
From: Le Roux Erwan <erwan.le-roux@irstea.fr>
Date: Mon, 16 Dec 2019 12:28:20 +0100
Subject: [PATCH] [PAPER 1] add gumbel type for mle fit. add
 study_visualizer_for_shape_repartition both for gumbel VS stationary and
 stationary VS non-stationary

---
 .../study_visualization/study_visualizer.py   |  7 +-
 .../main_shape_repartition.py                 | 30 ++++++++
 .../study_visualizer_for_shape_repartition.py | 61 ++++++++++++++++
 ...main_comparison_with_eurocode_examples.py} |  0
 .../main_comparison_with_eurocode_global.py   | 70 +++++++++++++++++++
 .../paper_past_snow_loads/paper_main_utils.py |  8 ++-
 ...dy_visualizer_for_non_stationary_trends.py | 16 +++--
 .../abstract_gev_trend_test.py                |  6 +-
 .../gev_trend_test_one_parameter.py           | 44 ++++++++----
 .../gev_trend_test_two_parameters.py          |  4 +-
 .../abstract_temporal_linear_margin_model.py  | 10 ++-
 .../temporal_linear_margin_models.py          | 14 +++-
 .../result_from_mle_extremes.py               |  8 ++-
 .../model/result_from_model_fit/utils.py      | 10 ++-
 .../test_gev_temporal_extremes_gumbel.py      | 51 ++++++++++++++
 15 files changed, 302 insertions(+), 37 deletions(-)
 create mode 100644 experiment/paper_past_snow_loads/check_mle_convergence_for_trends/main_shape_repartition.py
 create mode 100644 experiment/paper_past_snow_loads/check_mle_convergence_for_trends/study_visualizer_for_shape_repartition.py
 rename experiment/paper_past_snow_loads/discussion_data_comparison_with_eurocode/{main_comparison_with_eurocode.py => main_comparison_with_eurocode_examples.py} (100%)
 create mode 100644 experiment/paper_past_snow_loads/discussion_data_comparison_with_eurocode/main_comparison_with_eurocode_global.py
 create mode 100644 test/test_extreme_fit/test_distribution/test_gev/test_gev_temporal_extremes_gumbel.py

diff --git a/experiment/meteo_france_data/scm_models_data/visualization/study_visualization/study_visualizer.py b/experiment/meteo_france_data/scm_models_data/visualization/study_visualization/study_visualizer.py
index 132f14bb..d6da42c2 100644
--- a/experiment/meteo_france_data/scm_models_data/visualization/study_visualization/study_visualizer.py
+++ b/experiment/meteo_france_data/scm_models_data/visualization/study_visualization/study_visualizer.py
@@ -632,7 +632,10 @@ class StudyVisualizer(VisualizationParameters):
         # Display the graph of the max on top
         if ax is None:
             ax = plt.gca()
-        x, y = self.smooth_maxima_x_y(massif_names.index(massif_name))
+        if massif_name is None:
+            x, y = self.study.ordered_years, self.study.observations_annual_maxima.df_maxima_gev.mean(axis=0)
+        else:
+            x, y = self.smooth_maxima_x_y(massif_names.index(massif_name))
         ax.plot(x, y, color=color, linewidth=5, label=label, linestyle=linestyle)
         # ax.set_ylabel('{} (in {})'.format(snow_abbreviation, self.study.variable_unit), color=color, fontsize=15)
 
@@ -642,7 +645,7 @@ class StudyVisualizer(VisualizationParameters):
         self.plot_name = plot_name
         ax.set_ylabel('{} ({})'.format(snow_abbreviation, self.study.variable_unit), fontsize=15)
         ax.set_xlabel('years', fontsize=15)
-        if label is None:
+        if label is None and massif_name is not None:
             ax.set_title('{} at {} m'.format(massif_name, altitude))
         if last_plot:
             ax.legend()
diff --git a/experiment/paper_past_snow_loads/check_mle_convergence_for_trends/main_shape_repartition.py b/experiment/paper_past_snow_loads/check_mle_convergence_for_trends/main_shape_repartition.py
new file mode 100644
index 00000000..26988557
--- /dev/null
+++ b/experiment/paper_past_snow_loads/check_mle_convergence_for_trends/main_shape_repartition.py
@@ -0,0 +1,30 @@
+from experiment.meteo_france_data.scm_models_data.crocus.crocus import CrocusSnowLoadTotal
+from experiment.paper_past_snow_loads.check_mle_convergence_for_trends.study_visualizer_for_shape_repartition import \
+    StudyVisualizerForShape, StudyVisualizerGumbel
+from experiment.paper_past_snow_loads.paper_main_utils import load_altitude_to_visualizer
+
+
+def main_shape_repartition(altitudes, massif_names=None,
+                           non_stationary_uncertainty=None, uncertainty_methods=None,
+                           study_class=CrocusSnowLoadTotal,
+                           study_visualizer_class=StudyVisualizerForShape, save_to_file=True):
+    # Load altitude to visualizer
+    altitude_to_visualizer = load_altitude_to_visualizer(altitudes, massif_names, non_stationary_uncertainty,
+                                                         study_class, uncertainty_methods,
+                                                         study_visualizer_class=study_visualizer_class,
+                                                         save_to_file=save_to_file)
+    altitudes_for_plot_trend = altitudes  #[900, 1800, 2700]
+    visualizers_for_altitudes = [visualizer
+                                 for altitude, visualizer in altitude_to_visualizer.items()
+                                 if altitude in altitudes_for_plot_trend]
+    max_abs_tdrl = max([visualizer.max_abs_change for visualizer in visualizers_for_altitudes])
+    for visualizer in visualizers_for_altitudes:
+        # visualizer.plot_trends(max_abs_tdrl, add_colorbar=visualizer.study.altitude == 2700)
+        visualizer.plot_trends(max_abs_tdrl, add_colorbar=True)
+
+
+if __name__ == '__main__':
+    # main_shape_repartition([900], save_to_file=False)
+    # main_shape_repartition([900, 1800, 2700])
+    # main_shape_repartition([300, 600, 900, 1200, 1500, 1800, 2700])
+    main_shape_repartition([900], study_visualizer_class=StudyVisualizerGumbel)
diff --git a/experiment/paper_past_snow_loads/check_mle_convergence_for_trends/study_visualizer_for_shape_repartition.py b/experiment/paper_past_snow_loads/check_mle_convergence_for_trends/study_visualizer_for_shape_repartition.py
new file mode 100644
index 00000000..d7b62f8e
--- /dev/null
+++ b/experiment/paper_past_snow_loads/check_mle_convergence_for_trends/study_visualizer_for_shape_repartition.py
@@ -0,0 +1,61 @@
+from cached_property import cached_property
+
+from experiment.meteo_france_data.scm_models_data.abstract_study import AbstractStudy
+from experiment.paper_past_snow_loads.study_visualizer_for_non_stationary_trends import \
+    StudyVisualizerForNonStationaryTrends
+from experiment.trend_analysis.abstract_score import MeanScore
+from experiment.trend_analysis.univariate_test.gev_trend_test_one_parameter import GevLocationTrendTest, \
+    GevStationaryVersusGumbel
+
+
+class StudyVisualizerForShape(StudyVisualizerForNonStationaryTrends):
+
+    def __init__(self, study: AbstractStudy, show=True, save_to_file=False, only_one_graph=False, only_first_row=False,
+                 vertical_kde_plot=False, year_for_kde_plot=None, plot_block_maxima_quantiles=False,
+                 temporal_non_stationarity=False, transformation_class=None, verbose=False, multiprocessing=False,
+                 complete_non_stationary_trend_analysis=False, normalization_under_one_observations=True,
+                 score_class=MeanScore, uncertainty_methods=None, non_stationary_contexts=None,
+                 uncertainty_massif_names=None, effective_temporal_covariate=2017, relative_change_trend_plot=True):
+        super().__init__(study, show, save_to_file, only_one_graph, only_first_row, vertical_kde_plot,
+                         year_for_kde_plot, plot_block_maxima_quantiles, temporal_non_stationarity,
+                         transformation_class, verbose, multiprocessing, complete_non_stationary_trend_analysis,
+                         normalization_under_one_observations, score_class, uncertainty_methods,
+                         non_stationary_contexts, uncertainty_massif_names, effective_temporal_covariate,
+                         relative_change_trend_plot)
+
+    @cached_property
+    def massif_name_to_unconstrained_shape_parameter(self):
+        return {m: t.unconstrained_estimator_gev_params.shape
+                for m, t in self.massif_name_to_minimized_aic_non_stationary_trend_test.items()}
+
+    @cached_property
+    def massif_name_to_change_value(self):
+        print(self.massif_name_to_unconstrained_shape_parameter)
+        return self.massif_name_to_unconstrained_shape_parameter
+
+    @property
+    def label(self):
+        return 'Shape parameter value'
+
+    @property
+    def graduation(self):
+        return 0.1
+
+
+class StudyVisualizerGumbel(StudyVisualizerForNonStationaryTrends):
+
+    def __init__(self, study: AbstractStudy, show=True, save_to_file=False, only_one_graph=False, only_first_row=False,
+                 vertical_kde_plot=False, year_for_kde_plot=None, plot_block_maxima_quantiles=False,
+                 temporal_non_stationarity=False, transformation_class=None, verbose=False, multiprocessing=False,
+                 complete_non_stationary_trend_analysis=False, normalization_under_one_observations=True,
+                 score_class=MeanScore, uncertainty_methods=None, non_stationary_contexts=None,
+                 uncertainty_massif_names=None, effective_temporal_covariate=2017, relative_change_trend_plot=True):
+        super().__init__(study, show, save_to_file, only_one_graph, only_first_row, vertical_kde_plot,
+                         year_for_kde_plot, plot_block_maxima_quantiles, temporal_non_stationarity,
+                         transformation_class, verbose, multiprocessing, complete_non_stationary_trend_analysis,
+                         normalization_under_one_observations, score_class, uncertainty_methods,
+                         non_stationary_contexts, uncertainty_massif_names, effective_temporal_covariate,
+                         relative_change_trend_plot)
+        # Assign default argument for the non stationary trends
+        self.non_stationary_trend_test = [GevStationaryVersusGumbel]
+        self.non_stationary_trend_test_to_marker = dict(zip(self.non_stationary_trend_test, ["o"]))
diff --git a/experiment/paper_past_snow_loads/discussion_data_comparison_with_eurocode/main_comparison_with_eurocode.py b/experiment/paper_past_snow_loads/discussion_data_comparison_with_eurocode/main_comparison_with_eurocode_examples.py
similarity index 100%
rename from experiment/paper_past_snow_loads/discussion_data_comparison_with_eurocode/main_comparison_with_eurocode.py
rename to experiment/paper_past_snow_loads/discussion_data_comparison_with_eurocode/main_comparison_with_eurocode_examples.py
diff --git a/experiment/paper_past_snow_loads/discussion_data_comparison_with_eurocode/main_comparison_with_eurocode_global.py b/experiment/paper_past_snow_loads/discussion_data_comparison_with_eurocode/main_comparison_with_eurocode_global.py
new file mode 100644
index 00000000..22b5593b
--- /dev/null
+++ b/experiment/paper_past_snow_loads/discussion_data_comparison_with_eurocode/main_comparison_with_eurocode_global.py
@@ -0,0 +1,70 @@
+from experiment.meteo_france_data.scm_models_data.crocus.crocus import CrocusDepth
+from experiment.meteo_france_data.scm_models_data.crocus.crocus_variables import CrocusDepthVariable
+from experiment.meteo_france_data.scm_models_data.visualization.study_visualization.main_study_visualizer import \
+    study_iterator_global, SCM_STUDY_CLASS_TO_ABBREVIATION, snow_density_str
+from experiment.meteo_france_data.scm_models_data.visualization.study_visualization.study_visualizer import \
+    StudyVisualizer
+import matplotlib.pyplot as plt
+
+from experiment.paper_past_snow_loads.discussion_data_comparison_with_eurocode.crocus_study_comparison_with_eurocode import \
+    CrocusDifferenceSnowLoad, \
+    CrocusSnowDensityAtMaxofSwe, CrocusDifferenceSnowLoadRescaledAndEurocodeToSeeSynchronization, \
+    CrocusSnowDepthAtMaxofSwe, CrocusSnowDepthDifference
+from experiment.paper_past_snow_loads.paper_utils import dpi_paper1_figure
+
+
+def max_graph_annual_maxima_comparison():
+    """
+    We choose these massif because each represents a different eurocode region
+    we also choose them because they belong to a different climatic area
+    :return:
+    """
+    save_to_file = False
+    study_classes = [
+                        CrocusSnowDensityAtMaxofSwe,
+                        CrocusDifferenceSnowLoad,
+                        CrocusSnowDepthDifference,
+                    ][:]
+    study_class_to_ylim_and_yticks = {
+        CrocusSnowDensityAtMaxofSwe: ([150, 500], [50 * i for i in range(3, 11)]),
+        CrocusDifferenceSnowLoad: ([0, 10], [2 * i for i in range(0, 6)]),
+        CrocusSnowDepthDifference: ([0, 0.6], [0.2 * i for i in range(0, 4)]),
+    }
+    for study_class in study_classes:
+        ylim, yticks = study_class_to_ylim_and_yticks[study_class]
+
+        marker_altitude = [
+                              ('deepskyblue', 900),
+                              ('dodgerblue', 1800),
+                              ('blue', 2700),
+                          ][:]
+        ax = plt.gca()
+        for color, altitude in marker_altitude:
+            for study in study_iterator_global([study_class], altitudes=[altitude]):
+                study_visualizer = StudyVisualizer(study, save_to_file=save_to_file,
+                                                   verbose=True,
+                                                   multiprocessing=True)
+                snow_abbreviation = SCM_STUDY_CLASS_TO_ABBREVIATION[study_class]
+                nb_massifs = len(study_visualizer.study.study_massif_names)
+                label = 'Mean value at {}m (out of {} massifs)'.format(altitude, nb_massifs)
+                study_visualizer.visualize_max_graphs_poster(None, altitude, snow_abbreviation, color, label,
+                                                             False, ax)
+
+                last_plot = altitude == 2700
+                if last_plot:
+                    # if study_class == CrocusSnowDensityAtMaxofSwe:
+                    #     label = '{} for French standards'.format(snow_density_str)
+                    #     snow_density_eurocode = [150 for _ in study.ordered_years]
+                    #     ax.plot(study.ordered_years, snow_density_eurocode, color='k', label=label)
+                    ax.legend()
+                    tight_pad = {'h_pad': 0.2}
+                    ax.set_ylim(ylim)
+                    ax.set_xlim([1957, 2018])
+                    ax.yaxis.set_ticks(yticks)
+                    study_visualizer.show_or_save_to_file(no_title=True, tight_layout=True,
+                                                          tight_pad=tight_pad, dpi=dpi_paper1_figure)
+                    ax.clear()
+
+
+if __name__ == '__main__':
+    max_graph_annual_maxima_comparison()
diff --git a/experiment/paper_past_snow_loads/paper_main_utils.py b/experiment/paper_past_snow_loads/paper_main_utils.py
index 94493f2f..510dcdec 100644
--- a/experiment/paper_past_snow_loads/paper_main_utils.py
+++ b/experiment/paper_past_snow_loads/paper_main_utils.py
@@ -4,11 +4,13 @@ from experiment.paper_past_snow_loads.study_visualizer_for_non_stationary_trends
     StudyVisualizerForNonStationaryTrends
 
 
-def load_altitude_to_visualizer(altitudes, massif_names, non_stationary_uncertainty, study_class, uncertainty_methods):
+def load_altitude_to_visualizer(altitudes, massif_names, non_stationary_uncertainty, study_class, uncertainty_methods,
+                                study_visualizer_class=StudyVisualizerForNonStationaryTrends,
+                                save_to_file=True):
     altitude_to_visualizer = OrderedDict()
     for altitude in altitudes:
-        altitude_to_visualizer[altitude] = StudyVisualizerForNonStationaryTrends(
-            study=study_class(altitude=altitude), multiprocessing=True, save_to_file=True,
+        altitude_to_visualizer[altitude] = study_visualizer_class(
+            study=study_class(altitude=altitude), multiprocessing=True, save_to_file=save_to_file,
             uncertainty_massif_names=massif_names, uncertainty_methods=uncertainty_methods,
             non_stationary_contexts=non_stationary_uncertainty)
     return altitude_to_visualizer
diff --git a/experiment/paper_past_snow_loads/study_visualizer_for_non_stationary_trends.py b/experiment/paper_past_snow_loads/study_visualizer_for_non_stationary_trends.py
index 4fa5978d..976efe49 100644
--- a/experiment/paper_past_snow_loads/study_visualizer_for_non_stationary_trends.py
+++ b/experiment/paper_past_snow_loads/study_visualizer_for_non_stationary_trends.py
@@ -133,7 +133,6 @@ class StudyVisualizerForNonStationaryTrends(StudyVisualizer):
         ax.get_xaxis().set_visible(True)
         ax.set_xticks([])
         ax.set_xlabel('Altitude = {}m'.format(self.study.altitude), fontsize=15)
-
         self.plot_name = 'tdlr_trends'
         self.show_or_save_to_file(add_classic_title=False, tight_layout=True, no_title=True,
                                   dpi=500)
@@ -157,16 +156,20 @@ class StudyVisualizerForNonStationaryTrends(StudyVisualizer):
 
     @property
     def ticks_values_and_labels(self):
-        graduation = 10 if self.relative_change_trend_plot else 0.2
         positive_ticks = []
-        tick = graduation
+        tick = self.graduation
         while tick < self._max_abs_change:
             positive_ticks.append(round(tick, 1))
-            tick += graduation
+            tick += self.graduation
         all_ticks_labels = [-t for t in positive_ticks] + [0] + positive_ticks
         ticks_values = [((t / self._max_abs_change) + 1) / 2 for t in all_ticks_labels]
         return ticks_values, all_ticks_labels
 
+    @property
+    def graduation(self):
+        graduation = 10 if self.relative_change_trend_plot else 0.2
+        return graduation
+
     @cached_property
     def massif_name_to_tdrl_value(self):
         return {m: t.time_derivative_of_return_level for m, t in
@@ -291,3 +294,8 @@ class StudyVisualizerForNonStationaryTrends(StudyVisualizer):
                        uncertainty.confidence_interval[1] > eurocode)
                       for eurocode, uncertainty in eurocode_and_uncertainties])
         return 100 * np.mean(a, axis=0)
+
+
+
+
+
diff --git a/experiment/trend_analysis/univariate_test/abstract_gev_trend_test.py b/experiment/trend_analysis/univariate_test/abstract_gev_trend_test.py
index 71ff1725..8d3b47b0 100644
--- a/experiment/trend_analysis/univariate_test/abstract_gev_trend_test.py
+++ b/experiment/trend_analysis/univariate_test/abstract_gev_trend_test.py
@@ -132,13 +132,13 @@ class AbstractGevTrendTest(AbstractUnivariateTest):
                                                                               AbstractCoordinates.COORDINATE_T)
 
     @cached_property
-    def non_stationary_constant_gev_params(self) -> GevParams:
+    def unconstrained_estimator_gev_params(self) -> GevParams:
         # Constant parameters correspond to the gev params in 1958
         return self.unconstrained_estimator.margin_function_from_fit.get_gev_params(coordinate=np.array([1958]),
                                                                                     is_transformed=False)
 
     @cached_property
-    def stationary_constant_gev_params(self) -> GevParams:
+    def constrained_estimator_gev_params(self) -> GevParams:
         # Constant parameters correspond to any gev params
         return self.constrained_estimator.margin_function_from_fit.get_gev_params(coordinate=np.array([1958]),
                                                                                   is_transformed=False)
@@ -192,4 +192,4 @@ class AbstractGevTrendTest(AbstractUnivariateTest):
         if self.crashed:
             return 0.0
         else:
-            return self.non_stationary_constant_gev_params.quantile(p=self.quantile_level)
+            return self.unconstrained_estimator_gev_params.quantile(p=self.quantile_level)
diff --git a/experiment/trend_analysis/univariate_test/gev_trend_test_one_parameter.py b/experiment/trend_analysis/univariate_test/gev_trend_test_one_parameter.py
index 8af03890..5a89e207 100644
--- a/experiment/trend_analysis/univariate_test/gev_trend_test_one_parameter.py
+++ b/experiment/trend_analysis/univariate_test/gev_trend_test_one_parameter.py
@@ -1,28 +1,35 @@
 from experiment.eurocode_data.utils import EUROCODE_QUANTILE
 from experiment.trend_analysis.univariate_test.abstract_gev_trend_test import AbstractGevTrendTest
 from extreme_fit.model.margin_model.linear_margin_model.temporal_linear_margin_models import \
-    NonStationaryLocationTemporalModel, NonStationaryScaleTemporalModel, NonStationaryShapeTemporalModel
+    NonStationaryLocationTemporalModel, NonStationaryScaleTemporalModel, NonStationaryShapeTemporalModel, \
+    StationaryTemporalModel, GumbelTemporalModel
 from extreme_fit.distribution.gev.gev_params import GevParams
 
 
 class GevTrendTestOneParameter(AbstractGevTrendTest):
 
-    def __init__(self, years, maxima, starting_year, unconstrained_model_class, gev_param_name, quantile_level=EUROCODE_QUANTILE):
-        super().__init__(years, maxima, starting_year,
-                         unconstrained_model_class=unconstrained_model_class,
-                         quantile_level=quantile_level)
-        self.gev_param_name = gev_param_name
-
     @property
     def degree_freedom_chi2(self) -> int:
         return 1
 
+
+class GevTrendTestOneParameterAgainstStationary(GevTrendTestOneParameter):
+
+    def __init__(self, years, maxima, starting_year, unconstrained_model_class, gev_param_name,
+                 quantile_level=EUROCODE_QUANTILE,
+                 constrained_model_class=StationaryTemporalModel):
+        super().__init__(years, maxima, starting_year,
+                         unconstrained_model_class=unconstrained_model_class,
+                         quantile_level=quantile_level,
+                         constrained_model_class=constrained_model_class)
+        self.gev_param_name = gev_param_name
+
     @property
     def non_stationary_linear_coef(self):
         return self.get_non_stationary_linear_coef(gev_param_name=self.gev_param_name)
 
 
-class GevLocationTrendTest(GevTrendTestOneParameter):
+class GevLocationTrendTest(GevTrendTestOneParameterAgainstStationary):
 
     def __init__(self, years, maxima, starting_year, quantile_level=EUROCODE_QUANTILE):
         super().__init__(years, maxima, starting_year,
@@ -31,12 +38,12 @@ class GevLocationTrendTest(GevTrendTestOneParameter):
                          quantile_level=quantile_level)
 
     def _slope_strength(self):
-        return self.non_stationary_constant_gev_params.time_derivative_of_return_level(p=self.quantile_level,
+        return self.unconstrained_estimator_gev_params.time_derivative_of_return_level(p=self.quantile_level,
                                                                                        mu1=self.non_stationary_linear_coef)
 
     @property
     def mean_difference_same_sign_as_slope_strenght(self) -> bool:
-        zeta0 = self.non_stationary_constant_gev_params.shape
+        zeta0 = self.unconstrained_estimator_gev_params.shape
         mean_difference = self.mean_difference(zeta0=zeta0, mu1=self.non_stationary_linear_coef)
         return self.same_sign(mean_difference, self._slope_strength())
 
@@ -45,7 +52,7 @@ class GevLocationTrendTest(GevTrendTestOneParameter):
         return False
 
 
-class GevScaleTrendTest(GevTrendTestOneParameter):
+class GevScaleTrendTest(GevTrendTestOneParameterAgainstStationary):
 
     def __init__(self, years, maxima, starting_year, quantile_level=EUROCODE_QUANTILE):
         super().__init__(years, maxima, starting_year,
@@ -54,13 +61,13 @@ class GevScaleTrendTest(GevTrendTestOneParameter):
                          quantile_level=quantile_level)
 
     def _slope_strength(self):
-        return self.non_stationary_constant_gev_params.time_derivative_of_return_level(
+        return self.unconstrained_estimator_gev_params.time_derivative_of_return_level(
             p=self.quantile_level,
             sigma1=self.non_stationary_linear_coef)
 
     @property
     def mean_difference_same_sign_as_slope_strenght(self) -> bool:
-        zeta0 = self.non_stationary_constant_gev_params.shape
+        zeta0 = self.unconstrained_estimator_gev_params.shape
         mean_difference = self.mean_difference(zeta0=zeta0, sigma1=self.non_stationary_linear_coef)
         return self.same_sign(mean_difference, self._slope_strength())
 
@@ -70,10 +77,19 @@ class GevScaleTrendTest(GevTrendTestOneParameter):
         return self.same_sign(sigma1, self._slope_strength())
 
 
-class GevShapeTrendTest(GevTrendTestOneParameter):
+class GevShapeTrendTest(GevTrendTestOneParameterAgainstStationary):
 
     def __init__(self, years, maxima, starting_year, quantile_level=EUROCODE_QUANTILE):
         super().__init__(years, maxima, starting_year,
                          unconstrained_model_class=NonStationaryShapeTemporalModel,
                          gev_param_name=GevParams.SHAPE,
                          quantile_level=quantile_level)
+
+
+class GevStationaryVersusGumbel(GevTrendTestOneParameter):
+
+    def __init__(self, years, maxima, starting_year, quantile_level=EUROCODE_QUANTILE):
+        super().__init__(years, maxima, starting_year,
+                         unconstrained_model_class=StationaryTemporalModel,
+                         constrained_model_class=GumbelTemporalModel,
+                         quantile_level=quantile_level)
diff --git a/experiment/trend_analysis/univariate_test/gev_trend_test_two_parameters.py b/experiment/trend_analysis/univariate_test/gev_trend_test_two_parameters.py
index 95525524..2bd8a106 100644
--- a/experiment/trend_analysis/univariate_test/gev_trend_test_two_parameters.py
+++ b/experiment/trend_analysis/univariate_test/gev_trend_test_two_parameters.py
@@ -29,13 +29,13 @@ class GevLocationAndScaleTrendTest(GevTrendTestTwoParameters):
         return self.get_non_stationary_linear_coef(gev_param_name=GevParams.SCALE)
 
     def _slope_strength(self):
-        return self.non_stationary_constant_gev_params.time_derivative_of_return_level(p=self.quantile_level,
+        return self.unconstrained_estimator_gev_params.time_derivative_of_return_level(p=self.quantile_level,
                                                                                        mu1=self.mu1,
                                                                                        sigma1=self.sigma1)
 
     @property
     def mean_difference_same_sign_as_slope_strenght(self) -> bool:
-        zeta0 = self.non_stationary_constant_gev_params.shape
+        zeta0 = self.unconstrained_estimator_gev_params.shape
         mean_difference = self.mean_difference(zeta0=zeta0, mu1=self.mu1, sigma1=self.sigma1)
         return self.same_sign(mean_difference, self._slope_strength())
 
diff --git a/extreme_fit/model/margin_model/linear_margin_model/abstract_temporal_linear_margin_model.py b/extreme_fit/model/margin_model/linear_margin_model/abstract_temporal_linear_margin_model.py
index 3dd02216..08700dfa 100644
--- a/extreme_fit/model/margin_model/linear_margin_model/abstract_temporal_linear_margin_model.py
+++ b/extreme_fit/model/margin_model/linear_margin_model/abstract_temporal_linear_margin_model.py
@@ -25,8 +25,10 @@ class AbstractTemporalLinearMarginModel(LinearMarginModel):
 
     def __init__(self, coordinates: AbstractCoordinates, use_start_value=False, params_start_fit=None,
                  params_sample=None, starting_point=None, fit_method=TemporalMarginFitMethod.is_mev_gev_fit,
-                 nb_iterations_for_bayesian_fit=5000, params_start_fit_bayesian=None):
+                 nb_iterations_for_bayesian_fit=5000, params_start_fit_bayesian=None,
+                 type_for_MLE="GEV"):
         super().__init__(coordinates, use_start_value, params_start_fit, params_sample, starting_point)
+        self.type_for_mle = type_for_MLE
         self.params_start_fit_bayesian = params_start_fit_bayesian
         self.nb_iterations_for_bayesian_fit = nb_iterations_for_bayesian_fit
         assert isinstance(fit_method, TemporalMarginFitMethod)
@@ -59,10 +61,12 @@ class AbstractTemporalLinearMarginModel(LinearMarginModel):
         res = safe_run_r_estimator(function=r('fevd_fixed'),
                                    x=x,
                                    data=y,
-                                   method='MLE',
+                                   type=self.type_for_mle,
+                                   method="MLE",
                                    **r_type_argument_kwargs
                                    )
-        return ResultFromMleExtremes(res, self.margin_function_start_fit.gev_param_name_to_dims)
+        return ResultFromMleExtremes(res, self.margin_function_start_fit.gev_param_name_to_dims,
+                                     type_for_mle=self.type_for_mle)
 
     def extremes_fevd_bayesian_fit(self, x, df_coordinates_temp) -> AbstractResultFromExtremes:
         r_type_argument_kwargs, y = self.extreme_arguments(df_coordinates_temp)
diff --git a/extreme_fit/model/margin_model/linear_margin_model/temporal_linear_margin_models.py b/extreme_fit/model/margin_model/linear_margin_model/temporal_linear_margin_models.py
index 33d267bc..e6a54751 100644
--- a/extreme_fit/model/margin_model/linear_margin_model/temporal_linear_margin_models.py
+++ b/extreme_fit/model/margin_model/linear_margin_model/temporal_linear_margin_models.py
@@ -1,7 +1,8 @@
 from extreme_fit.model.margin_model.linear_margin_model.abstract_temporal_linear_margin_model import \
-    AbstractTemporalLinearMarginModel
+    AbstractTemporalLinearMarginModel, TemporalMarginFitMethod
 from extreme_fit.model.utils import r
 from extreme_fit.distribution.gev.gev_params import GevParams
+from spatio_temporal_dataset.coordinates.abstract_coordinates import AbstractCoordinates
 
 
 class StationaryTemporalModel(AbstractTemporalLinearMarginModel):
@@ -10,6 +11,15 @@ class StationaryTemporalModel(AbstractTemporalLinearMarginModel):
         super().load_margin_functions({})
 
 
+class GumbelTemporalModel(StationaryTemporalModel):
+
+    def __init__(self, coordinates: AbstractCoordinates, use_start_value=False, params_start_fit=None,
+                 params_sample=None, starting_point=None, fit_method=TemporalMarginFitMethod.is_mev_gev_fit,
+                 nb_iterations_for_bayesian_fit=5000, params_start_fit_bayesian=None):
+        super().__init__(coordinates, use_start_value, params_start_fit, params_sample, starting_point, fit_method,
+                         nb_iterations_for_bayesian_fit, params_start_fit_bayesian, type_for_MLE="Gumbel")
+
+
 class NonStationaryLocationTemporalModel(AbstractTemporalLinearMarginModel):
 
     def load_margin_functions(self, gev_param_name_to_dims=None):
@@ -62,4 +72,4 @@ class NonStationaryLocationAndScaleTemporalModel(AbstractTemporalLinearMarginMod
 
     @property
     def sigl(self):
-        return 1
\ No newline at end of file
+        return 1
diff --git a/extreme_fit/model/result_from_model_fit/result_from_extremes/result_from_mle_extremes.py b/extreme_fit/model/result_from_model_fit/result_from_extremes/result_from_mle_extremes.py
index 56a167bf..9fa9b18f 100644
--- a/extreme_fit/model/result_from_model_fit/result_from_extremes/result_from_mle_extremes.py
+++ b/extreme_fit/model/result_from_model_fit/result_from_extremes/result_from_mle_extremes.py
@@ -1,5 +1,6 @@
 import numpy as np
 import rpy2
+from rpy2 import robjects
 
 from extreme_fit.model.result_from_model_fit.result_from_extremes.abstract_result_from_extremes import \
     AbstractResultFromExtremes
@@ -11,12 +12,17 @@ from extreme_fit.model.utils import r
 
 class ResultFromMleExtremes(AbstractResultFromExtremes):
 
+    def __init__(self, result_from_fit: robjects.ListVector, gev_param_name_to_dim=None,
+                 type_for_mle="GEV") -> None:
+        super().__init__(result_from_fit, gev_param_name_to_dim)
+        self.type_for_mle = type_for_mle
+
     @property
     def margin_coef_ordered_dict(self):
         values = self.name_to_value['results']
         d = self.get_python_dictionary(values)
         values = {i: param for i, param in enumerate(np.array(d['par']))}
-        return get_margin_coef_ordered_dict(self.gev_param_name_to_dim, values)
+        return get_margin_coef_ordered_dict(self.gev_param_name_to_dim, values, self.type_for_mle)
 
     def _confidence_interval_method(self, common_kwargs, ci_method, return_period):
         method_name = ci_method_to_method_name[ci_method]
diff --git a/extreme_fit/model/result_from_model_fit/utils.py b/extreme_fit/model/result_from_model_fit/utils.py
index 7dabcc92..03d95a07 100644
--- a/extreme_fit/model/result_from_model_fit/utils.py
+++ b/extreme_fit/model/result_from_model_fit/utils.py
@@ -11,15 +11,19 @@ def convertFloatVector_to_float(f):
     return np.array(f)[0]
 
 
-def get_margin_coef_ordered_dict(gev_param_name_to_dim, mle_values):
+def get_margin_coef_ordered_dict(gev_param_name_to_dim, mle_values, type_for_mle="GEV"):
     assert gev_param_name_to_dim is not None
     # Build the Coeff dict from gev_param_name_to_dim
     coef_dict = OrderedDict()
     i = 0
     for gev_param_name in GevParams.PARAM_NAMES:
-        # Add intercept
+        # Add intercept (i.e. stationary parameter)
         intercept_coef_name = LinearCoef.coef_template_str(gev_param_name, LinearCoef.INTERCEPT_NAME).format(1)
-        coef_dict[intercept_coef_name] = mle_values[i]
+        if type_for_mle == "Gumbel" and i == 2:
+            coef_value = 0
+        else:
+            coef_value = mle_values[i]
+        coef_dict[intercept_coef_name] = coef_value
         i += 1
         # Add a potential linear temporal trend
         if gev_param_name in gev_param_name_to_dim:
diff --git a/test/test_extreme_fit/test_distribution/test_gev/test_gev_temporal_extremes_gumbel.py b/test/test_extreme_fit/test_distribution/test_gev/test_gev_temporal_extremes_gumbel.py
new file mode 100644
index 00000000..2996bb21
--- /dev/null
+++ b/test/test_extreme_fit/test_distribution/test_gev/test_gev_temporal_extremes_gumbel.py
@@ -0,0 +1,51 @@
+import unittest
+
+import numpy as np
+import pandas as pd
+
+from experiment.trend_analysis.univariate_test.abstract_gev_trend_test import fitted_linear_margin_estimator
+from extreme_fit.model.margin_model.linear_margin_model.abstract_temporal_linear_margin_model import \
+    TemporalMarginFitMethod
+from extreme_fit.model.margin_model.linear_margin_model.temporal_linear_margin_models import StationaryTemporalModel, \
+    NonStationaryLocationTemporalModel, NonStationaryLocationAndScaleTemporalModel, GumbelTemporalModel
+from extreme_fit.model.utils import r, set_seed_r
+from spatio_temporal_dataset.coordinates.abstract_coordinates import AbstractCoordinates
+from spatio_temporal_dataset.coordinates.temporal_coordinates.abstract_temporal_coordinates import \
+    AbstractTemporalCoordinates
+from spatio_temporal_dataset.dataset.abstract_dataset import AbstractDataset
+from spatio_temporal_dataset.spatio_temporal_observations.abstract_spatio_temporal_observations import \
+    AbstractSpatioTemporalObservations
+
+
+class TestGevTemporalExtremesGumbel(unittest.TestCase):
+
+    def setUp(self) -> None:
+        set_seed_r()
+        r("""
+        N <- 50
+        loc = 0; scale = 1; shape <- 0
+        x_gev <- rgev(N, loc = loc, scale = scale, shape = shape)
+        start_loc = 0; start_scale = 1; start_shape = 1
+        """)
+        # Compute the stationary temporal margin with isMev
+        self.start_year = 0
+        df = pd.DataFrame({AbstractCoordinates.COORDINATE_T: range(self.start_year, self.start_year + 50)})
+        self.coordinates = AbstractTemporalCoordinates.from_df(df)
+        df2 = pd.DataFrame(data=np.array(r['x_gev']), index=df.index)
+        observations = AbstractSpatioTemporalObservations(df_maxima_gev=df2)
+        self.dataset = AbstractDataset(observations=observations, coordinates=self.coordinates)
+
+    def test_gev_temporal_margin_fit(self):
+        # Create estimator
+        estimator = fitted_linear_margin_estimator(GumbelTemporalModel,
+                                                   self.coordinates, self.dataset,
+                                                   starting_year=0,
+                                                   fit_method=TemporalMarginFitMethod.extremes_fevd_mle)
+        ref = {'loc': -0.0862185692806497, 'scale': 1.0818465357627252, 'shape': 0}
+        for year in range(1, 3):
+            mle_params_estimated = estimator.margin_function_from_fit.get_gev_params(np.array([year])).to_dict()
+            for key in ref.keys():
+                self.assertAlmostEqual(ref[key], mle_params_estimated[key], places=3)
+
+if __name__ == '__main__':
+    unittest.main()
-- 
GitLab