From d61ff6d917df639ff8b8ef91e7b2924a9b0e0b52 Mon Sep 17 00:00:00 2001
From: Le Roux Erwan <erwan.le-roux@irstea.fr>
Date: Mon, 15 Jul 2019 10:53:54 +0200
Subject: [PATCH] [SCM MODEL] some checks with Safran. add RecentSWE variable.
 rename SWE to TotalSWE

---
 .../scm_models_data/abstract_study.py         |  2 +-
 .../scm_models_data/crocus/crocus.py          | 24 +++++++++++++------
 .../crocus/crocus_variables.py                | 12 ++++++++--
 .../scm_models_data/safran/safran.py          | 19 ++++++++++++++-
 .../scm_models_data/safran/safran_variable.py |  2 +-
 .../scm_models_data/scm_constants.py          |  2 ++
 .../main_study_visualizer.py                  | 18 +++++++-------
 .../test_coordinate_sensitivity.py            |  4 ++--
 test/test_utils.py                            |  4 ++--
 9 files changed, 62 insertions(+), 25 deletions(-)

diff --git a/experiment/meteo_france_data/scm_models_data/abstract_study.py b/experiment/meteo_france_data/scm_models_data/abstract_study.py
index 54dd606d..684c8ee4 100644
--- a/experiment/meteo_france_data/scm_models_data/abstract_study.py
+++ b/experiment/meteo_france_data/scm_models_data/abstract_study.py
@@ -42,7 +42,7 @@ class AbstractStudy(object):
 
     Les fichiers netcdf de SAFRAN et CROCUS sont autodocumentés (on peut les comprendre avec ncdump -h notamment).
     """
-    REANALYSIS_FOLDER = 'alp_flat/reanalysis'
+    REANALYSIS_FOLDER = 'SAFRAN_montagne-CROCUS_2019/alp_flat/reanalysis'
 
     def __init__(self, variable_class: type, altitude: int = 1800, year_min=1000, year_max=3000,
                  multiprocessing=True):
diff --git a/experiment/meteo_france_data/scm_models_data/crocus/crocus.py b/experiment/meteo_france_data/scm_models_data/crocus/crocus.py
index c25c0af6..c5442151 100644
--- a/experiment/meteo_france_data/scm_models_data/crocus/crocus.py
+++ b/experiment/meteo_france_data/scm_models_data/crocus/crocus.py
@@ -2,7 +2,8 @@ import numpy as np
 
 from experiment.meteo_france_data.scm_models_data.abstract_extended_study import AbstractExtendedStudy
 from experiment.meteo_france_data.scm_models_data.abstract_study import AbstractStudy
-from experiment.meteo_france_data.scm_models_data.crocus.crocus_variables import CrocusSweVariable, CrocusDepthVariable
+from experiment.meteo_france_data.scm_models_data.crocus.crocus_variables import CrocusTotalSweVariable, \
+    CrocusDepthVariable, CrocusRecentSweVariable
 
 
 class Crocus(AbstractStudy):
@@ -11,7 +12,7 @@ class Crocus(AbstractStudy):
     """
 
     def __init__(self, variable_class, *args, **kwargs):
-        assert variable_class in [CrocusSweVariable, CrocusDepthVariable]
+        assert variable_class in [CrocusTotalSweVariable, CrocusDepthVariable, CrocusRecentSweVariable]
         super().__init__(variable_class, *args, **kwargs)
         self.model_name = 'Crocus'
 
@@ -25,16 +26,25 @@ class Crocus(AbstractStudy):
         return super().apply_annual_aggregation(time_serie[91:-92, ...])
 
 
-class CrocusSwe(Crocus):
+class CrocusRecentSwe(Crocus):
 
     def __init__(self, *args, **kwargs):
-        Crocus.__init__(self, CrocusSweVariable, *args, **kwargs)
+        Crocus.__init__(self, CrocusRecentSweVariable, *args, **kwargs)
 
     def apply_annual_aggregation(self, time_serie):
         return self.winter_annual_aggregation(time_serie)
 
 
