Salome HOME
Minor documentation and code review corrections (14)
[modules/adao.git] / src / daComposant / daCore / AssimilationStudy.py
index ac5abc971abbe741aa86167fa0d64efd89e54c82..910e5fd4b7825e67170712f85c5c0221ecc5307a 100644 (file)
@@ -1,6 +1,6 @@
-#-*-coding:iso-8859-1-*-
+# -*- coding: utf-8 -*-
 #
-# Copyright (C) 2008-2017 EDF R&D
+# Copyright (C) 2008-2021 EDF R&D
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # Author: Jean-Philippe Argaud, jean-philippe.argaud@edf.fr, EDF R&D
 
 """
-    Classe principale pour la préparation, la réalisation et la restitution de
-    calculs d'assimilation de données.
-
-    Ce module est destiné à être appelé par AssimilationStudy pour constituer
-    les objets élémentaires de l'étude.
+    Normalized interface for ADAO scripting (full version API)
 """
 __author__ = "Jean-Philippe ARGAUD"
 __all__ = ["AssimilationStudy"]
 
-import os, sys
-import numpy
-import ExtendedLogging ; ExtendedLogging.ExtendedLogging() # A importer en premier
-import logging
-try:
-    import scipy.optimize
-    logging.debug("Succeed initial import of scipy.optimize with Scipy %s", scipy.version.version)
-except ImportError:
-    logging.debug("Fail initial import of scipy.optimize")
-import Persistence
-from BasicObjects import Operator, Covariance
-import PlatformInfo
+from daCore.Aidsm import Aidsm as _Aidsm
 
 # ==============================================================================
-class AssimilationStudy:
+class AssimilationStudy(_Aidsm):
     """
-    Cette classe sert d'interface pour l'utilisation de l'assimilation de
-    données. Elle contient les méthodes ou accesseurs nécessaires à la
-    construction d'un calcul d'assimilation.
+    Generic ADAO TUI builder
     """
