This algorithm performs a state estimation by variational minimization of the
classical :math:`J` function in static data assimilation:
-.. math:: J(\mathbf{x})=(\mathbf{x}-\mathbf{x}^b)^T.\mathbf{B}^{-1}.(\mathbf{x}-\mathbf{x}^b)+(\mathbf{y}^o-\mathbf{H}.\mathbf{x})^T.\mathbf{R}^{-1}.(\mathbf{y}^o-\mathbf{H}.\mathbf{x})
+.. math:: J(\mathbf{x})=(\mathbf{x}-\mathbf{x}^b)^T.\mathbf{B}^{-1}.(\mathbf{x}-\mathbf{x}^b)+(\mathbf{y}^o-H(\mathbf{x}))^T.\mathbf{R}^{-1}.(\mathbf{y}^o-H(\mathbf{x}))
which is usually designed as the "*3D-VAR*" function (see for example
[Talagrand97]_).
centered schema of second order (of twice the first order computational cost).
If necessary and if possible, :ref:`subsection_ref_parallel_df` can be used. In
all cases, an internal cache mechanism is used to restrict the number of
-operator evaluations to avoid redundant calculations, at the minimum possible in
-a sequential or parallel execution scheme for numerical approximations of the
-tangent and adjoint operators.
+operator evaluations at the minimum possible in a sequential or parallel
+execution scheme for numerical approximations of the tangent and adjoint
+operators, to avoid redundant calculations.
This first operator definition form allows easily to test the functional form
before its use in an ADAO case, greatly reducing the complexity of operator
**[DocR]** Textual Application Programming Interface for the user (API/TUI)
================================================================================
-.. warning::
-
- in its present version, this text programming interface (TUI) is experimental,
- and so changes can be required in forthcoming versions.
-
This section presents advanced usage of the ADAO module using its text
programming interface (API/TUI). This interface gives ability to create a
calculation object in a similar way than the case building obtained through the
**setObserver** (*Variable, Template, String, Script, Info*)
This command allows to set an *observer* on the current or final calculation
- variable. Reference should be made to the description of the way of
- ':ref:`section_advanced_observer`, and to the :ref:`section_reference` to
- know what are the observable quantities. One defines as "*String*" the
- *observer* body, using a string including if necessary line breaks. It is
- recommended to use the patterns available by the argument "*Template*".
- There exist the following simple patterns: "ValuePrinter",
- "ValueSeriePrinter", "ValueSaver", "ValueSerieSaver",
- "ValuePrinterAndSaver", "ValueSeriePrinterAndSaver", "ValueGnuPlotter",
- "ValueSerieGnuPlotter", "ValuePrinterAndGnuPlotter",
- "ValueSeriePrinterAndGnuPlotter", "ValuePrinterSaverAndGnuPlotter",
- "ValueSeriePrinterSaverAndGnuPlotter", "ValueMean", "ValueStandardError",
- "ValueVariance", "ValueRMS". In the case of a definition as "*Script*", the
- file must contain only the body of the function, as described in the way of
- :ref:`section_advanced_observer`.
+ variable. Reference should be made to the description of the
+ ':ref:`ref_observers_requirements` for their list and content, and to the
+ :ref:`section_reference` to know what are the observable quantities. One
+ defines as "*String*" the *observer* body, using a string including if
+ necessary line breaks. It is recommended to use the patterns available by
+ the argument "*Template*". In the case of a definition as "*Script*", the
+ file must contain only the body of the function, as described in the
+ :ref:`ref_observers_requirements`.
Perform the calculation
+++++++++++++++++++++++
la fonctionnelle :math:`J` d'écart classique en assimilation de données
statique:
-.. math:: J(\mathbf{x})=(\mathbf{x}-\mathbf{x}^b)^T.\mathbf{B}^{-1}.(\mathbf{x}-\mathbf{x}^b)+(\mathbf{y}^o-\mathbf{H}.\mathbf{x})^T.\mathbf{R}^{-1}.(\mathbf{y}^o-\mathbf{H}.\mathbf{x})
+.. math:: J(\mathbf{x})=(\mathbf{x}-\mathbf{x}^b)^T.\mathbf{B}^{-1}.(\mathbf{x}-\mathbf{x}^b)+(\mathbf{y}^o-H(\mathbf{x}))^T.\mathbf{R}^{-1}.(\mathbf{y}^o-H(\mathbf{x}))
qui est usuellement désignée comme la fonctionnelle "*3D-VAR*" (voir par exemple
[Talagrand97]_).
second ordre (qui coûte numériquement deux fois plus cher que le premier ordre).
Si nécessaire et si possible, on peut :ref:`subsection_ref_parallel_df`. Dans
tous les cas, un mécanisme de cache interne permet de limiter le nombre
-d'évaluations de l'opérateur pour éviter des calculs redondants, au minimum
-possible du point de vue de l'exécution séquentielle ou parallèle des
-approximations numériques des opérateurs tangent et adjoint.
+d'évaluations de l'opérateur au minimum possible du point de vue de l'exécution
+séquentielle ou parallèle des approximations numériques des opérateurs tangent
+et adjoint, pour éviter des calculs redondants.
Cette première forme de définition de l'opérateur permet aisément de tester la
forme fonctionnelle avant son usage dans un cas ADAO, réduisant notablement la
**[DocR]** Interface de programmation textuelle pour l'utilisateur (API/TUI)
================================================================================
-.. warning::
-
- dans sa présente version, cette interface de programmation textuelle (TUI) est
- expérimentale, et reste donc susceptible de changements dans les prochaines
- versions.
-
Cette section présente des méthodes avancées d'usage du module ADAO à l'aide de
son interface de programmation textuelle (API/TUI). Cette interface permet de
créer un objet de calcul de manière similaire à la construction d'un cas par
**setObserver** (*Variable, Template, String, Script, Info*)
Cette commande permet de définir un *observer* sur une variable courante ou
- finale du calcul. On se reportera à la description de la manière
- d':ref:`section_advanced_observer`, et à la :ref:`section_reference` pour
- savoir quelles sont les quantités observables. On définit comme un
- "*String*" le corps de l'*observer*, en utilisant une chaine de caractères
- incluant si nécessaire des sauts de lignes. On recommande d'utiliser les
- patrons disponibles par l'argument "*Template*". On dispose des patrons
- simples suivants : "ValuePrinter", "ValueSeriePrinter", "ValueSaver",
- "ValueSerieSaver", "ValuePrinterAndSaver", "ValueSeriePrinterAndSaver",
- "ValueGnuPlotter", "ValueSerieGnuPlotter", "ValuePrinterAndGnuPlotter",
- "ValueSeriePrinterAndGnuPlotter", "ValuePrinterSaverAndGnuPlotter",
- "ValueSeriePrinterSaverAndGnuPlotter", "ValueMean", "ValueStandardError",
- "ValueVariance", "ValueRMS". Dans le cas d'une définition par "*Script*", le
- fichier indiqué doit contenir uniquement le corps de la fonction, comme
- décrit dans la manière d':ref:`section_advanced_observer`.
+ finale du calcul. On se reportera à la description des
+ :ref:`ref_observers_requirements` pour avoir leur liste et leur format, et à
+ la :ref:`section_reference` pour savoir quelles sont les quantités
+ observables. On définit comme un "*String*" le corps de l'*observer*, en
+ utilisant une chaine de caractères incluant si nécessaire des sauts de
+ lignes. On recommande d'utiliser les patrons disponibles par l'argument
+ "*Template*". Dans le cas d'une définition par "*Script*", le fichier
+ indiqué doit contenir uniquement le corps de la fonction, comme décrit dans
+ les :ref:`ref_observers_requirements`.
Effectuer le calcul
+++++++++++++++++++
default = [],
typecast = tuple,
message = "Liste de calculs supplémentaires à stocker et/ou effectuer",
- listval = ["BMA", "CurrentState", "CostFunctionJ"]
+ listval = ["BMA", "CurrentState", "CostFunctionJ", "IndexOfOptimum", "CurrentOptimum"]
)
self.defineRequiredParameter( # Pas de type
name = "Bounds",
self.DirectInnovation = [None,] # Le pas 0 n'est pas observé
def CostFunction(x):
_X = numpy.asmatrix(numpy.ravel( x )).T
- if self._parameters["StoreInternalVariables"] or "CurrentState" in self._parameters["StoreSupplementaryCalculations"]:
+ if self._parameters["StoreInternalVariables"] or \
+ "CurrentState" in self._parameters["StoreSupplementaryCalculations"] or \
+ "CurrentOptimum" in self._parameters["StoreSupplementaryCalculations"]:
self.StoredVariables["CurrentState"].store( _X )
Jb = 0.5 * (_X - Xb).T * BI * (_X - Xb)
self.DirectCalculation = [None,]
self.StoredVariables["CostFunctionJb"].store( Jb )
self.StoredVariables["CostFunctionJo"].store( Jo )
self.StoredVariables["CostFunctionJ" ].store( J )
+ if "IndexOfOptimum" in self._parameters["StoreSupplementaryCalculations"] or \
+ "CurrentOptimum" in self._parameters["StoreSupplementaryCalculations"]:
+ IndexMin = numpy.argmin( self.StoredVariables["CostFunctionJ"][nbPreviousSteps:] ) + nbPreviousSteps
+ if "IndexOfOptimum" in self._parameters["StoreSupplementaryCalculations"]:
+ self.StoredVariables["IndexOfOptimum"].store( IndexMin )
+ if "CurrentOptimum" in self._parameters["StoreSupplementaryCalculations"]:
+ self.StoredVariables["CurrentOptimum"].store( self.StoredVariables["CurrentState"][IndexMin] )
return J
#
def GradientOfCostFunction(x):
import PlatformInfo
# ==============================================================================
-class CacheManager:
+class CacheManager(object):
"""
Classe générale de gestion d'un cache de calculs
"""
def __init__(self,
- toleranceInRedundancy = 1.e-18,
- lenghtOfRedundancy = -1,
- ):
+ toleranceInRedundancy = 1.e-18,
+ lenghtOfRedundancy = -1,
+ ):
"""
Les caractéristiques de tolérance peuvent être modifées à la création.
"""
self.clearCache()
def clearCache(self):
+ "Vide le cache"
self.__listOPCV = [] # Operator Previous Calculated Points, Results, Point Norms
- # logging.debug("CM Tolerance de determination des doublons : %.2e"%self.__tolerBP)
+ # logging.debug("CM Tolerance de determination des doublons : %.2e", self.__tolerBP)
def wasCalculatedIn(self, xValue ): #, info="" ):
+ "Vérifie l'existence d'un calcul correspondant à la valeur"
__alc = False
__HxV = None
for i in xrange(min(len(self.__listOPCV),self.__lenghtOR)-1,-1,-1):
if xValue.size != self.__listOPCV[i][0].size:
- # logging.debug("CM Différence de la taille %s de X et de celle %s du point %i déjà calculé"%(xValue.shape,i,self.__listOPCP[i].shape))
+ # logging.debug("CM Différence de la taille %s de X et de celle %s du point %i déjà calculé", xValue.shape,i,self.__listOPCP[i].shape)
continue
if numpy.linalg.norm(numpy.ravel(xValue) - self.__listOPCV[i][0]) < self.__tolerBP * self.__listOPCV[i][2]:
__alc = True
__HxV = self.__listOPCV[i][1]
- # logging.debug("CM Cas%s déja calculé, portant le numéro %i"%(info,i))
+ # logging.debug("CM Cas%s déja calculé, portant le numéro %i", info, i)
break
return __alc, __HxV
def storeValueInX(self, xValue, HxValue ):
+ "Stocke un calcul correspondant à la valeur"
if self.__lenghtOR < 0:
self.__lenghtOR = 2 * xValue.size + 2
self.__initlnOR = self.__lenghtOR
while len(self.__listOPCV) > self.__lenghtOR:
- # logging.debug("CM Réduction de la liste des cas à %i éléments par suppression du premier"%self.__lenghtOR)
+ # logging.debug("CM Réduction de la liste des cas à %i éléments par suppression du premier", self.__lenghtOR)
self.__listOPCV.pop(0)
self.__listOPCV.append( (
copy.copy(numpy.ravel(xValue)),
) )
def disable(self):
+ "Inactive le cache"
self.__initlnOR = self.__lenghtOR
self.__lenghtOR = 0
def enable(self):
+ "Active le cache"
self.__lenghtOR = self.__initlnOR
# ==============================================================================
-class Operator:
+class Operator(object):
"""
Classe générale d'interface de type opérateur
"""
self.__Type = None
def disableAvoidingRedundancy(self):
+ "Inactive le cache"
Operator.CM.disable()
def enableAvoidingRedundancy(self):
+ "Active le cache"
if self.__AvoidRC:
Operator.CM.enable()
else:
Operator.CM.disable()
def isType(self):
+ "Renvoie le type"
return self.__Type
def appliedTo(self, xValue):
else: return __nbcalls[which]
def __addOneMatrixCall(self):
+ "Comptabilise un appel"
self.__NbCallsAsMatrix += 1 # Decompte local
Operator.NbCallsAsMatrix += 1 # Decompte global
def __addOneMethodCall(self):
+ "Comptabilise un appel"
self.__NbCallsAsMethod += 1 # Decompte local
Operator.NbCallsAsMethod += 1 # Decompte global
def __addOneCacheCall(self):
+ "Comptabilise un appel"
self.__NbCallsOfCached += 1 # Decompte local
Operator.NbCallsOfCached += 1 # Decompte global
# ==============================================================================
-class Algorithm:
+class Algorithm(object):
"""
Classe générale d'interface de type algorithme
On peut rajouter des variables à stocker dans l'initialisation de
l'algorithme élémentaire qui va hériter de cette classe
"""
- logging.debug("%s Initialisation"%str(name))
+ logging.debug("%s Initialisation", str(name))
self._m = PlatformInfo.SystemUsage()
#
self._name = str( name )
self.StoredVariables["SimulationQuantiles"] = Persistence.OneMatrix(name = "SimulationQuantiles")
def _pre_run(self):
- logging.debug("%s Lancement"%self._name)
- logging.debug("%s Taille mémoire utilisée de %.1f Mio"%(self._name, self._m.getUsedMemory("Mio")))
+ "Pré-calcul"
+ logging.debug("%s Lancement", self._name)
+ logging.debug("%s Taille mémoire utilisée de %.1f Mio", self._name, self._m.getUsedMemory("Mio"))
return 0
def _post_run(self,_oH=None):
+ "Post-calcul"
if ("StoreSupplementaryCalculations" in self._parameters) and \
"APosterioriCovariance" in self._parameters["StoreSupplementaryCalculations"]:
for _A in self.StoredVariables["APosterioriCovariance"]:
_C = numpy.dot(_EI, numpy.dot(_A, _EI))
self.StoredVariables["APosterioriCorrelations"].store( _C )
if _oH is not None:
- logging.debug("%s Nombre d'évaluation(s) de l'opérateur d'observation direct/tangent/adjoint.: %i/%i/%i"%(self._name, _oH["Direct"].nbcalls(0),_oH["Tangent"].nbcalls(0),_oH["Adjoint"].nbcalls(0)))
- logging.debug("%s Nombre d'appels au cache d'opérateur d'observation direct/tangent/adjoint..: %i/%i/%i"%(self._name, _oH["Direct"].nbcalls(3),_oH["Tangent"].nbcalls(3),_oH["Adjoint"].nbcalls(3)))
- logging.debug("%s Taille mémoire utilisée de %.1f Mio"%(self._name, self._m.getUsedMemory("Mio")))
- logging.debug("%s Terminé"%self._name)
+ logging.debug("%s Nombre d'évaluation(s) de l'opérateur d'observation direct/tangent/adjoint.: %i/%i/%i", self._name, _oH["Direct"].nbcalls(0),_oH["Tangent"].nbcalls(0),_oH["Adjoint"].nbcalls(0))
+ logging.debug("%s Nombre d'appels au cache d'opérateur d'observation direct/tangent/adjoint..: %i/%i/%i", self._name, _oH["Direct"].nbcalls(3),_oH["Tangent"].nbcalls(3),_oH["Adjoint"].nbcalls(3))
+ logging.debug("%s Taille mémoire utilisée de %.1f Mio", self._name, self._m.getUsedMemory("Mio"))
+ logging.debug("%s Terminé", self._name)
return 0
def get(self, key=None):
return self.StoredVariables
def __contains__(self, key=None):
- """
- Vérifie si l'une des variables stockées est identifiée par la clé.
- """
- return (key in self.StoredVariables)
+ "D.__contains__(k) -> True if D has a key k, else False"
+ return key in self.StoredVariables
def keys(self):
- """
- Renvoie la liste des clés de variables stockées.
- """
+ "D.keys() -> list of D's keys"
return self.StoredVariables.keys()
def run(self, Xb=None, Y=None, H=None, M=None, R=None, B=None, Q=None, Parameters=None):
"listval" : listval,
"message" : message,
}
- logging.debug("%s %s (valeur par défaut = %s)"%(self._name, message, self.setParameterValue(name)))
+ logging.debug("%s %s (valeur par défaut = %s)", self._name, message, self.setParameterValue(name))
def getRequiredParameters(self, noDetails=True):
"""
if maxval is not None and (numpy.array(__val, float) > maxval).any():
raise ValueError("The parameter named \"%s\" of value \"%s\" can not be greater than %s."%(name, __val, maxval))
if listval is not None:
- if typecast is list or typecast is tuple or type(__val) is list or type(__val) is tuple:
+ if typecast is list or typecast is tuple or isinstance(__val,list) or isinstance(__val,tuple):
for v in __val:
if v not in listval:
raise ValueError("The value \"%s\" of the parameter named \"%s\" is not allowed, it has to be in the list %s."%(v, name, listval))
self._parameters[k] = self.setParameterValue(k,fromDico[k])
else:
self._parameters[k] = self.setParameterValue(k)
- logging.debug("%s %s : %s"%(self._name, self.__required_parameters[k]["message"], self._parameters[k]))
+ logging.debug("%s %s : %s", self._name, self.__required_parameters[k]["message"], self._parameters[k])
# ==============================================================================
-class Diagnostic:
+class Diagnostic(object):
"""
Classe générale d'interface de type diagnostic
externe d'activation).
"""
def __init__(self, name = "", parameters = {}):
+ "Initialisation"
self.name = str(name)
self.parameters = dict( parameters )
raise NotImplementedError("Diagnostic activation method has not been implemented!")
# ==============================================================================
-class Covariance:
+class Covariance(object):
"""
Classe générale d'interface de type covariance
"""
def __init__(self,
- name = "GenericCovariance",
- asCovariance = None,
- asEyeByScalar = None,
- asEyeByVector = None,
- asCovObject = None,
- toBeChecked = False,
- ):
+ name = "GenericCovariance",
+ asCovariance = None,
+ asEyeByScalar = None,
+ asEyeByVector = None,
+ asCovObject = None,
+ toBeChecked = False,
+ ):
"""
Permet de définir une covariance :
- asCovariance : entrée des données, comme une matrice compatible avec
self.__validate()
def __validate(self):
+ "Validation"
if self.ismatrix() and min(self.shape) != max(self.shape):
raise ValueError("The given matrix for %s is not a square one, its shape is %s. Please check your matrix input."%(self.__name,self.shape))
if self.isobject() and min(self.shape) != max(self.shape):
raise ValueError("The %s covariance object is not symmetric positive-definite. Please check your matrix input."%(self.__name,))
def isscalar(self):
+ "Vérification du type interne"
return self.__is_scalar
def isvector(self):
+ "Vérification du type interne"
return self.__is_vector
def ismatrix(self):
+ "Vérification du type interne"
return self.__is_matrix
def isobject(self):
+ "Vérification du type interne"
return self.__is_object
def getI(self):
+ "Inversion"
if self.ismatrix():
return Covariance(self.__name+"I", asCovariance = self.__C.I )
elif self.isvector():
return None # Indispensable
def getT(self):
+ "Transposition"
if self.ismatrix():
return Covariance(self.__name+"T", asCovariance = self.__C.T )
elif self.isvector():
return Covariance(self.__name+"T", asCovObject = self.__C.getT() )
def cholesky(self):
+ "Décomposition de Cholesky"
if self.ismatrix():
return Covariance(self.__name+"C", asCovariance = numpy.linalg.cholesky(self.__C) )
elif self.isvector():
return Covariance(self.__name+"C", asCovObject = self.__C.cholesky() )
def choleskyI(self):
+ "Inversion de la décomposition de Cholesky"
if self.ismatrix():
return Covariance(self.__name+"H", asCovariance = numpy.linalg.cholesky(self.__C).I )
elif self.isvector():
return Covariance(self.__name+"H", asCovObject = self.__C.choleskyI() )
def diag(self, msize=None):
+ "Diagonale de la matrice"
if self.ismatrix():
return numpy.diag(self.__C)
elif self.isvector():
return self.__C.diag()
def asfullmatrix(self, msize=None):
+ "Matrice pleine"
if self.ismatrix():
return self.__C
elif self.isvector():
return self.__C.asfullmatrix()
def trace(self, msize=None):
+ "Trace de la matrice"
if self.ismatrix():
return numpy.trace(self.__C)
elif self.isvector():
return self.__C.trace()
def __repr__(self):
+ "x.__repr__() <==> repr(x)"
return repr(self.__C)
def __str__(self):
+ "x.__str__() <==> str(x)"
return str(self.__C)
def __add__(self, other):
+ "x.__add__(y) <==> x+y"
if self.ismatrix() or self.isobject():
return self.__C + numpy.asmatrix(other)
elif self.isvector() or self.isscalar():
return numpy.asmatrix(_A)
def __radd__(self, other):
+ "x.__radd__(y) <==> y+x"
raise NotImplementedError("%s covariance matrix __radd__ method not available for %s type!"%(self.__name,type(other)))
def __sub__(self, other):
+ "x.__sub__(y) <==> x-y"
if self.ismatrix() or self.isobject():
return self.__C - numpy.asmatrix(other)
elif self.isvector() or self.isscalar():
return numpy.asmatrix(_A)
def __rsub__(self, other):
+ "x.__rsub__(y) <==> y-x"
raise NotImplementedError("%s covariance matrix __rsub__ method not available for %s type!"%(self.__name,type(other)))
def __neg__(self):
+ "x.__neg__() <==> -x"
return - self.__C
def __mul__(self, other):
+ "x.__mul__(y) <==> x*y"
if self.ismatrix() and isinstance(other,numpy.matrix):
return self.__C * other
elif self.ismatrix() and (isinstance(other,numpy.ndarray) \
raise NotImplementedError("%s covariance matrix __mul__ method not available for %s type!"%(self.__name,type(other)))
def __rmul__(self, other):
+ "x.__rmul__(y) <==> y*x"
if self.ismatrix() and isinstance(other,numpy.matrix):
return other * self.__C
elif self.isvector() and isinstance(other,numpy.matrix):
raise NotImplementedError("%s covariance matrix __rmul__ method not available for %s type!"%(self.__name,type(other)))
def __len__(self):
+ "x.__len__() <==> len(x)"
return self.shape[0]
# ==============================================================================
-def CostFunction3D(
- _x,
- _Hm = None, # Pour simuler Hm(x) : HO["Direct"].appliedTo
- _HmX = None, # Simulation déjà faite de Hm(x)
- _arg = None, # Arguments supplementaires pour Hm, sous la forme d'un tuple
- _BI = None,
- _RI = None,
- _Xb = None,
- _Y = None,
- _SIV = False, # A résorber pour la 8.0
- _SSC = [], # self._parameters["StoreSupplementaryCalculations"]
- _nPS = 0, # nbPreviousSteps
- _QM = "DA", # QualityMeasure
- _SSV = {}, # Entrée et/ou sortie : self.StoredVariables
- _fRt = False, # Restitue ou pas la sortie étendue
- _sSc = True, # Stocke ou pas les SSC
- ):
+def CostFunction3D(_x,
+ _Hm = None, # Pour simuler Hm(x) : HO["Direct"].appliedTo
+ _HmX = None, # Simulation déjà faite de Hm(x)
+ _arg = None, # Arguments supplementaires pour Hm, sous la forme d'un tuple
+ _BI = None,
+ _RI = None,
+ _Xb = None,
+ _Y = None,
+ _SIV = False, # A résorber pour la 8.0
+ _SSC = [], # self._parameters["StoreSupplementaryCalculations"]
+ _nPS = 0, # nbPreviousSteps
+ _QM = "DA", # QualityMeasure
+ _SSV = {}, # Entrée et/ou sortie : self.StoredVariables
+ _fRt = False, # Restitue ou pas la sortie étendue
+ _sSc = True, # Stocke ou pas les SSC
+ ):
"""
Fonction-coût générale utile pour les algorithmes statiques/3D : 3DVAR, BLUE
et dérivés, Kalman et dérivés, LeastSquares, SamplingTest, PSO, SA, Tabu,
"CostFunctionJb",
"CostFunctionJo",
"CurrentOptimum",
- "CurrentState",
+ "CurrentState",
"IndexOfOptimum",
"SimulatedObservationAtCurrentOptimum",
"SimulatedObservationAtCurrentState",
- ]:
+ ]:
if k not in _SSV:
_SSV[k] = []
if hasattr(_SSV[k],"store"):
_HX = _HmX
else:
if _Hm is None:
- raise ValueError("%s Operator has to be defined."%(self.__name,))
+ raise ValueError("COSTFUNCTION3D Operator has to be defined.")
if _arg is None:
_HX = _Hm( _X )
else:
LOGFILE = os.path.join(os.path.abspath(os.curdir),"AssimilationStudy.log")
# ==============================================================================
-class ExtendedLogging:
+class ExtendedLogging(object):
+ """
+ Logger général pour disposer conjointement de la sortie standard et de la
+ sortie sur fichier
+ """
def __init__(self, level=logging.WARNING):
"""
Initialise un logging à la console pour TOUS les niveaux de messages.
from PlatformInfo import PathManagement ; PathManagement()
# ==============================================================================
-class Persistence:
+class Persistence(object):
"""
Classe générale de persistence définissant les accesseurs nécessaires
(Template)
name : nom courant
unit : unité
basetype : type de base de l'objet stocké à chaque pas
-
+
La gestion interne des données est exclusivement basée sur les variables
initialisées ici (qui ne sont pas accessibles depuis l'extérieur des
objets comme des attributs) :
__basetype : le type de base de chaque valeur, sous la forme d'un type
- permettant l'instanciation ou le casting Python
+ permettant l'instanciation ou le casting Python
__values : les valeurs de stockage. Par défaut, c'est None
"""
self.__name = str(name)
#
self.__basetype = basetype
#
- self.__values = []
- self.__tags = []
+ self.__values = []
+ self.__tags = []
#
self.__dynamic = False
+ self.__gnuplot = None
+ self.__g = None
+ self.__title = None
+ self.__ltitle = None
+ self.__pause = None
#
self.__dataobservers = []
-
+
def basetype(self, basetype=None):
"""
Renvoie ou met en place le type de base des objets stockés
else:
self.__values.pop()
self.__tags.pop()
-
+
def shape(self):
"""
Renvoie la taille sous forme numpy du dernier objet stocké. Si c'est un
# ---------------------------------------------------------
def __str__(self):
+ "x.__str__() <==> str(x)"
msg = " Index Value Tags\n"
for i,v in enumerate(self.__values):
msg += " i=%05i %10s %s\n"%(i,v,self.__tags[i])
return msg
-
+
def __len__(self):
+ "x.__len__() <==> len(x)"
return len(self.__values)
-
+
def __getitem__(self, index=None ):
+ "x.__getitem__(y) <==> x[y]"
return copy.copy(self.__values[index])
-
+
def count(self, value):
+ "L.count(value) -> integer -- return number of occurrences of value"
return self.__values.count(value)
-
+
def index(self, value, start=0, stop=None):
+ "L.index(value, [start, [stop]]) -> integer -- return first index of value."
if stop is None : stop = len(self.__values)
return self.__values.index(value, start, stop)
# ---------------------------------------------------------
def __filteredIndexes(self, **kwargs):
+ "Function interne filtrant les index"
__indexOfFilteredItems = range(len(self.__tags))
__filteringKwTags = kwargs.keys()
if len(__filteringKwTags) > 0:
# ---------------------------------------------------------
def values(self, **kwargs):
+ "D.values() -> list of D's values"
__indexOfFilteredItems = self.__filteredIndexes(**kwargs)
return [self.__values[i] for i in __indexOfFilteredItems]
def keys(self, keyword=None , **kwargs):
+ "D.keys() -> list of D's keys"
__indexOfFilteredItems = self.__filteredIndexes(**kwargs)
__keys = []
for i in __indexOfFilteredItems:
return __keys
def items(self, keyword=None , **kwargs):
+ "D.items() -> list of D's (key, value) pairs, as 2-tuples"
__indexOfFilteredItems = self.__filteredIndexes(**kwargs)
__pairs = []
for i in __indexOfFilteredItems:
if keyword in self.__tags[i]:
- __pairs.append( [self.__tags[i][keyword], self.__values[i]] )
+ __pairs.append( (self.__tags[i][keyword], self.__values[i]) )
else:
- __pairs.append( [None, self.__values[i]] )
+ __pairs.append( (None, self.__values[i]) )
return __pairs
def tagkeys(self):
+ "D.tagkeys() -> list of D's tag keys"
__allKeys = []
for dicotags in self.__tags:
__allKeys.extend( dicotags.keys() )
# return [self.__values[i] for i in __indexOfFilteredItems]
def tagserie(self, item=None, withValues=False, outputTag=None, **kwargs):
+ "D.tagserie() -> list of D's tag serie"
if item is None:
__indexOfFilteredItems = self.__filteredIndexes(**kwargs)
else:
__indexOfFilteredItems = [item,]
#
# Dans le cas où la sortie donne les valeurs d'un "outputTag"
- if outputTag is not None and type(outputTag) is str :
+ if outputTag is not None and isinstance(outputTag,str) :
outputValues = []
for index in __indexOfFilteredItems:
if outputTag in self.__tags[index].keys():
# ---------------------------------------------------------
# Pour compatibilite
def stepnumber(self):
+ "Nombre de pas"
return len(self.__values)
# Pour compatibilite
def stepserie(self, **kwargs):
+ "Nombre de pas filtrés"
__indexOfFilteredItems = self.__filteredIndexes(**kwargs)
return __indexOfFilteredItems
Renvoie la série, contenant à chaque pas, l'écart-type des données
au pas. Il faut que le type de base soit compatible avec les types
élémentaires numpy.
-
+
ddof : c'est le nombre de degrés de liberté pour le calcul de
l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
"""
raise TypeError("Base type is incompatible with numpy")
def __preplots(self,
- title = "",
- xlabel = "",
- ylabel = "",
- ltitle = None,
- geometry = "600x400",
- persist = False,
- pause = True,
- ):
+ title = "",
+ xlabel = "",
+ ylabel = "",
+ ltitle = None,
+ geometry = "600x400",
+ persist = False,
+ pause = True,
+ ):
+ "Préparation des plots"
#
# Vérification de la disponibilité du module Gnuplot
try:
self.__ltitle = ltitle
self.__pause = pause
- def plots(self, item=None, step=None,
- steps = None,
- title = "",
- xlabel = "",
- ylabel = "",
- ltitle = None,
- geometry = "600x400",
- filename = "",
- dynamic = False,
- persist = False,
- pause = True,
- ):
+ def plots(self,
+ item = None,
+ step = None,
+ steps = None,
+ title = "",
+ xlabel = "",
+ ylabel = "",
+ ltitle = None,
+ geometry = "600x400",
+ filename = "",
+ dynamic = False,
+ persist = False,
+ pause = True,
+ ):
"""
Renvoie un affichage de la valeur à chaque pas, si elle est compatible
avec un affichage Gnuplot (donc essentiellement un vecteur). Si
i = -1
for index in indexes:
self.__g('set title "'+str(title).encode('ascii','replace')+' (pas '+str(index)+')"')
- if ( type(steps) is list ) or ( type(steps) is type(numpy.array([])) ):
+ if isinstance(steps,list) or isinstance(steps,numpy.ndarray):
Steps = list(steps)
else:
Steps = range(len(self.__values[index]))
Renvoie l'écart-type de toutes les valeurs sans tenir compte de la
longueur des pas. Il faut que le type de base soit compatible avec
les types élémentaires numpy.
-
+
ddof : c'est le nombre de degrés de liberté pour le calcul de
l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
"""
# "tofile", "min"...
def plot(self,
- steps = None,
- title = "",
- xlabel = "",
- ylabel = "",
- ltitle = None,
- geometry = "600x400",
- filename = "",
- persist = False,
- pause = True,
+ steps = None,
+ title = "",
+ xlabel = "",
+ ylabel = "",
+ ltitle = None,
+ geometry = "600x400",
+ filename = "",
+ persist = False,
+ pause = True,
):
"""
Renvoie un affichage unique pour l'ensemble des valeurs à chaque pas, si
self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
if ltitle is None:
ltitle = ""
- if ( type(steps) is list ) or ( type(steps) is type(numpy.array([])) ):
+ if isinstance(steps,list) or isinstance(steps, numpy.ndarray):
Steps = list(steps)
else:
Steps = range(len(self.__values[0]))
raw_input('Please press return to continue...\n')
# ---------------------------------------------------------
- def setDataObserver(self,
- HookFunction = None,
- HookParameters = None,
- Scheduler = None,
- ):
+ def setDataObserver(self, HookFunction = None, HookParameters = None, Scheduler = None):
"""
Association à la variable d'un triplet définissant un observer
-
+
Le Scheduler attendu est une fréquence, une simple liste d'index ou un
xrange des index.
"""
# Vérification du Scheduler
# -------------------------
maxiter = int( 1e9 )
- if type(Scheduler) is int: # Considéré comme une fréquence à partir de 0
+ if isinstance(Scheduler,int): # Considéré comme une fréquence à partir de 0
Schedulers = xrange( 0, maxiter, int(Scheduler) )
- elif type(Scheduler) is xrange: # Considéré comme un itérateur
+ elif isinstance(Scheduler,xrange): # Considéré comme un itérateur
Schedulers = Scheduler
- elif type(Scheduler) is list: # Considéré comme des index explicites
- Schedulers = map( long, Scheduler )
- else: # Dans tous les autres cas, activé par défaut
+ elif isinstance(Scheduler,list): # Considéré comme des index explicites
+ Schedulers = [long(i) for i in Scheduler] # map( long, Scheduler )
+ else: # Dans tous les autres cas, activé par défaut
Schedulers = xrange( 0, maxiter )
#
# Stockage interne de l'observer dans la variable
# -----------------------------------------------
self.__dataobservers.append( [HookFunction, HookParameters, Schedulers] )
- def removeDataObserver(self,
- HookFunction = None,
- ):
+ def removeDataObserver(self, HookFunction = None):
"""
Suppression d'un observer nommé sur la variable.
-
+
On peut donner dans HookFunction la meme fonction que lors de la
définition, ou un simple string qui est le nom de la fonction.
-
"""
if hasattr(HookFunction,"func_name"):
name = str( HookFunction.func_name )
- elif type(HookFunction) is str:
+ elif isinstance(HookFunction,str):
name = str( HookFunction )
else:
name = None
if name is hf.func_name: index_to_remove.append( i )
index_to_remove.reverse()
for i in index_to_remove:
- self.__dataobservers.pop( i )
+ self.__dataobservers.pop( i )
# ==============================================================================
class OneScalar(Persistence):
def __init__(self, name="", unit="", basetype = list):
Persistence.__init__(self, name, unit, basetype)
-def NoType( value ): return value
+def NoType( value ):
+ "Fonction transparente, sans effet sur son argument"
+ return value
class OneNoType(Persistence):
"""
Persistence.__init__(self, name, unit, basetype)
# ==============================================================================
-class CompositePersistence:
+class CompositePersistence(object):
"""
Structure de stockage permettant de rassembler plusieurs objets de
persistence.
-
+
Des objets par défaut sont prévus, et des objets supplémentaires peuvent
être ajoutés.
"""
def __init__(self, name="", defaults=True):
"""
name : nom courant
-
+
La gestion interne des données est exclusivement basée sur les variables
initialisées ici (qui ne sont pas accessibles depuis l'extérieur des
objets comme des attributs) :
# ---------------------------------------------------------
# Méthodes d'accès de type dictionnaire
def __getitem__(self, name=None ):
+ "x.__getitem__(y) <==> x[y]"
return self.get_object( name )
def __setitem__(self, name=None, objet=None ):
+ "x.__setitem__(i, y) <==> x[i]=y"
self.set_object( name, objet )
def keys(self):
+ "D.keys() -> list of D's keys"
return self.get_stored_objects(hideVoidObjects = False)
def values(self):
+ "D.values() -> list of D's values"
return self.__StoredObjects.values()
def items(self):
+ "D.items() -> list of D's (key, value) pairs, as 2-tuples"
return self.__StoredObjects.items()
# ---------------------------------------------------------
def get_stored_objects(self, hideVoidObjects = False):
+ "Renvoie la liste des objets présents"
objs = self.__StoredObjects.keys()
if hideVoidObjects:
usedObjs = []
import os
# ==============================================================================
-class PlatformInfo:
+class PlatformInfo(object):
"""
Rassemblement des informations sur le code et la plateforme
"""
+ def __init__(self):
+ "Sans effet"
+ pass
+
def getName(self):
"Retourne le nom de l'application"
- import version
- return version.name
+ import version as dav
+ return dav.name
def getVersion(self):
"Retourne le numéro de la version"
- import version
- return version.version
+ import version as dav
+ return dav.version
def getDate(self):
"Retourne la date de création de la version"
- import version
- return version.date
-
+ import version as dav
+ return dav.date
+
def getPythonVersion(self):
"Retourne la version de python disponible"
import sys
- return ".".join(map(str,sys.version_info[0:3]))
+ return ".".join([str(x) for x in sys.version_info[0:3]]) # map(str,sys.version_info[0:3]))
def getNumpyVersion(self):
"Retourne la version de numpy disponible"
try:
import matplotlib
return matplotlib.__version__
- except:
+ except ImportError:
return "0.0.0"
def getGnuplotVersion(self):
try:
import Gnuplot
return Gnuplot.__version__
- except:
+ except ImportError:
return "0.0"
def getSphinxVersion(self):
try:
import sphinx
return sphinx.__version__
- except:
+ except ImportError:
return "0.0.0"
def getCurrentMemorySize(self):
return 1
def __str__(self):
- import version
- return "%s %s (%s)"%(version.name,version.version,version.date)
+ import version as dav
+ return "%s %s (%s)"%(dav.name,dav.version,dav.date)
# ==============================================================================
def uniq(sequence):
return [x for x in sequence if x not in __seen and not __seen.add(x)]
# ==============================================================================
-class PathManagement:
+class PathManagement(object):
"""
Mise à jour du path système pour les répertoires d'outils
"""
def __init__(self):
+ "Déclaration des répertoires statiques"
import sys
parent = os.path.abspath(os.path.join(os.path.dirname(__file__),".."))
self.__paths = {}
return self.__paths
# ==============================================================================
-class SystemUsage:
+class SystemUsage(object):
"""
Permet de récupérer les différentes tailles mémoires du process courant
"""
_proc_status = '/proc/%d/status' % os.getpid()
_memo_status = '/proc/meminfo'
_scale = {
- 'o': 1.0, 'ko' : 1.e3, 'Mo' : 1.e6, 'Go' : 1.e9, # Multiples SI de l'octet
- 'kio': 1024.0, 'Mio': 1024.0*1024.0, 'Gio': 1024.0*1024.0*1024.0, # Multiples binaires de l'octet
- 'B': 1.0, 'kB' : 1024.0, 'MB' : 1024.0*1024.0, 'GB' : 1024.0*1024.0*1024.0, # Multiples binaires du byte=octet
- }
+ 'o' : 1.0, # Multiples SI de l'octet
+ 'ko' : 1.e3,
+ 'Mo' : 1.e6,
+ 'Go' : 1.e9,
+ 'kio': 1024.0, # Multiples binaires de l'octet
+ 'Mio': 1024.0*1024.0,
+ 'Gio': 1024.0*1024.0*1024.0,
+ 'B': 1.0, # Multiples binaires du byte=octet
+ 'kB' : 1024.0,
+ 'MB' : 1024.0*1024.0,
+ 'GB' : 1024.0*1024.0*1024.0,
+ }
+ #
+ def __init__(self):
+ "Sans effet"
+ pass
#
def _VmA(self, VmKey, unit):
+ "Lecture des paramètres mémoire de la machine"
try:
t = open(self._memo_status)
v = t.read()
t.close()
- except:
+ except IOError:
return 0.0 # non-Linux?
i = v.index(VmKey) # get VmKey line e.g. 'VmRSS: 9999 kB\n ...'
v = v[i:].split(None, 3) # whitespace
self._VmA('Cached:', unit) + self._VmA('SwapCached:', unit)
#
def _VmB(self, VmKey, unit):
+ "Lecture des paramètres mémoire du processus"
try:
t = open(self._proc_status)
v = t.read()
t.close()
- except:
+ except IOError:
return 0.0 # non-Linux?
i = v.index(VmKey) # get VmKey line e.g. 'VmRSS: 9999 kB\n ...'
v = v[i:].split(None, 3) # whitespace
self.__order = -1
def store( self, name = None, content = None, fr_FR = "", en_EN = "", order = "next" ):
+ "D.store(k, c, fr_FR, en_EN, o) -> Store template k and its main characteristics"
if name is None or content is None:
raise ValueError("To be consistent, the storage of a template must provide a name and a content.")
if order == "next":
}
def keys(self):
+ "D.keys() -> list of D's keys"
__keys = self.__values.keys()
__keys.sort()
return __keys
def has_key(self, name):
- return self.__values.has_key(name)
+ "D.has_key(k) -> True if D has a key k, else False"
+ return name in self.__values
+
+ def __contains__(self, name):
+ "D.__contains__(k) -> True if D has a key k, else False"
+ return name in self.__values
def __len__(self):
+ "x.__len__() <==> len(x)"
return len(self.__values)
def __getitem__(self, name=None ):
+ "x.__getitem__(y) <==> x[y]"
return self.__values[name]['content']
def getdoc(self, name = None, lang = "fr_FR"):
+ "D.getdoc(k, l) -> Return documentation of key k in language l"
if lang not in self.__values[name]: lang = self.__preferedLanguage
return self.__values[name][lang]
def keys_in_presentation_order(self):
- "Restitue l'ordre de présentation requis"
+ "D.keys_in_presentation_order() -> list of D's keys in presentation order"
__orders = []
for k in self.keys():
__orders.append( self.__values[k]['order'] )
else:
raise ValueError("the variable named '%s' is not allowed."%str(Concept))
except Exception as e:
- if type(e) == type(SyntaxError()): msg = "at %s: %s"%(e.offset, e.text)
+ if isinstance(e, SyntaxError): msg = "at %s: %s"%(e.offset, e.text)
else: msg = ""
raise ValueError("during settings, the following error occurs:\n\n%s %s\n\nSee also the potential messages, which can show the origin of the above error, in the launching terminal."%(str(e),msg))
"Définition d'une entrée de calcul"
self.__dumper.register("setControlModel", dir(), locals())
__Parameters = {}
- if Parameters is not None and type(Parameters) == type({}):
- if Parameters.has_key("DifferentialIncrement"):
+ if (Parameters is not None) and isinstance(Parameters, dict):
+ if "DifferentialIncrement" in Parameters:
__Parameters["withIncrement"] = Parameters["DifferentialIncrement"]
- if Parameters.has_key("CenteredFiniteDifference"):
+ if "CenteredFiniteDifference" in Parameters:
__Parameters["withCenteredDF"] = Parameters["CenteredFiniteDifference"]
if Script is not None:
__Matrix, __Function = None, None
__Function.update({"useApproximatedDerivatives":True})
__Function.update(__Parameters)
elif ThreeFunctions is not None:
- if (type(ThreeFunctions) is not type({})) or \
- not ThreeFunctions.has_key("Direct") or \
- not ThreeFunctions.has_key("Tangent") or \
- not ThreeFunctions.has_key("Adjoint"):
+ if (not isinstance(ThreeFunctions, dict)) or \
+ "Direct" not in ThreeFunctions or \
+ "Tangent" not in ThreeFunctions or \
+ "Adjoint" not in ThreeFunctions:
raise ValueError("ThreeFunctions has to be a dictionnary and to have the 3 keys Direct, Tangent, Adjoint")
__Function = ThreeFunctions
__Function.update(__Parameters)
"Définition d'une entrée de calcul"
self.__dumper.register("setEvolutionModel", dir(), locals())
__Parameters = {}
- if Parameters is not None and type(Parameters) == type({}):
- if Parameters.has_key("DifferentialIncrement"):
+ if (Parameters is not None) and isinstance(Parameters, dict):
+ if "DifferentialIncrement" in Parameters:
__Parameters["withIncrement"] = Parameters["DifferentialIncrement"]
- if Parameters.has_key("CenteredFiniteDifference"):
+ if "CenteredFiniteDifference" in Parameters:
__Parameters["withCenteredDF"] = Parameters["CenteredFiniteDifference"]
- if Parameters.has_key("EnableMultiProcessing"):
+ if "EnableMultiProcessing" in Parameters:
__Parameters["withmpEnabled"] = Parameters["EnableMultiProcessing"]
- if Parameters.has_key("NumberOfProcesses"):
+ if "NumberOfProcesses" in Parameters:
__Parameters["withmpWorkers"] = Parameters["NumberOfProcesses"]
if Script is not None:
__Matrix, __Function = None, None
__Function.update({"useApproximatedDerivatives":True})
__Function.update(__Parameters)
elif ThreeFunctions is not None:
- if (type(ThreeFunctions) is not type({})) or \
- not ThreeFunctions.has_key("Direct") or \
- not ThreeFunctions.has_key("Tangent") or \
- not ThreeFunctions.has_key("Adjoint"):
+ if (not isinstance(ThreeFunctions, dict)) or \
+ "Direct" not in ThreeFunctions or \
+ "Tangent" not in ThreeFunctions or \
+ "Adjoint" not in ThreeFunctions:
raise ValueError("ThreeFunctions has to be a dictionnary and to have the 3 keys Direct, Tangent, Adjoint")
__Function = ThreeFunctions
__Function.update(__Parameters)
"Définition d'une entrée de calcul"
self.__dumper.register("setObservationOperator", dir(), locals())
__Parameters = {}
- if Parameters is not None and type(Parameters) == type({}):
- if Parameters.has_key("DifferentialIncrement"):
+ if (Parameters is not None) and isinstance(Parameters, dict):
+ if "DifferentialIncrement" in Parameters:
__Parameters["withIncrement"] = Parameters["DifferentialIncrement"]
- if Parameters.has_key("CenteredFiniteDifference"):
+ if "CenteredFiniteDifference" in Parameters:
__Parameters["withCenteredDF"] = Parameters["CenteredFiniteDifference"]
- if Parameters.has_key("EnableMultiProcessing"):
+ if "EnableMultiProcessing" in Parameters:
__Parameters["EnableMultiProcessing"] = Parameters["EnableMultiProcessing"]
__Parameters["withmpEnabled"] = Parameters["EnableMultiProcessing"]
- if Parameters.has_key("NumberOfProcesses"):
+ if "NumberOfProcesses" in Parameters:
__Parameters["NumberOfProcesses"] = Parameters["NumberOfProcesses"]
__Parameters["withmpWorkers"] = Parameters["NumberOfProcesses"]
if Script is not None:
__Function.update({"useApproximatedDerivatives":True})
__Function.update(__Parameters)
elif ThreeFunctions is not None:
- if (type(ThreeFunctions) is not type({})) or \
- not ThreeFunctions.has_key("Direct") or \
- not ThreeFunctions.has_key("Tangent") or \
- not ThreeFunctions.has_key("Adjoint"):
+ if (not isinstance(ThreeFunctions, dict)) or \
+ "Direct" not in ThreeFunctions or \
+ "Tangent" not in ThreeFunctions or \
+ "Adjoint" not in ThreeFunctions:
raise ValueError("ThreeFunctions has to be a dictionnary and to have the 3 keys Direct, Tangent, Adjoint")
__Function = ThreeFunctions
__Function.update(__Parameters)
#
if String is not None:
__FunctionText = String
- elif (Template is not None) and Templates.ObserverTemplates.has_key(Template):
+ elif (Template is not None) and (Template in Templates.ObserverTemplates):
__FunctionText = Templates.ObserverTemplates[Template]
elif Script is not None:
__FunctionText = _ImportFromScript(Script).getstring()
try:
self.__adaoStudy.analyze()
except Exception as e:
- if type(e) == type(SyntaxError()): msg = "at %s: %s"%(e.offset, e.text)
+ if isinstance(e, SyntaxError): msg = "at %s: %s"%(e.offset, e.text)
else: msg = ""
raise ValueError("during execution, the following error occurs:\n\n%s %s\n\nSee also the potential messages, which can show the origin of the above error, in the launching terminal."%(str(e),msg))
# -- Infos from daAlgorithms --
AssimAlgos = [
"3DVAR",
+ "4DVAR",
"Blue",
"ExtendedBlue",
"EnsembleBlue",
"Observation", "ObservationError",
"ObservationOperator",
]
+AlgoDataRequirements["4DVAR"] = [
+ "Background", "BackgroundError",
+ "Observation", "ObservationError",
+ "ObservationOperator",
+ ]
AlgoDataRequirements["Blue"] = [
"Background", "BackgroundError",
"Observation", "ObservationError",
AlgoType = {}
AlgoType["3DVAR"] = "Optim"
+AlgoType["4DVAR"] = "Optim"
AlgoType["Blue"] = "Optim"
AlgoType["ExtendedBlue"] = "Optim"
AlgoType["EnsembleBlue"] = "Optim"