-class ExtendedCrocusSwe(AbstractExtendedStudy, CrocusSwe):
+class CrocusTotalSwe(Crocus):
+
+    def __init__(self, *args, **kwargs):
+        Crocus.__init__(self, CrocusTotalSweVariable, *args, **kwargs)
+
+    def apply_annual_aggregation(self, time_serie):
+        return self.winter_annual_aggregation(time_serie)
+
+
+class ExtendedCrocusTotalSwe(AbstractExtendedStudy, CrocusTotalSwe):
     pass
 
 
@@ -62,6 +72,6 @@ class CrocusDaysWithSnowOnGround(Crocus):
 
 
 if __name__ == '__main__':
-    for study in [CrocusSwe(altitude=900), CrocusSwe(altitude=3000)]:
+    for study in [CrocusRecentSwe(altitude=900)]:
         a = study.year_to_daily_time_serie_array[1960]
-        print(a.shape)
+        print(a)
diff --git a/experiment/meteo_france_data/scm_models_data/crocus/crocus_variables.py b/experiment/meteo_france_data/scm_models_data/crocus/crocus_variables.py
index f52943a3..d9f63484 100644
--- a/experiment/meteo_france_data/scm_models_data/crocus/crocus_variables.py
+++ b/experiment/meteo_france_data/scm_models_data/crocus/crocus_variables.py
@@ -10,8 +10,8 @@ class CrocusVariable(AbstractVariable):
         return self.variable_array
 
 
-class CrocusSweVariable(CrocusVariable):
-    NAME = 'Snow Water Equivalent'
+class CrocusTotalSweVariable(CrocusVariable):
+    NAME = 'Snow Water Equivalent total'
     UNIT = 'kg $m^{-2}$'
 
     @classmethod
@@ -19,6 +19,14 @@ class CrocusSweVariable(CrocusVariable):
         return 'WSN_T_ISBA'
 
 
+class CrocusRecentSweVariable(CrocusTotalSweVariable):
+    NAME = 'Snow Water Equivalent last 3 days'
+
+    @classmethod
+    def keyword(cls):
+        return 'SWE_3DY_ISBA'
+
+
 class CrocusDepthVariable(CrocusVariable):
     NAME = 'Snow Depth'
     UNIT = 'm'
diff --git a/experiment/meteo_france_data/scm_models_data/safran/safran.py b/experiment/meteo_france_data/scm_models_data/safran/safran.py
index 28c47d8a..173fbfb3 100644
--- a/experiment/meteo_france_data/scm_models_data/safran/safran.py
+++ b/experiment/meteo_france_data/scm_models_data/safran/safran.py
@@ -65,7 +65,15 @@ class SafranTemperature(Safran):
 
 if __name__ == '__main__':
     study = SafranSnowfall()
-    d = study.year_to_dataset_ordered_dict[1958]
+    # d = study.year_to_dataset_ordered_dict[1958]
+    # print(d.variables)
+    for i in range(1958, 1959):
+        d = study.year_to_dataset_ordered_dict[i]
+        variable = 'Tair'
+        a = np.mean(np.array(d.variables[variable]), axis=1)
+        d = study.year_to_dataset_ordered_dict[i+1]
+        b = np.mean(np.array(d.variables[variable]), axis=1)
+        print(a[-1] - b[0])
     # print(d.variables['time'])
     # print(study.all_massif_names)
     # print(study.massif_name_to_altitudes)
@@ -75,3 +83,12 @@ if __name__ == '__main__':
     # print(len(d.variables['time']))
     # print(study.year_to_annual_total)
     # print(study.df_annual_total.columns)
+
+
+    # for i in range(1958, 2016):
+    #     d = study.year_to_dataset_ordered_dict[i]
+    #     variable = 'Tair'
+    #     a = np.mean(np.array(d.variables[variable]), axis=1)
+    #     d = study.year_to_dataset_ordered_dict[i+1]
+    #     b = np.mean(np.array(d.variables[variable]), axis=1)
+    #     print(a[-1] - b[0])
diff --git a/experiment/meteo_france_data/scm_models_data/safran/safran_variable.py b/experiment/meteo_france_data/scm_models_data/safran/safran_variable.py
index cfdde8d3..e9861a10 100644
--- a/experiment/meteo_france_data/scm_models_data/safran/safran_variable.py
+++ b/experiment/meteo_france_data/scm_models_data/safran/safran_variable.py
@@ -23,13 +23,13 @@ class SafranSnowfallVariable(AbstractVariable):
 
     NAME = 'Snowfall'
     UNIT = 'kg $m^{-2}$'