-    def __init__(self, name=""):
-        """
-        Prévoit de conserver l'ensemble des variables nécssaires à un algorithme
-        élémentaire. Ces variables sont ensuite disponibles pour implémenter un
-        algorithme élémentaire particulier.
-
-        - Background : vecteur Xb
-        - Observation : vecteur Y (potentiellement temporel) d'observations
-        - State : vecteur d'état dont une partie est le vecteur de contrôle.
-          Cette information n'est utile que si l'on veut faire des calculs sur
-          l'état complet, mais elle n'est pas indispensable pour l'assimilation.
-        - Control : vecteur X contenant toutes les variables de contrôle, i.e.
-          les paramètres ou l'état dont on veut estimer la valeur pour obtenir
-          les observations
-        - ObservationOperator...: opérateur d'observation H
-
-        Les observations présentent une erreur dont la matrice de covariance est
-        R. L'ébauche du vecteur de contrôle présente une erreur dont la matrice
-        de covariance est B.
-        """
-        self.__name = str(name)
-        self.__Xb  = None
-        self.__Y   = None
-        self.__U   = None
-        self.__B   = None
-        self.__R   = None
-        self.__Q   = None
-        self.__HO  = {}
-        self.__EM  = {}
-        self.__CM  = {}
-        #
-        self.__X  = Persistence.OneVector()
-        self.__Parameters        = {}
-        self.__StoredDiagnostics = {}
-        self.__StoredInputs      = {}
-        #
-        # Variables temporaires
-        self.__algorithm         = {}
-        self.__algorithmFile     = None
-        self.__algorithmName     = None
-        self.__diagnosticFile    = None
-        #
-        # Récupère le chemin du répertoire parent et l'ajoute au path
-        # (Cela complète l'action de la classe PathManagement dans PlatformInfo,
-        # qui est activée dans Persistence)
-        self.__parent = os.path.abspath(os.path.join(os.path.dirname(__file__),".."))
-        sys.path.insert(0, self.__parent)
-        sys.path = PlatformInfo.uniq( sys.path ) # Conserve en unique exemplaire chaque chemin
-
-    # ---------------------------------------------------------
-    def setBackground(self,
-            asVector           = None,
-            asPersistentVector = None,
-            Scheduler          = None,
-            toBeStored         = False,
-            ):
-        """
-        Permet de définir l'estimation a priori :
-        - asVector : entrée des données, comme un vecteur compatible avec le
-          constructeur de numpy.matrix
-        - asPersistentVector : entrée des données, comme un vecteur de type
-          persistent contruit avec la classe ad-hoc "Persistence"
-        - Scheduler est le contrôle temporel des données
-        - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
-          être rendue disponible au même titre que les variables de calcul
-        """
-        if asVector is not None:
-            self.__Xb = numpy.matrix( numpy.ravel(numpy.matrix(asVector)), numpy.float ).T
-        elif asPersistentVector is not None:
-            if type(asPersistentVector) in [type([]),type(()),type(numpy.array([])),type(numpy.matrix([]))]:
-                self.__Xb = Persistence.OneVector("Background", basetype=numpy.matrix)
-                for member in asPersistentVector:
-                    self.__Xb.store( numpy.matrix( numpy.asmatrix(member).A1, numpy.float ).T )
-            else:
-                self.__Xb = asPersistentVector
-        else:
-            raise ValueError("Error: improperly defined background, it requires at minima either a vector, a list/tuple of vectors or a persistent object")
-        if toBeStored:
-            self.__StoredInputs["Background"] = self.__Xb
-        return 0
-
-    def setBackgroundError(self,
-            asCovariance  = None,
-            asEyeByScalar = None,
-            asEyeByVector = None,
-            asCovObject   = None,
-            toBeStored    = False,
-            toBeChecked   = False,
-            ):
-        """
-        Permet de définir la covariance des erreurs d'ébauche :
-        - asCovariance : entrée des données, comme une matrice compatible avec
-          le constructeur de numpy.matrix
-        - asEyeByScalar : entrée des données comme un seul scalaire de variance,
-          multiplicatif d'une matrice de corrélation identité, aucune matrice
-          n'étant donc explicitement à donner
-        - asEyeByVector : entrée des données comme un seul vecteur de variance,
-          à mettre sur la diagonale d'une matrice de corrélation, aucune matrice
-          n'étant donc explicitement à donner
-        - asCovObject : entrée des données comme un objet ayant des méthodes
-          particulieres de type matriciel
-        - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
-          être rendue disponible au même titre que les variables de calcul
-        """
-        self.__B = Covariance(
-            name          = "BackgroundError",
-            asCovariance  = asCovariance,
-            asEyeByScalar = asEyeByScalar,
-            asEyeByVector = asEyeByVector,
-            asCovObject   = asCovObject,
-            toBeChecked   = toBeChecked,
-            )
-        if toBeStored:
-            self.__StoredInputs["BackgroundError"] = self.__B
-        return 0
-
-    # -----------------------------------------------------------
-    def setObservation(self,
-            asVector           = None,
-            asPersistentVector = None,
-            Scheduler          = None,
-            toBeStored         = False,
-            ):
-        """
-        Permet de définir les observations :
-        - asVector : entrée des données, comme un vecteur compatible avec le
-          constructeur de numpy.matrix
-        - asPersistentVector : entrée des données, comme un vecteur de type
-          persistent contruit avec la classe ad-hoc "Persistence"
-        - Scheduler est le contrôle temporel des données disponibles
-        - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
-          être rendue disponible au même titre que les variables de calcul
-        """
-        if asVector is not None:
-            self.__Y = numpy.matrix( numpy.ravel(numpy.matrix(asVector)), numpy.float ).T
-        elif asPersistentVector is not None:
-            if type(asPersistentVector) in [type([]),type(()),type(numpy.array([])),type(numpy.matrix([]))]:
-                self.__Y = Persistence.OneVector("Observation", basetype=numpy.matrix)
-                for member in asPersistentVector:
-                    self.__Y.store( numpy.matrix( numpy.asmatrix(member).A1, numpy.float ).T )
-            else:
-                self.__Y = asPersistentVector
-        else:
-            raise ValueError("Error: improperly defined observations, it requires at minima either a vector, a list/tuple of vectors or a persistent object")
-        if toBeStored:
-            self.__StoredInputs["Observation"] = self.__Y
-        return 0
-
-    def setObservationError(self,
-            asCovariance  = None,
-            asEyeByScalar = None,
-            asEyeByVector = None,
-            asCovObject   = None,
-            toBeStored    = False,
-            toBeChecked   = False,
-            ):
-        """
-        Permet de définir la covariance des erreurs d'observations :
-        - asCovariance : entrée des données, comme une matrice compatible avec
-          le constructeur de numpy.matrix
-        - asEyeByScalar : entrée des données comme un seul scalaire de variance,
-          multiplicatif d'une matrice de corrélation identité, aucune matrice
-          n'étant donc explicitement à donner
-        - asEyeByVector : entrée des données comme un seul vecteur de variance,
-          à mettre sur la diagonale d'une matrice de corrélation, aucune matrice
-          n'étant donc explicitement à donner
-        - asCovObject : entrée des données comme un objet ayant des méthodes
-          particulieres de type matriciel
-        - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
-          être rendue disponible au même titre que les variables de calcul
-        """
-        self.__R = Covariance(
-            name          = "ObservationError",
-            asCovariance  = asCovariance,
-            asEyeByScalar = asEyeByScalar,
-            asEyeByVector = asEyeByVector,
-            asCovObject   = asCovObject,
-            toBeChecked   = toBeChecked,
-            )
-        if toBeStored:
-            self.__StoredInputs["ObservationError"] = self.__R
-        return 0
-
-    def setObservationOperator(self,
-            asFunction = None,
-            asMatrix   = None,
-            appliedToX = None,
-            toBeStored = False,
-            avoidRC    = True,
-            ):
-        """
-        Permet de définir un opérateur d'observation H. L'ordre de priorité des
-        définitions et leur sens sont les suivants :
-        - si asFunction["Tangent"] et asFunction["Adjoint"] ne sont pas None
-          alors on définit l'opérateur à l'aide de fonctions. Si la fonction
-          "Direct" n'est pas définie, on prend la fonction "Tangent".
-          Si "useApproximatedDerivatives" est vrai, on utilise une approximation
-          des opérateurs tangents et adjoints. On utilise par défaut des
-          différences finies non centrées ou centrées (si "withCenteredDF" est
-          vrai) avec un incrément multiplicatif "withIncrement" de 1% autour
-          du point courant ou sur le point fixe "withdX".
-        - si les fonctions ne sont pas disponibles et si asMatrix n'est pas
-          None, alors on définit l'opérateur "Direct" et "Tangent" à l'aide de
-          la matrice, et l'opérateur "Adjoint" à l'aide de la transposée. La
-          matrice fournie doit être sous une forme compatible avec le
-          constructeur de numpy.matrix.
-        - si l'argument "appliedToX" n'est pas None, alors on définit, pour des
-          X divers, l'opérateur par sa valeur appliquée à cet X particulier,
-          sous la forme d'un dictionnaire appliedToX[NAME] avec NAME un nom.
-          L'opérateur doit néanmoins déjà avoir été défini comme d'habitude.
-        - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
-          être rendue disponible au même titre que les variables de calcul
-        L'argument "asFunction" peut prendre la forme complète suivante, avec
-        les valeurs par défaut standards :
-          asFunction = {"Direct":None, "Tangent":None, "Adjoint":None,
-                        "useApproximatedDerivatives":False,
-                        "withCenteredDF"            :False,
-                        "withIncrement"             :0.01,
-                        "withdX"                    :None,
-                        "withAvoidingRedundancy"    :True,
-                        "withToleranceInRedundancy" :1.e-18,
-                        "withLenghtOfRedundancy"    :-1,
-                        "withmpEnabled"             :False,
-                        "withmpWorkers"             :None,
-                       }
-        """
-        if isinstance(asFunction, dict) and \
-                ("useApproximatedDerivatives" in asFunction) and bool(asFunction["useApproximatedDerivatives"]) and \
-                ("Direct" in asFunction) and (asFunction["Direct"] is not None):
-            if "withCenteredDF"            not in asFunction: asFunction["withCenteredDF"]            = False
-            if "withIncrement"             not in asFunction: asFunction["withIncrement"]             = 0.01
-            if "withdX"                    not in asFunction: asFunction["withdX"]                    = None
-            if "withAvoidingRedundancy"    not in asFunction: asFunction["withAvoidingRedundancy"]    = True
-            if "withToleranceInRedundancy" not in asFunction: asFunction["withToleranceInRedundancy"] = 1.e-18
-            if "withLenghtOfRedundancy"    not in asFunction: asFunction["withLenghtOfRedundancy"]    = -1
-            if "withmpEnabled"             not in asFunction: asFunction["withmpEnabled"]             = False
-            if "withmpWorkers"             not in asFunction: asFunction["withmpWorkers"]             = None
-            from daNumerics.ApproximatedDerivatives import FDApproximation
-            FDA = FDApproximation(
-                Function              = asFunction["Direct"],
-                centeredDF            = asFunction["withCenteredDF"],
-                increment             = asFunction["withIncrement"],
-                dX                    = asFunction["withdX"],
-                avoidingRedundancy    = asFunction["withAvoidingRedundancy"],
-                toleranceInRedundancy = asFunction["withToleranceInRedundancy"],
-                lenghtOfRedundancy    = asFunction["withLenghtOfRedundancy"],
-                mpEnabled             = asFunction["withmpEnabled"],
-                mpWorkers             = asFunction["withmpWorkers"],
-                )
-            self.__HO["Direct"]  = Operator( fromMethod = FDA.DirectOperator,  avoidingRedundancy = avoidRC )
-            self.__HO["Tangent"] = Operator( fromMethod = FDA.TangentOperator, avoidingRedundancy = avoidRC )
-            self.__HO["Adjoint"] = Operator( fromMethod = FDA.AdjointOperator, avoidingRedundancy = avoidRC )
-        elif isinstance(asFunction, dict) and \
-                ("Tangent" in asFunction) and ("Adjoint" in asFunction) and \
-                (asFunction["Tangent"] is not None) and (asFunction["Adjoint"] is not None):
-            if ("Direct" not in asFunction) or (asFunction["Direct"] is None):
-                self.__HO["Direct"] = Operator( fromMethod = asFunction["Tangent"], avoidingRedundancy = avoidRC )
-            else:
-                self.__HO["Direct"] = Operator( fromMethod = asFunction["Direct"],  avoidingRedundancy = avoidRC )
-            self.__HO["Tangent"]    = Operator( fromMethod = asFunction["Tangent"], avoidingRedundancy = avoidRC )
-            self.__HO["Adjoint"]    = Operator( fromMethod = asFunction["Adjoint"], avoidingRedundancy = avoidRC )
-        elif asMatrix is not None:
-            matrice = numpy.matrix( asMatrix, numpy.float )
-            self.__HO["Direct"]  = Operator( fromMatrix = matrice,   avoidingRedundancy = avoidRC )
-            self.__HO["Tangent"] = Operator( fromMatrix = matrice,   avoidingRedundancy = avoidRC )
-            self.__HO["Adjoint"] = Operator( fromMatrix = matrice.T, avoidingRedundancy = avoidRC )
-            del matrice
-        else:
-            raise ValueError("Improperly defined observation operator, it requires at minima either a matrix, a Direct for approximate derivatives or a Tangent/Adjoint pair.")
-        #
-        if appliedToX is not None:
-            self.__HO["AppliedToX"] = {}
-            if type(appliedToX) is not dict:
-                raise ValueError("Error: observation operator defined by \"appliedToX\" need a dictionary as argument.")
-            for key in list(appliedToX.keys()):
-                if type( appliedToX[key] ) is type( numpy.matrix([]) ):
-                    # Pour le cas où l'on a une vraie matrice
-                    self.__HO["AppliedToX"][key] = numpy.matrix( appliedToX[key].A1, numpy.float ).T
-                elif type( appliedToX[key] ) is type( numpy.array([]) ) and len(appliedToX[key].shape) > 1:
-                    # Pour le cas où l'on a un vecteur représenté en array avec 2 dimensions
-                    self.__HO["AppliedToX"][key] = numpy.matrix( appliedToX[key].reshape(len(appliedToX[key]),), numpy.float ).T
-                else:
-                    self.__HO["AppliedToX"][key] = numpy.matrix( appliedToX[key],    numpy.float ).T
-        else:
-            self.__HO["AppliedToX"] = None
-        #
-        if toBeStored:
-            self.__StoredInputs["ObservationOperator"] = self.__HO
-        return 0
-
-    # -----------------------------------------------------------
-    def setEvolutionModel(self,
-            asFunction = None,
-            asMatrix   = None,
-            Scheduler  = None,
-            toBeStored = False,
-            avoidRC    = True,
-            ):
-        """
-        Permet de définir un opérateur d'évolution M. L'ordre de priorité des
-        définitions et leur sens sont les suivants :
-        - si asFunction["Tangent"] et asFunction["Adjoint"] ne sont pas None
-          alors on définit l'opérateur à l'aide de fonctions. Si la fonction
-          "Direct" n'est pas définie, on prend la fonction "Tangent".
-          Si "useApproximatedDerivatives" est vrai, on utilise une approximation
-          des opérateurs tangents et adjoints. On utilise par défaut des
-          différences finies non centrées ou centrées (si "withCenteredDF" est
-          vrai) avec un incrément multiplicatif "withIncrement" de 1% autour
-          du point courant ou sur le point fixe "withdX".
-        - si les fonctions ne sont pas disponibles et si asMatrix n'est pas
-          None, alors on définit l'opérateur "Direct" et "Tangent" à l'aide de
-          la matrice, et l'opérateur "Adjoint" à l'aide de la transposée. La
-          matrice fournie doit être sous une forme compatible avec le
-          constructeur de numpy.matrix.
-        - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
-          être rendue disponible au même titre que les variables de calcul
-        L'argument "asFunction" peut prendre la forme complète suivante, avec
-        les valeurs par défaut standards :
-          asFunction = {"Direct":None, "Tangent":None, "Adjoint":None,
-                        "useApproximatedDerivatives":False,
-                        "withCenteredDF"            :False,
-                        "withIncrement"             :0.01,
-                        "withdX"                    :None,
-                        "withAvoidingRedundancy"    :True,
-                        "withToleranceInRedundancy" :1.e-18,
-                        "withLenghtOfRedundancy"    :-1,
-                        "withmpEnabled"             :False,
-                        "withmpWorkers"             :None,
-                       }
-        """
-        if isinstance(asFunction, dict) and \
-                ("useApproximatedDerivatives" in asFunction) and bool(asFunction["useApproximatedDerivatives"]) and \
-                ("Direct" in asFunction) and (asFunction["Direct"] is not None):
-            if "withCenteredDF"            not in asFunction: asFunction["withCenteredDF"]            = False
-            if "withIncrement"             not in asFunction: asFunction["withIncrement"]             = 0.01
-            if "withdX"                    not in asFunction: asFunction["withdX"]                    = None
-            if "withAvoidingRedundancy"    not in asFunction: asFunction["withAvoidingRedundancy"]    = True
-            if "withToleranceInRedundancy" not in asFunction: asFunction["withToleranceInRedundancy"] = 1.e-18
-            if "withLenghtOfRedundancy"    not in asFunction: asFunction["withLenghtOfRedundancy"]    = -1
-            if "withmpEnabled"             not in asFunction: asFunction["withmpEnabled"]             = False
-            if "withmpWorkers"             not in asFunction: asFunction["withmpWorkers"]             = None
-            from daNumerics.ApproximatedDerivatives import FDApproximation
-            FDA = FDApproximation(
-                Function              = asFunction["Direct"],
-                centeredDF            = asFunction["withCenteredDF"],
-                increment             = asFunction["withIncrement"],
-                dX                    = asFunction["withdX"],
-                avoidingRedundancy    = asFunction["withAvoidingRedundancy"],
-                toleranceInRedundancy = asFunction["withToleranceInRedundancy"],
-                lenghtOfRedundancy    = asFunction["withLenghtOfRedundancy"],
-                mpEnabled             = asFunction["withmpEnabled"],
-                mpWorkers             = asFunction["withmpWorkers"],
-                )
-            self.__EM["Direct"]  = Operator( fromMethod = FDA.DirectOperator,  avoidingRedundancy = avoidRC )
-            self.__EM["Tangent"] = Operator( fromMethod = FDA.TangentOperator, avoidingRedundancy = avoidRC )
-            self.__EM["Adjoint"] = Operator( fromMethod = FDA.AdjointOperator, avoidingRedundancy = avoidRC )
-        elif isinstance(asFunction, dict) and \
-                ("Tangent" in asFunction) and ("Adjoint" in asFunction) and \
-                (asFunction["Tangent"] is not None) and (asFunction["Adjoint"] is not None):
-            if ("Direct" not in asFunction) or (asFunction["Direct"] is None):
-                self.__EM["Direct"] = Operator( fromMethod = asFunction["Tangent"], avoidingRedundancy = avoidRC )
-            else:
-                self.__EM["Direct"] = Operator( fromMethod = asFunction["Direct"],  avoidingRedundancy = avoidRC )
-            self.__EM["Tangent"]    = Operator( fromMethod = asFunction["Tangent"], avoidingRedundancy = avoidRC )
-            self.__EM["Adjoint"]    = Operator( fromMethod = asFunction["Adjoint"], avoidingRedundancy = avoidRC )
-        elif asMatrix is not None:
-            matrice = numpy.matrix( asMatrix, numpy.float )
-            self.__EM["Direct"]  = Operator( fromMatrix = matrice,   avoidingRedundancy = avoidRC )
-            self.__EM["Tangent"] = Operator( fromMatrix = matrice,   avoidingRedundancy = avoidRC )
-            self.__EM["Adjoint"] = Operator( fromMatrix = matrice.T, avoidingRedundancy = avoidRC )
-            del matrice
-        else:
-            raise ValueError("Improperly defined evolution model, it requires at minima either a matrix, a Direct for approximate derivatives or a Tangent/Adjoint pair.")
-        #
-        if toBeStored:
-            self.__StoredInputs["EvolutionModel"] = self.__EM
-        return 0
-
-    def setEvolutionError(self,
-            asCovariance  = None,
-            asEyeByScalar = None,
-            asEyeByVector = None,
-            asCovObject   = None,
-            toBeStored    = False,
-            toBeChecked   = False,
-            ):
-        """
-        Permet de définir la covariance des erreurs de modèle :
-        - asCovariance : entrée des données, comme une matrice compatible avec
-          le constructeur de numpy.matrix
-        - asEyeByScalar : entrée des données comme un seul scalaire de variance,
-          multiplicatif d'une matrice de corrélation identité, aucune matrice
-          n'étant donc explicitement à donner
-        - asEyeByVector : entrée des données comme un seul vecteur de variance,
-          à mettre sur la diagonale d'une matrice de corrélation, aucune matrice
-          n'étant donc explicitement à donner
-        - asCovObject : entrée des données comme un objet ayant des méthodes
-          particulieres de type matriciel
-        - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
-          être rendue disponible au même titre que les variables de calcul
-        """
-        self.__Q = Covariance(
-            name          = "EvolutionError",
-            asCovariance  = asCovariance,
-            asEyeByScalar = asEyeByScalar,
-            asEyeByVector = asEyeByVector,
-            asCovObject   = asCovObject,
-            toBeChecked   = toBeChecked,
-            )
-        if toBeStored:
-            self.__StoredInputs["EvolutionError"] = self.__Q
-        return 0
-
-    # -----------------------------------------------------------
-    def setControlModel(self,
-            asFunction = None,
-            asMatrix   = None,
-            Scheduler  = None,
-            toBeStored = False,
-            avoidRC    = True,
-            ):
-        """
-        Permet de définir un opérateur de controle C. L'ordre de priorité des
-        définitions et leur sens sont les suivants :
-        - si asFunction["Tangent"] et asFunction["Adjoint"] ne sont pas None
-          alors on définit l'opérateur à l'aide de fonctions. Si la fonction
-          "Direct" n'est pas définie, on prend la fonction "Tangent".
-          Si "useApproximatedDerivatives" est vrai, on utilise une approximation
-          des opérateurs tangents et adjoints. On utilise par défaut des
-          différences finies non centrées ou centrées (si "withCenteredDF" est
-          vrai) avec un incrément multiplicatif "withIncrement" de 1% autour
-          du point courant ou sur le point fixe "withdX".
-        - si les fonctions ne sont pas disponibles et si asMatrix n'est pas
-          None, alors on définit l'opérateur "Direct" et "Tangent" à l'aide de
-          la matrice, et l'opérateur "Adjoint" à l'aide de la transposée. La
-          matrice fournie doit être sous une forme compatible avec le
-          constructeur de numpy.matrix.
-        - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
-          être rendue disponible au même titre que les variables de calcul
-        L'argument "asFunction" peut prendre la forme complète suivante, avec
-        les valeurs par défaut standards :
-          asFunction = {"Direct":None, "Tangent":None, "Adjoint":None,
-                        "useApproximatedDerivatives":False,
-                        "withCenteredDF"            :False,
-                        "withIncrement"             :0.01,
-                        "withdX"                    :None,
-                        "withAvoidingRedundancy"    :True,
-                        "withToleranceInRedundancy" :1.e-18,
-                        "withLenghtOfRedundancy"    :-1,
-                        "withmpEnabled"             :False,
-                        "withmpWorkers"             :None,
-                       }
-        """
-        if isinstance(asFunction, dict) and \
-                ("useApproximatedDerivatives" in asFunction) and bool(asFunction["useApproximatedDerivatives"]) and \
-                ("Direct" in asFunction) and (asFunction["Direct"] is not None):
-            if "withCenteredDF"            not in asFunction: asFunction["withCenteredDF"]            = False
-            if "withIncrement"             not in asFunction: asFunction["withIncrement"]             = 0.01
-            if "withdX"                    not in asFunction: asFunction["withdX"]                    = None
-            if "withAvoidingRedundancy"    not in asFunction: asFunction["withAvoidingRedundancy"]    = True
-            if "withToleranceInRedundancy" not in asFunction: asFunction["withToleranceInRedundancy"] = 1.e-18
-            if "withLenghtOfRedundancy"    not in asFunction: asFunction["withLenghtOfRedundancy"]    = -1
-            if "withmpEnabled"             not in asFunction: asFunction["withmpEnabled"]             = False
-            if "withmpWorkers"             not in asFunction: asFunction["withmpWorkers"]             = None
-            from daNumerics.ApproximatedDerivatives import FDApproximation
-            FDA = FDApproximation(
-                Function              = asFunction["Direct"],
-                centeredDF            = asFunction["withCenteredDF"],
-                increment             = asFunction["withIncrement"],
-                dX                    = asFunction["withdX"],
-                avoidingRedundancy    = asFunction["withAvoidingRedundancy"],
-                toleranceInRedundancy = asFunction["withToleranceInRedundancy"],
-                lenghtOfRedundancy    = asFunction["withLenghtOfRedundancy"],
-                mpEnabled             = asFunction["withmpEnabled"],
-                mpWorkers             = asFunction["withmpWorkers"],
-                )
-            self.__CM["Direct"]  = Operator( fromMethod = FDA.DirectOperator,  avoidingRedundancy = avoidRC )
-            self.__CM["Tangent"] = Operator( fromMethod = FDA.TangentOperator, avoidingRedundancy = avoidRC )
-            self.__CM["Adjoint"] = Operator( fromMethod = FDA.AdjointOperator, avoidingRedundancy = avoidRC )
-        elif isinstance(asFunction, dict) and \
-                ("Tangent" in asFunction) and ("Adjoint" in asFunction) and \
-                (asFunction["Tangent"] is not None) and (asFunction["Adjoint"] is not None):
-            if ("Direct" not in asFunction) or (asFunction["Direct"] is None):
-                self.__CM["Direct"] = Operator( fromMethod = asFunction["Tangent"], avoidingRedundancy = avoidRC )
-            else:
-                self.__CM["Direct"] = Operator( fromMethod = asFunction["Direct"],  avoidingRedundancy = avoidRC )
-            self.__CM["Tangent"]    = Operator( fromMethod = asFunction["Tangent"], avoidingRedundancy = avoidRC )
-            self.__CM["Adjoint"]    = Operator( fromMethod = asFunction["Adjoint"], avoidingRedundancy = avoidRC )
-        elif asMatrix is not None:
-            matrice = numpy.matrix( asMatrix, numpy.float )
-            self.__CM["Direct"]  = Operator( fromMatrix = matrice,   avoidingRedundancy = avoidRC )
-            self.__CM["Tangent"] = Operator( fromMatrix = matrice,   avoidingRedundancy = avoidRC )
-            self.__CM["Adjoint"] = Operator( fromMatrix = matrice.T, avoidingRedundancy = avoidRC )
-            del matrice
-        else:
-            raise ValueError("Improperly defined input control model, it requires at minima either a matrix, a Direct for approximate derivatives or a Tangent/Adjoint pair.")
-        #
-        if toBeStored:
-            self.__StoredInputs["ControlModel"] = self.__CM
-        return 0
-
-    def setControlInput(self,
-            asVector           = None,
-            asPersistentVector = None,
-            Scheduler          = None,
-            toBeStored         = False,
-            ):
-        """
-        Permet de définir le controle en entree :
-        - asVector : entrée des données, comme un vecteur compatible avec le
-          constructeur de numpy.matrix
-        - asPersistentVector : entrée des données, comme un vecteur de type
-          persistent contruit avec la classe ad-hoc "Persistence"
-        - Scheduler est le contrôle temporel des données disponibles
-        - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
-          être rendue disponible au même titre que les variables de calcul
-        """
-        if asVector is not None:
-            self.__U = numpy.matrix( numpy.ravel(numpy.matrix(asVector)), numpy.float ).T
-        elif asPersistentVector is not None:
-            if type(asPersistentVector) in [type([]),type(()),type(numpy.array([])),type(numpy.matrix([]))]:
-                self.__U = Persistence.OneVector("ControlInput", basetype=numpy.matrix)
-                for member in asPersistentVector:
-                    self.__U.store( numpy.matrix( numpy.asmatrix(member).A1, numpy.float ).T )
-            else:
-                self.__U = asPersistentVector
-        else:
-            raise ValueError("Error: improperly defined control input, it requires at minima either a vector, a list/tuple of vectors or a persistent object")
-        if toBeStored:
-            self.__StoredInputs["ControlInput"] = self.__U
-        return 0
-
-    # -----------------------------------------------------------
-    def setControls (self,
-            asVector = None,
-            toBeStored   = False,
-            ):
-        """
-        Permet de définir la valeur initiale du vecteur X contenant toutes les
-        variables de contrôle, i.e. les paramètres ou l'état dont on veut
-        estimer la valeur pour obtenir les observations. C'est utile pour un
-        algorithme itératif/incrémental.
-        - asVector : entrée des données, comme un vecteur compatible avec le
-          constructeur de numpy.matrix.
-        - toBeStored : booléen indiquant si la donnée d'entrée est sauvée pour
-          être rendue disponible au même titre que les variables de calcul
-        """
-        if asVector is not None:
-            self.__X.store( asVector )
-        if toBeStored:
-            self.__StoredInputs["Controls"] = self.__X
-        return 0
-
-    # -----------------------------------------------------------
-    def setAlgorithm(self, choice = None ):
-        """
-        Permet de sélectionner l'algorithme à utiliser pour mener à bien l'étude
-        d'assimilation. L'argument est un champ caractère se rapportant au nom
-        d'un fichier contenu dans "../daAlgorithms" et réalisant l'opération
-        d'assimilation sur les arguments fixes.
-        """
-        if choice is None:
-            raise ValueError("Error: algorithm choice has to be given")
-        if self.__algorithmName is not None:
-            raise ValueError("Error: algorithm choice has already been done as \"%s\", it can't be changed."%self.__algorithmName)
-        daDirectory = "daAlgorithms"
-        #
-        # Recherche explicitement le fichier complet
-        # ------------------------------------------
-        module_path = None
-        for directory in sys.path:
-            if os.path.isfile(os.path.join(directory, daDirectory, str(choice)+'.py')):
-                module_path = os.path.abspath(os.path.join(directory, daDirectory))
-        if module_path is None:
-            raise ImportError("No algorithm module named \"%s\" was found in a \"%s\" subdirectory\n             The search path is %s"%(choice, daDirectory, sys.path))
-        #
-        # Importe le fichier complet comme un module
-        # ------------------------------------------
-        try:
-            sys_path_tmp = sys.path ; sys.path.insert(0,module_path)
-            self.__algorithmFile = __import__(str(choice), globals(), locals(), [])
-            self.__algorithmName = str(choice)
-            sys.path = sys_path_tmp ; del sys_path_tmp
-        except ImportError as e:
-            raise ImportError("The module named \"%s\" was found, but is incorrect at the import stage.\n             The import error message is: %s"%(choice,e))
-        #
-        # Instancie un objet du type élémentaire du fichier
-        # -------------------------------------------------
-        self.__algorithm = self.__algorithmFile.ElementaryAlgorithm()
-        self.__StoredInputs["AlgorithmName"] = self.__algorithmName
-        return 0
-
-    def setAlgorithmParameters(self, asDico=None):
-        """
-        Permet de définir les paramètres de l'algorithme, sous la forme d'un
-        dictionnaire.
-        """
-        if asDico is not None:
-            self.__Parameters.update( dict( asDico ) )
-        #
-        self.__StoredInputs["AlgorithmParameters"] = self.__Parameters
-        return 0
-
-    def getAlgorithmParameters(self, noDetails=True):
-        """
-        Renvoie la liste des paramètres requis selon l'algorithme
-        """
-        return self.__algorithm.getRequiredParameters(noDetails)
-
-    # -----------------------------------------------------------
-    def setDiagnostic(self, choice = None, name = "", unit = "", basetype = None, parameters = {} ):
-        """
-        Permet de sélectionner un diagnostic a effectuer.
-        """
-        if choice is None:
-            raise ValueError("Error: diagnostic choice has to be given")
-        daDirectory = "daDiagnostics"
-        #
-        # Recherche explicitement le fichier complet
-        # ------------------------------------------
-        module_path = None
-        for directory in sys.path:
-            if os.path.isfile(os.path.join(directory, daDirectory, str(choice)+'.py')):
-                module_path = os.path.abspath(os.path.join(directory, daDirectory))
-        if module_path is None:
-            raise ImportError("No diagnostic module named \"%s\" was found in a \"%s\" subdirectory\n             The search path is %s"%(choice, daDirectory, sys.path))
-        #
-        # Importe le fichier complet comme un module
-        # ------------------------------------------
-        try:
-            sys_path_tmp = sys.path ; sys.path.insert(0,module_path)
-            self.__diagnosticFile = __import__(str(choice), globals(), locals(), [])
-            sys.path = sys_path_tmp ; del sys_path_tmp
-        except ImportError as e:
-            raise ImportError("The module named \"%s\" was found, but is incorrect at the import stage.\n             The import error message is: %s"%(choice,e))
-        #
-        # Instancie un objet du type élémentaire du fichier
-        # -------------------------------------------------
-        if name in self.__StoredInputs:
-            raise ValueError("A default input with the same name \"%s\" already exists."%str(name))
-        elif name in self.__StoredDiagnostics:
-            raise ValueError("A diagnostic with the same name \"%s\" already exists."%str(name))
-        else:
-            self.__StoredDiagnostics[name] = self.__diagnosticFile.ElementaryDiagnostic(
-                name       = name,
-                unit       = unit,
-                basetype   = basetype,
-                parameters = parameters )
-        return 0
-
-    # -----------------------------------------------------------
-    def shape_validate(self):
-        """
-        Validation de la correspondance correcte des tailles des variables et
-        des matrices s'il y en a.
-        """
-        if self.__Xb is None:                      __Xb_shape = (0,)
-        elif hasattr(self.__Xb,"size"):            __Xb_shape = (self.__Xb.size,)
-        elif hasattr(self.__Xb,"shape"):
-            if isinstance(self.__Xb.shape, tuple): __Xb_shape = self.__Xb.shape
-            else:                                  __Xb_shape = self.__Xb.shape()
-        else: raise TypeError("The background (Xb) has no attribute of shape: problem !")
-        #
-        if self.__Y is None:                       __Y_shape = (0,)
-        elif hasattr(self.__Y,"size"):             __Y_shape = (self.__Y.size,)
-        elif hasattr(self.__Y,"shape"):
-            if isinstance(self.__Y.shape, tuple):  __Y_shape = self.__Y.shape
-            else:                                  __Y_shape = self.__Y.shape()
-        else: raise TypeError("The observation (Y) has no attribute of shape: problem !")
-        #
-        if self.__U is None:                       __U_shape = (0,)
-        elif hasattr(self.__U,"size"):             __U_shape = (self.__U.size,)
-        elif hasattr(self.__U,"shape"):
-            if isinstance(self.__U.shape, tuple):  __U_shape = self.__U.shape
-            else:                                  __U_shape = self.__U.shape()
-        else: raise TypeError("The control (U) has no attribute of shape: problem !")
-        #
-        if self.__B is None:                       __B_shape = (0,0)
-        elif hasattr(self.__B,"shape"):
-            if isinstance(self.__B.shape, tuple):  __B_shape = self.__B.shape
-            else:                                  __B_shape = self.__B.shape()
-        else: raise TypeError("The a priori errors covariance matrix (B) has no attribute of shape: problem !")
-        #
-        if self.__R is None:                       __R_shape = (0,0)
-        elif hasattr(self.__R,"shape"):
-            if isinstance(self.__R.shape, tuple):  __R_shape = self.__R.shape
-            else:                                  __R_shape = self.__R.shape()
-        else: raise TypeError("The observation errors covariance matrix (R) has no attribute of shape: problem !")
-        #
-        if self.__Q is None:                       __Q_shape = (0,0)
-        elif hasattr(self.__Q,"shape"):
-            if isinstance(self.__Q.shape, tuple):  __Q_shape = self.__Q.shape
-            else:                                  __Q_shape = self.__Q.shape()
-        else: raise TypeError("The evolution errors covariance matrix (Q) has no attribute of shape: problem !")
-        #
-        if len(self.__HO) == 0:                              __HO_shape = (0,0)
-        elif isinstance(self.__HO, dict):                    __HO_shape = (0,0)
-        elif hasattr(self.__HO["Direct"],"shape"):
-            if isinstance(self.__HO["Direct"].shape, tuple): __HO_shape = self.__HO["Direct"].shape
-            else:                                            __HO_shape = self.__HO["Direct"].shape()
-        else: raise TypeError("The observation operator (H) has no attribute of shape: problem !")
-        #
-        if len(self.__EM) == 0:                              __EM_shape = (0,0)
-        elif isinstance(self.__EM, dict):                    __EM_shape = (0,0)
-        elif hasattr(self.__EM["Direct"],"shape"):
-            if isinstance(self.__EM["Direct"].shape, tuple): __EM_shape = self.__EM["Direct"].shape
-            else:                                            __EM_shape = self.__EM["Direct"].shape()
-        else: raise TypeError("The evolution model (EM) has no attribute of shape: problem !")
-        #
-        if len(self.__CM) == 0:                              __CM_shape = (0,0)
-        elif isinstance(self.__CM, dict):                    __CM_shape = (0,0)
-        elif hasattr(self.__CM["Direct"],"shape"):
-            if isinstance(self.__CM["Direct"].shape, tuple): __CM_shape = self.__CM["Direct"].shape
-            else:                                            __CM_shape = self.__CM["Direct"].shape()
-        else: raise TypeError("The control model (CM) has no attribute of shape: problem !")
-        #
-        # Vérification des conditions
-        # ---------------------------
-        if not( len(__Xb_shape) == 1 or min(__Xb_shape) == 1 ):
-            raise ValueError("Shape characteristic of background (Xb) is incorrect: \"%s\"."%(__Xb_shape,))
-        if not( len(__Y_shape) == 1 or min(__Y_shape) == 1 ):
-            raise ValueError("Shape characteristic of observation (Y) is incorrect: \"%s\"."%(__Y_shape,))
-        #
-        if not( min(__B_shape) == max(__B_shape) ):
-            raise ValueError("Shape characteristic of a priori errors covariance matrix (B) is incorrect: \"%s\"."%(__B_shape,))
-        if not( min(__R_shape) == max(__R_shape) ):
-            raise ValueError("Shape characteristic of observation errors covariance matrix (R) is incorrect: \"%s\"."%(__R_shape,))
-        if not( min(__Q_shape) == max(__Q_shape) ):
-            raise ValueError("Shape characteristic of evolution errors covariance matrix (Q) is incorrect: \"%s\"."%(__Q_shape,))
-        if not( min(__EM_shape) == max(__EM_shape) ):
-            raise ValueError("Shape characteristic of evolution operator (EM) is incorrect: \"%s\"."%(__EM_shape,))
-        #
-        if len(self.__HO) > 0 and not(type(self.__HO) is type({})) and not( __HO_shape[1] == max(__Xb_shape) ):
-            raise ValueError("Shape characteristic of observation operator (H) \"%s\" and state (X) \"%s\" are incompatible."%(__HO_shape,__Xb_shape))
-        if len(self.__HO) > 0 and not(type(self.__HO) is type({})) and not( __HO_shape[0] == max(__Y_shape) ):
-            raise ValueError("Shape characteristic of observation operator (H) \"%s\" and observation (Y) \"%s\" are incompatible."%(__HO_shape,__Y_shape))
-        if len(self.__HO) > 0 and not(type(self.__HO) is type({})) and len(self.__B) > 0 and not( __HO_shape[1] == __B_shape[0] ):
-            raise ValueError("Shape characteristic of observation operator (H) \"%s\" and a priori errors covariance matrix (B) \"%s\" are incompatible."%(__HO_shape,__B_shape))
-        if len(self.__HO) > 0 and not(type(self.__HO) is type({})) and len(self.__R) > 0 and not( __HO_shape[0] == __R_shape[1] ):
-            raise ValueError("Shape characteristic of observation operator (H) \"%s\" and observation errors covariance matrix (R) \"%s\" are incompatible."%(__HO_shape,__R_shape))
-        #
-        if self.__B is not None and len(self.__B) > 0 and not( __B_shape[1] == max(__Xb_shape) ):
-            if self.__StoredInputs["AlgorithmName"] in ["EnsembleBlue",]:
-                asPersistentVector = self.__Xb.reshape((-1,min(__B_shape)))
-                self.__Xb = Persistence.OneVector("Background", basetype=numpy.matrix)
-                for member in asPersistentVector:
-                    self.__Xb.store( numpy.matrix( numpy.ravel(member), numpy.float ).T )
-                __Xb_shape = min(__B_shape)
-            else:
-                raise ValueError("Shape characteristic of a priori errors covariance matrix (B) \"%s\" and background (Xb) \"%s\" are incompatible."%(__B_shape,__Xb_shape))
-        #
-        if self.__R is not None and len(self.__R) > 0 and not( __R_shape[1] == max(__Y_shape) ):
-            raise ValueError("Shape characteristic of observation errors covariance matrix (R) \"%s\" and observation (Y) \"%s\" are incompatible."%(__R_shape,__Y_shape))
-        #
-        if self.__EM is not None and len(self.__EM) > 0 and not(type(self.__EM) is type({})) and not( __EM_shape[1] == max(__Xb_shape) ):
-            raise ValueError("Shape characteristic of evolution model (EM) \"%s\" and state (X) \"%s\" are incompatible."%(__EM_shape,__Xb_shape))
-        #
-        if self.__CM is not None and len(self.__CM) > 0 and not(type(self.__CM) is type({})) and not( __CM_shape[1] == max(__U_shape) ):
-            raise ValueError("Shape characteristic of control model (CM) \"%s\" and control (U) \"%s\" are incompatible."%(__CM_shape,__U_shape))
-        #
-        if ("AlgorithmParameters" in self.__StoredInputs) \
-            and ("Bounds" in self.__StoredInputs["AlgorithmParameters"]) \
-            and (isinstance(self.__StoredInputs["AlgorithmParameters"]["Bounds"], list) or isinstance(self.__StoredInputs["AlgorithmParameters"]["Bounds"], tuple)) \
-            and (len(self.__StoredInputs["AlgorithmParameters"]["Bounds"]) != max(__Xb_shape)):
-            raise ValueError("The number \"%s\" of bound pairs for the state (X) components is different of the size \"%s\" of the state itself." \
-                %(len(self.__StoredInputs["AlgorithmParameters"]["Bounds"]),max(__Xb_shape)))
-        #
-        return 1
-
-    # -----------------------------------------------------------
-    def analyze(self):
-        """
-        Permet de lancer le calcul d'assimilation.
-
-        Le nom de la méthode à activer est toujours "run". Les paramètres en
-        arguments de la méthode sont fixés. En sortie, on obtient les résultats
-        dans la variable de type dictionnaire "StoredVariables", qui contient en
-        particulier des objets de Persistence pour les analyses, OMA...
-        """
-        Operator.CM.clearCache()
-        self.shape_validate()
-        #
-        self.__algorithm.run(
-            Xb         = self.__Xb,
-            Y          = self.__Y,
-            U          = self.__U,
-            HO         = self.__HO,
-            EM         = self.__EM,
-            CM         = self.__CM,
-            R          = self.__R,
-            B          = self.__B,
-            Q          = self.__Q,
-            Parameters = self.__Parameters,
-            )
-        return 0
-
-    # -----------------------------------------------------------
-    def get(self, key=None):
-        """
-        Renvoie les résultats disponibles après l'exécution de la méthode
-        d'assimilation, ou les diagnostics disponibles. Attention, quand un
-        diagnostic porte le même nom qu'une variable stockée, c'est la variable
-        stockée qui est renvoyée, et le diagnostic est inatteignable.
-        """
-        if key is not None:
-            if key in self.__algorithm:
-                return self.__algorithm.get( key )
-            elif key in self.__StoredInputs:
-                return self.__StoredInputs[key]
-            elif key in self.__StoredDiagnostics:
-                return self.__StoredDiagnostics[key]
-            else:
-                raise ValueError("The requested key \"%s\" does not exists as an input, a diagnostic or a stored variable."%key)
-        else:
-            allvariables = self.__algorithm.get()
-            allvariables.update( self.__StoredDiagnostics )
-            allvariables.update( self.__StoredInputs )
-            return allvariables
-
-    def get_available_variables(self):
-        """
-        Renvoie les variables potentiellement utilisables pour l'étude,
-        initialement stockées comme données d'entrées ou dans les algorithmes,
-        identifiés par les chaînes de caractères. L'algorithme doit avoir été
-        préalablement choisi sinon la méthode renvoie "None".
-        """
-        if len(list(self.__algorithm.keys())) == 0 and len(list(self.__StoredInputs.keys())) == 0:
-            return None
-        else:
-            variables = []
-            if len( list(self.__algorithm.keys())) > 0:
-                variables.extend( list(self.__algorithm.get().keys()) )
-            if len( list(self.__StoredInputs.keys()) ) > 0:
-                variables.extend( list(self.__StoredInputs.keys()) )
-            variables.sort()
-            return variables
-
-    def get_available_algorithms(self):
-        """
-        Renvoie la liste des algorithmes potentiellement utilisables, identifiés
-        par les chaînes de caractères.
-        """
-        files = []
-        for directory in sys.path:
-            if os.path.isdir(os.path.join(directory,"daAlgorithms")):
-                for fname in os.listdir(os.path.join(directory,"daAlgorithms")):
-                    root, ext = os.path.splitext(fname)
-                    if ext == '.py' and root != '__init__':
-                        files.append(root)
-        files.sort()
-        return files
-
-    def get_available_diagnostics(self):
-        """
-        Renvoie la liste des diagnostics potentiellement utilisables, identifiés
-        par les chaînes de caractères.
-        """
-        files = []
-        for directory in sys.path:
-            if os.path.isdir(os.path.join(directory,"daDiagnostics")):
-                for fname in os.listdir(os.path.join(directory,"daDiagnostics")):
-                    root, ext = os.path.splitext(fname)
-                    if ext == '.py' and root != '__init__':
-                        files.append(root)
-        files.sort()
-        return files
-
-    # -----------------------------------------------------------
-    def get_algorithms_main_path(self):
-        """
-        Renvoie le chemin pour le répertoire principal contenant les algorithmes
-        dans un sous-répertoire "daAlgorithms"
-        """
-        return self.__parent
-
-    def add_algorithms_path(self, asPath=None):
-        """
-        Ajoute au chemin de recherche des algorithmes un répertoire dans lequel
-        se trouve un sous-répertoire "daAlgorithms"
-
-        Remarque : si le chemin a déjà été ajouté pour les diagnostics, il n'est
-        pas indispensable de le rajouter ici.
-        """
-        if not os.path.isdir(asPath):
-            raise ValueError("The given "+asPath+" argument must exist as a directory")
-        if not os.path.isdir(os.path.join(asPath,"daAlgorithms")):
-            raise ValueError("The given \""+asPath+"\" argument must contain a subdirectory named \"daAlgorithms\"")
-        if not os.path.isfile(os.path.join(asPath,"daAlgorithms","__init__.py")):
-            raise ValueError("The given \""+asPath+"/daAlgorithms\" path must contain a file named \"__init__.py\"")
-        sys.path.insert(0, os.path.abspath(asPath))
-        sys.path = PlatformInfo.uniq( sys.path ) # Conserve en unique exemplaire chaque chemin
-        return 1
-
-    def get_diagnostics_main_path(self):
-        """
-        Renvoie le chemin pour le répertoire principal contenant les diagnostics
-        dans un sous-répertoire "daDiagnostics"
-        """
-        return self.__parent
-
-    def add_diagnostics_path(self, asPath=None):
-        """
-        Ajoute au chemin de recherche des algorithmes un répertoire dans lequel
-        se trouve un sous-répertoire "daDiagnostics"
-
-        Remarque : si le chemin a déjà été ajouté pour les algorithmes, il n'est
-        pas indispensable de le rajouter ici.
-        """
-        if not os.path.isdir(asPath):
-            raise ValueError("The given "+asPath+" argument must exist as a directory")
-        if not os.path.isdir(os.path.join(asPath,"daDiagnostics")):
-            raise ValueError("The given \""+asPath+"\" argument must contain a subdirectory named \"daDiagnostics\"")
-        if not os.path.isfile(os.path.join(asPath,"daDiagnostics","__init__.py")):
-            raise ValueError("The given \""+asPath+"/daDiagnostics\" path must contain a file named \"__init__.py\"")
-        sys.path.insert(0, os.path.abspath(asPath))
-        sys.path = PlatformInfo.uniq( sys.path ) # Conserve en unique exemplaire chaque chemin
-        return 1
-
-    # -----------------------------------------------------------
-    def setDataObserver(self,
-            VariableName   = None,
-            HookFunction   = None,
-            HookParameters = None,
-            Scheduler      = None,
-            ):
-        """
-        Permet d'associer un observer à une ou des variables nommées gérées en
-        interne, activable selon des règles définies dans le Scheduler. A chaque
-        pas demandé dans le Scheduler, il effectue la fonction HookFunction avec
-        les arguments (variable persistante VariableName, paramètres HookParameters).
-        """
-        #
-        if isinstance(self.__algorithm, dict):
-            raise ValueError("No observer can be build before choosing an algorithm.")
-        #
-        # Vérification du nom de variable et typage
-        # -----------------------------------------
-        if isinstance(VariableName, str):
-            VariableNames = [VariableName,]
-        elif isinstance(VariableName, list):
-            VariableNames = list(map( str, VariableName ))
-        else:
-            raise ValueError("The observer requires a name or a list of names of variables.")
-        #
-        # Association interne de l'observer à la variable
-        # -----------------------------------------------
-        for n in VariableNames:
-            if n not in self.__algorithm:
-                raise ValueError("An observer requires to be set on a variable named %s which does not exist."%n)
-            else:
-                self.__algorithm.StoredVariables[ n ].setDataObserver(
-                    Scheduler      = Scheduler,
-                    HookFunction   = HookFunction,
-                    HookParameters = HookParameters,
-                    )
-
-    def removeDataObserver(self,
-            VariableName   = None,
-            HookFunction   = None,
-            ):
-        """
-        Permet de retirer un observer à une ou des variable nommée.
-        """
-        #
-        if isinstance(self.__algorithm, dict):
-            raise ValueError("No observer can be removed before choosing an algorithm.")
-        #
-        # Vérification du nom de variable et typage
-        # -----------------------------------------
-        if isinstance(VariableName, str):
-            VariableNames = [VariableName,]
-        elif isinstance(VariableName, list):
-            VariableNames = list(map( str, VariableName ))
-        else:
-            raise ValueError("The observer requires a name or a list of names of variables.")
-        #
-        # Association interne de l'observer à la variable
-        # -----------------------------------------------
-        for n in VariableNames:
-            if n not in self.__algorithm:
-                raise ValueError("An observer requires to be removed on a variable named %s which does not exist."%n)
-            else:
-                self.__algorithm.StoredVariables[ n ].removeDataObserver(
-                    HookFunction   = HookFunction,
-                    )
-
-    # -----------------------------------------------------------
-    def setDebug(self, level=10):
-        """
-        Utiliser par exemple "import logging ; level = logging.DEBUG" avant cet
-        appel pour changer le niveau de verbosité, avec :
-        NOTSET=0 < DEBUG=10 < INFO=20 < WARNING=30 < ERROR=40 < CRITICAL=50
-        """
-        log = logging.getLogger()
-        log.setLevel( level )
-
-    def unsetDebug(self):
-        """
-        Remet le logger au niveau par défaut
-        """
-        log = logging.getLogger()
-        log.setLevel( logging.WARNING )
-
-    def __dir__(self):
-        # return set(self.__dict__.keys() + dir(self.__class__))
-        return ['get', '__doc__', '__init__', '__module__']
-
-    def prepare_to_pickle(self):
-        """
-        Retire les variables non pickelisables
-        """
-        del self.__B
-        del self.__CM # non pickelisable
-        del self.__EM # non pickelisable
-        del self.__HO # non pickelisable
-        del self.__Parameters
-        del self.__Q
-        del self.__R
-        del self.__U
-        del self.__X
-        del self.__Xb
-        del self.__Y
-        del self.__algorithmFile # non pickelisable
-        del self.__algorithmName
-        del self.__diagnosticFile # non pickelisable
-        self.__class__.__doc__ = ""
+    def __init__(self, name = ""):
+        _Aidsm.__init__(self, name)
 
 # ==============================================================================
 if __name__ == "__main__":
-    print('\n AUTODIAGNOSTIC \n')
+    print('\n AUTODIAGNOSTIC\n')