+
     # this could have been mm w.e (mm in water equivalent)
 
     @classmethod
     def keyword(cls):
         return 'Snowf'
 
-
     def __init__(self, variable_array, nb_consecutive_days=3):
         super().__init__(variable_array)
         self.nb_consecutive_days_of_snowfall = nb_consecutive_days
diff --git a/experiment/meteo_france_data/scm_models_data/scm_constants.py b/experiment/meteo_france_data/scm_models_data/scm_constants.py
index 4701e531..13659880 100644
--- a/experiment/meteo_france_data/scm_models_data/scm_constants.py
+++ b/experiment/meteo_france_data/scm_models_data/scm_constants.py
@@ -42,6 +42,8 @@ ZS_INT_MASK[-10:] = np.nan
 LONGITUDES = [6.64493, 6.64493, 6.64493, 6.64493, 6.64493, 6.64493, 6.64493, 6.64493, 6.64493, 6.64493, 6.64493, 6.39738, 6.39738, 6.39738, 6.39738, 6.39738, 6.39738, 6.39738, 6.39738, 6.39738, 6.39738, 6.82392, 6.82392, 6.82392, 6.82392, 6.82392, 6.82392, 6.82392, 6.82392, 6.82392, 6.82392, 6.82392, 6.82392, 6.82392, 6.82392, 6.82392, 6.82392, 6.10178, 6.10178, 6.10178, 6.10178, 6.10178, 6.10178, 6.10178, 6.10178, 6.10178, 6.57668, 6.57668, 6.57668, 6.57668, 6.57668, 6.57668, 6.57668, 6.57668, 6.57668, 6.57668, 6.90053, 6.90053, 6.90053, 6.90053, 6.90053, 6.90053, 6.90053, 6.90053, 6.90053, 6.90053, 6.90053, 6.90053, 5.80795, 5.80795, 5.80795, 5.80795, 5.80795, 5.80795, 5.80795, 5.80795, 6.00201, 6.00201, 6.00201, 6.00201, 6.00201, 6.00201, 6.00201, 6.00201, 6.00201, 6.00201, 6.00201, 6.35451, 6.35451, 6.35451, 6.35451, 6.35451, 6.35451, 6.35451, 6.35451, 6.35451, 6.35451, 6.35451, 6.35451, 6.61786, 6.61786, 6.61786, 6.61786, 6.61786, 6.61786, 6.61786, 6.61786, 6.61786, 6.61786, 6.61786, 6.61786, 6.61786, 6.91492, 6.91492, 6.91492, 6.91492, 6.91492, 6.91492, 6.91492, 6.91492, 6.91492, 6.91492, 6.91492, 6.21836, 6.21836, 6.21836, 6.21836, 6.21836, 6.21836, 6.21836, 6.21836, 6.21836, 6.21836, 6.21836, 6.59154, 6.59154, 6.59154, 6.59154, 6.59154, 6.59154, 6.59154, 6.59154, 6.59154, 5.4932, 5.4932, 5.4932, 5.4932, 5.4932, 5.4932, 5.4932, 5.4932, 5.4932, 5.99951, 5.99951, 5.99951, 5.99951, 5.99951, 5.99951, 5.99951, 5.99951, 5.99951, 5.99951, 5.99951, 5.99951, 5.99951, 5.99951, 5.99951, 6.45769, 6.45769, 6.45769, 6.45769, 6.45769, 6.45769, 6.45769, 6.45769, 6.45769, 6.45769, 6.45769, 6.45769, 6.79352, 6.79352, 6.79352, 6.79352, 6.79352, 6.79352, 6.79352, 6.79352, 6.79352, 5.8499, 5.8499, 5.8499, 5.8499, 5.8499, 5.8499, 5.8499, 5.8499, 5.8499, 5.8499, 6.23469, 6.23469, 6.23469, 6.23469, 6.23469, 6.23469, 6.23469, 6.23469, 6.23469, 6.23469, 6.23469, 6.50065, 6.50065, 6.50065, 6.50065, 6.50065, 6.50065, 6.50065, 6.50065, 6.50065, 6.50065, 6.67076, 6.67076, 6.67076, 6.67076, 6.67076, 6.67076, 6.67076, 6.67076, 6.67076, 6.67076, 6.67076, 6.79647, 6.79647, 6.79647, 6.79647, 6.79647, 6.79647, 6.79647, 6.79647, 6.79647, 6.79647, 6.79647, 6.79647, 7.31586, 7.31586, 7.31586, 7.31586, 7.31586, 7.31586, 7.31586, 7.31586, 7.31586, 7.31586, 7.31586, 7.3025, 7.3025, 7.3025, 7.3025, 7.3025, 7.3025, 7.3025, 7.3025, 7.3025, 7.3025]
 LATITUDES = [46.17685, 46.17685, 46.17685, 46.17685, 46.17685, 46.17685, 46.17685, 46.17685, 46.17685, 46.17685, 46.17685, 45.89494, 45.89494, 45.89494, 45.89494, 45.89494, 45.89494, 45.89494, 45.89494, 45.89494, 45.89494, 45.89794, 45.89794, 45.89794, 45.89794, 45.89794, 45.89794, 45.89794, 45.89794, 45.89794, 45.89794, 45.89794, 45.89794, 45.89794, 45.89794, 45.89794, 45.89794, 45.65578, 45.65578, 45.65578, 45.65578, 45.65578, 45.65578, 45.65578, 45.65578, 45.65578, 45.65756, 45.65756, 45.65756, 45.65756, 45.65756, 45.65756, 45.65756, 45.65756, 45.65756, 45.65756, 45.54313, 45.54313, 45.54313, 45.54313, 45.54313, 45.54313, 45.54313, 45.54313, 45.54313, 45.54313, 45.54313, 45.54313, 45.37753, 45.37753, 45.37753, 45.37753, 45.37753, 45.37753, 45.37753, 45.37753, 45.27395, 45.27395, 45.27395, 45.27395, 45.27395, 45.27395, 45.27395, 45.27395, 45.27395, 45.27395, 45.27395, 45.32783, 45.32783, 45.32783, 45.32783, 45.32783, 45.32783, 45.32783, 45.32783, 45.32783, 45.32783, 45.32783, 45.32783, 45.411, 45.411, 45.411, 45.411, 45.411, 45.411, 45.411, 45.411, 45.411, 45.411, 45.411, 45.411, 45.411, 45.26072, 45.26072, 45.26072, 45.26072, 45.26072, 45.26072, 45.26072, 45.26072, 45.26072, 45.26072, 45.26072, 45.11517, 45.11517, 45.11517, 45.11517, 45.11517, 45.11517, 45.11517, 45.11517, 45.11517, 45.11517, 45.11517, 45.01923, 45.01923, 45.01923, 45.01923, 45.01923, 45.01923, 45.01923, 45.01923, 45.01923, 45.00409, 45.00409, 45.00409, 45.00409, 45.00409, 45.00409, 45.00409, 45.00409, 45.00409, 44.94609, 44.94609, 44.94609, 44.94609, 44.94609, 44.94609, 44.94609, 44.94609, 44.94609, 44.94609, 44.94609, 44.94609, 44.94609, 44.94609, 44.94609, 44.83699, 44.83699, 44.83699, 44.83699, 44.83699, 44.83699, 44.83699, 44.83699, 44.83699, 44.83699, 44.83699, 44.83699, 44.77139, 44.77139, 44.77139, 44.77139, 44.77139, 44.77139, 44.77139, 44.77139, 44.77139, 44.69552, 44.69552, 44.69552, 44.69552, 44.69552, 44.69552, 44.69552, 44.69552, 44.69552, 44.69552, 44.70565, 44.70565, 44.70565, 44.70565, 44.70565, 44.70565, 44.70565, 44.70565, 44.70565, 44.70565, 44.70565, 44.57217, 44.57217, 44.57217, 44.57217, 44.57217, 44.57217, 44.57217, 44.57217, 44.57217, 44.57217, 44.44757, 44.44757, 44.44757, 44.44757, 44.44757, 44.44757, 44.44757, 44.44757, 44.44757, 44.44757, 44.44757, 44.12458, 44.12458, 44.12458, 44.12458, 44.12458, 44.12458, 44.12458, 44.12458, 44.12458, 44.12458, 44.12458, 44.12458, 44.12649, 44.12649, 44.12649, 44.12649, 44.12649, 44.12649, 44.12649, 44.12649, 44.12649, 44.12649, 44.12649, 46.39, 46.39, 46.39, 46.39, 46.39, 46.39, 46.39, 46.39, 46.39, 46.39]
 
+# All the type of study
+STUDY_TYPES = ['alp_flat', 'postes', 'alp_allslopes']
 
 
 
diff --git a/experiment/meteo_france_data/scm_models_data/visualization/study_visualization/main_study_visualizer.py b/experiment/meteo_france_data/scm_models_data/visualization/study_visualization/main_study_visualizer.py
index 162f3261..fd6f2f38 100644
--- a/experiment/meteo_france_data/scm_models_data/visualization/study_visualization/main_study_visualizer.py
+++ b/experiment/meteo_france_data/scm_models_data/visualization/study_visualization/main_study_visualizer.py
@@ -5,8 +5,8 @@ from experiment.meteo_france_data.scm_models_data.visualization.study_visualizat
     StudyVisualizer
 from experiment.trend_analysis.abstract_score import MannKendall
 from experiment.meteo_france_data.scm_models_data.abstract_study import AbstractStudy
-from experiment.meteo_france_data.scm_models_data.crocus.crocus import CrocusDepth, CrocusSwe, ExtendedCrocusDepth, \
-    ExtendedCrocusSwe, CrocusDaysWithSnowOnGround
+from experiment.meteo_france_data.scm_models_data.crocus.crocus import CrocusDepth, CrocusTotalSwe, ExtendedCrocusDepth, \
+    ExtendedCrocusTotalSwe, CrocusDaysWithSnowOnGround
 from experiment.meteo_france_data.scm_models_data.safran.safran import SafranSnowfall, ExtendedSafranSnowfall, \
     SafranRainfall, \
     SafranTemperature, SafranTotalPrecip
@@ -18,17 +18,17 @@ from spatio_temporal_dataset.coordinates.transformed_coordinates.transformation.
     BetweenZeroAndOneNormalization, BetweenMinusOneAndOneNormalization
 from utils import get_display_name_from_object_type
 
-SCM_STUDIES = [SafranSnowfall, CrocusSwe, CrocusDepth]
+SCM_STUDIES = [SafranSnowfall, CrocusTotalSwe, CrocusDepth]
 SCM_STUDIES_NAMES = [get_display_name_from_object_type(k) for k in SCM_STUDIES]
 SCM_STUDY_NAME_TO_SCM_STUDY = dict(zip(SCM_STUDIES_NAMES, SCM_STUDIES))
 SCM_STUDY_CLASS_TO_ABBREVIATION = {
     SafranSnowfall: 'SF3',
-    CrocusSwe: 'SWE',
+    CrocusTotalSwe: 'SWE',
     CrocusDepth: 'SD',
 }
 
 altitude_massif_name_and_study_class_for_poster = [
-    (900, 'Chartreuse', CrocusSwe),
+    (900, 'Chartreuse', CrocusTotalSwe),
     (1800, 'Vanoise', CrocusDepth),
     (2700, 'Parpaillon', SafranSnowfall),
 ]
@@ -40,7 +40,7 @@ SCM_STUDY_CLASS_TO_COLOR = dict(zip(SCM_STUDIES, SCM_COLORS))
 SCM_STUDY_NAME_TO_COLOR = {get_display_name_from_object_type(s): color
                            for s, color in zip(SCM_STUDIES, SCM_COLORS)}
 
-SCM_EXTENDED_STUDIES = [ExtendedSafranSnowfall, ExtendedCrocusSwe, ExtendedCrocusDepth]
+SCM_EXTENDED_STUDIES = [ExtendedSafranSnowfall, ExtendedCrocusTotalSwe, ExtendedCrocusDepth]
 SCM_STUDY_TO_EXTENDED_STUDY = OrderedDict(zip(SCM_STUDIES, SCM_EXTENDED_STUDIES))
 
 ALL_ALTITUDES = [0, 300, 600, 900, 1200, 1500, 1800, 2100, 2400, 2700, 3000, 3300, 3600, 3900, 4200, 4500, 4800]
@@ -109,7 +109,7 @@ def annual_mean_vizu_compare_durand_study(safran=True, take_mean_value=True, alt
             study_visualizer = StudyVisualizer(study)
             study_visualizer.visualize_annual_mean_values(take_mean_value=True)
     else:
-        for study_class in [CrocusSwe, CrocusDepth, CrocusDaysWithSnowOnGround][-1:]:
+        for study_class in [CrocusTotalSwe, CrocusDepth, CrocusDaysWithSnowOnGround][-1:]:
             study = study_class(altitude=altitude, year_min=1958, year_max=2005)
             study_visualizer = StudyVisualizer(study)
             study_visualizer.visualize_annual_mean_values(take_mean_value=take_mean_value)
@@ -198,7 +198,7 @@ def trend_analysis():
     durand_altitude = [1800]
     altitudes = durand_altitude
     normalization_class = [None, BetweenMinusOneAndOneNormalization, BetweenZeroAndOneNormalization][-1]
-    study_classes = [CrocusSwe, CrocusDepth, SafranSnowfall, SafranRainfall, SafranTemperature][2:3]
+    study_classes = [CrocusTotalSwe, CrocusDepth, SafranSnowfall, SafranRainfall, SafranTemperature][2:3]
     for study in study_iterator_global(study_classes, only_first_one=only_first_one, altitudes=altitudes):
         study_visualizer = StudyVisualizer(study, save_to_file=save_to_file,
                                            transformation_class=normalization_class,
@@ -246,7 +246,7 @@ def max_graph_annual_maxima_poster():
 
 
 def altitude_analysis():
-    study = CrocusSwe(altitude=900)
+    study = CrocusTotalSwe(altitude=900)
     all_names = set(study.study_massif_names)
     for a, names in study.altitude_to_massif_names.items():
         print(a, len(names), all_names - set(names))
diff --git a/test/test_experiment/test_coordinate_sensitivity.py b/test/test_experiment/test_coordinate_sensitivity.py
index 75f4525b..7f8295fe 100644
--- a/test/test_experiment/test_coordinate_sensitivity.py
+++ b/test/test_experiment/test_coordinate_sensitivity.py
@@ -1,6 +1,6 @@
 import unittest
 
-from experiment.meteo_france_data.scm_models_data.crocus.crocus import CrocusSwe
+from experiment.meteo_france_data.scm_models_data.crocus.crocus import CrocusTotalSwe
 from experiment.meteo_france_data.scm_models_data.visualization.study_visualization.main_study_visualizer import \
     study_iterator_global
 from experiment.meteo_france_data.scm_models_data.visualization.study_visualization.study_visualizer import \
@@ -20,7 +20,7 @@ class TestCoordinateSensitivity(unittest.TestCase):
         transformation_classes = [None, BetweenZeroAndOneNormalization, BetweenZeroAndOneNormalizationMinEpsilon,
                                   BetweenZeroAndOneNormalizationMaxEpsilon][1:2]
 
-        study_classes = [CrocusSwe]
+        study_classes = [CrocusTotalSwe]
         for study in study_iterator_global(study_classes, altitudes=altitudes, verbose=False):
             if self.DISPLAY:
                 print(study.altitude)
diff --git a/test/test_utils.py b/test/test_utils.py
index df8d68c3..567f007c 100644
--- a/test/test_utils.py
+++ b/test/test_utils.py
@@ -2,7 +2,7 @@ from itertools import product
 from typing import List
 
 from experiment.meteo_france_data.scm_models_data.abstract_study import AbstractStudy
-from experiment.meteo_france_data.scm_models_data.crocus.crocus import Crocus, CrocusSwe, CrocusDepth
+from experiment.meteo_france_data.scm_models_data.crocus.crocus import Crocus, CrocusTotalSwe, CrocusDepth
 from extreme_estimator.estimator.full_estimator.abstract_full_estimator import SmoothMarginalsThenUnitaryMsp, \
     FullEstimatorInASingleStepWithSmoothMargin
 from extreme_estimator.estimator.max_stable_estimator.abstract_max_stable_estimator import MaxStableEstimator
@@ -119,7 +119,7 @@ def load_safran_studies(altitudes) -> List[Safran]:
 
 
 def load_crocus_studies(altitudes) -> List[Crocus]:
-    crocus_classes = [CrocusSwe, CrocusDepth][:]
+    crocus_classes = [CrocusTotalSwe, CrocusDepth][:]
     return [crocus_class(altitude=altitude) for crocus_class, altitude in product(crocus_classes, altitudes)]
 
 
-- 
GitLab