From ce5147cbb50f6a0f11911cfe5b12196bbccb482d Mon Sep 17 00:00:00 2001 From: Jean-Philippe ARGAUD Date: Tue, 11 Jun 2024 16:53:39 +0200 Subject: [PATCH] Models and code update for crossobs, minor review update --- src/daComposant/daCore/Aidsm.py | 9 +++- src/daComposant/daCore/BasicObjects.py | 42 +++++++++++++++---- src/daComposant/daCore/Interfaces.py | 14 ++++--- src/daComposant/daCore/Persistence.py | 33 +++++++++++---- src/daComposant/daCore/PlatformInfo.py | 1 + .../TwoDimensionalRosenbrockFunctionR1960.py | 8 +++- .../modelica_libraries/around_simulation.py | 6 +-- 7 files changed, 84 insertions(+), 29 deletions(-) diff --git a/src/daComposant/daCore/Aidsm.py b/src/daComposant/daCore/Aidsm.py index 6e0a336..0fa4cb6 100644 --- a/src/daComposant/daCore/Aidsm.py +++ b/src/daComposant/daCore/Aidsm.py @@ -109,6 +109,7 @@ class Aidsm(object): Checked = False, ColMajor = False, ColNames = None, + CrossObs = False, DataFile = None, DiagonalSparseMatrix = None, ExtraArguments = None, @@ -125,6 +126,7 @@ class Aidsm(object): Script = None, Stored = False, String = None, + SyncObs = True, Template = None, ThreeFunctions = None, Variable = None, @@ -154,7 +156,8 @@ class Aidsm(object): elif Concept == "NoDebug": self.setNoDebug() elif Concept == "Observer": - self.setObserver( Variable, Template, String, Script, Info, ObjectFunction, Scheduler ) + self.setObserver( Variable, Template, String, Script, Info, + ObjectFunction, CrossObs, SyncObs, Scheduler ) elif Concept == "UserPostAnalysis": self.setUserPostAnalysis( Template, String, Script ) elif Concept == "SupplementaryParameters": @@ -598,6 +601,8 @@ class Aidsm(object): Script = None, Info = None, ObjectFunction = None, + CrossObs = False, + SyncObs = True, Scheduler = None ): "Définition d'un concept de calcul" Concept = "Observer" @@ -610,6 +615,8 @@ class Aidsm(object): asScript = self.__with_directory(Script), asObsObject = ObjectFunction, withInfo = Info, + crossObs = CrossObs, + syncObs = SyncObs, scheduledBy = Scheduler, withAlgo = self.__adaoObject["AlgorithmParameters"] )) diff --git a/src/daComposant/daCore/BasicObjects.py b/src/daComposant/daCore/BasicObjects.py index 60a478c..838cb1d 100644 --- a/src/daComposant/daCore/BasicObjects.py +++ b/src/daComposant/daCore/BasicObjects.py @@ -1512,7 +1512,8 @@ class AlgorithmAndParameters(object): "Renvoie la liste des attributs selon l'algorithme" return self.__algorithm.setAttributes() - def setObserver(self, __V, __O, __I, __S): + def setObserver(self, __V, __O, __I, __A, __S): + "Associe un observer à une variable unique" if self.__algorithm is None \ or isinstance(self.__algorithm, dict) \ or not hasattr(self.__algorithm, "StoredVariables"): @@ -1520,7 +1521,24 @@ class AlgorithmAndParameters(object): if __V not in self.__algorithm: raise ValueError("An observer requires to be set on a variable named %s which does not exist."%__V) else: - self.__algorithm.StoredVariables[ __V ].setDataObserver( Scheduler = __S, HookFunction = __O, HookParameters = __I ) + self.__algorithm.StoredVariables[ __V ].setDataObserver( HookFunction = __O, HookParameters = __I, Scheduler = __S ) + + def setCrossObserver(self, __V, __O, __I, __A, __S): + "Associe un observer à une collection ordonnée de variables" + if self.__algorithm is None \ + or isinstance(self.__algorithm, dict) \ + or not hasattr(self.__algorithm, "StoredVariables"): + raise ValueError("No observer can be build before choosing an algorithm.") + if not isinstance(__V, (list, tuple)): + raise ValueError("A cross observer requires to be set on a variable series which is not the case of %s."%__V) + if len(__V) != len(__I): + raise ValueError("The number of information fields has to be the same than the number of variables on which to set the observer.") + # + for __eV in __V: + if __eV not in self.__algorithm: + raise ValueError("An observer requires to be set on a variable named %s which does not exist."%__eV) + else: + self.__algorithm.StoredVariables[ __eV ].setDataObserver( HookFunction = __O, HookParameters = __I, Scheduler = __S, Order = __V, OSync = __A, DOVar = self.__algorithm.StoredVariables ) def removeObserver(self, __V, __O, __A = False): if self.__algorithm is None \ @@ -1818,6 +1836,8 @@ class DataObserver(object): asScript = None, asObsObject = None, withInfo = None, + crossObs = False, + syncObs = True, scheduledBy = None, withAlgo = None ): """ @@ -1829,10 +1849,12 @@ class DataObserver(object): # if onVariable is None: raise ValueError("setting an observer has to be done over a variable name or a list of variable names, not over None.") - elif type(onVariable) in (tuple, list): + elif isinstance(onVariable, (tuple, list)): self.__V = tuple(map( str, onVariable )) if withInfo is None: self.__I = self.__V + elif crossObs or isinstance(withInfo, (tuple, list)): + self.__I = withInfo else: self.__I = (str(withInfo),) * len(self.__V) elif isinstance(onVariable, str): @@ -1852,12 +1874,14 @@ class DataObserver(object): self.__O = __Function.getfunc() # for k in range(len(self.__V)): - ename = self.__V[k] - einfo = self.__I[k] - if ename not in withAlgo: - raise ValueError("An observer is asked to be set on a variable named %s which does not exist."%ename) - else: - withAlgo.setObserver(ename, self.__O, einfo, scheduledBy) + if self.__V[k] not in withAlgo: + raise ValueError("An observer is asked to be set on a variable named %s which does not exist."%self.__V[k]) + # + if bool(crossObs): + withAlgo.setCrossObserver(self.__V, self.__O, self.__I, syncObs, scheduledBy) + else: + for k in range(len(self.__V)): + withAlgo.setObserver(self.__V[k], self.__O, self.__I[k], syncObs, scheduledBy) def __repr__(self): "x.__repr__() <==> repr(x)" diff --git a/src/daComposant/daCore/Interfaces.py b/src/daComposant/daCore/Interfaces.py index eaa9024..786683a 100644 --- a/src/daComposant/daCore/Interfaces.py +++ b/src/daComposant/daCore/Interfaces.py @@ -162,12 +162,14 @@ class _TUIViewer(GenericCaseViewer): if k not in __local: continue # noqa: E701 __v = __local[k] if __v is None: continue # noqa: E701 + if k == "SyncObs" and not __v: continue # noqa: E241,E271,E272,E701 if k == "Checked" and not __v: continue # noqa: E241,E271,E272,E701 - if k == "Stored" and not __v: continue # noqa: E241,E271,E272,E701 if k == "ColMajor" and not __v: continue # noqa: E241,E271,E272,E701 + if k == "CrossObs" and not __v: continue # noqa: E241,E271,E272,E701 if k == "InputFunctionAsMulti" and not __v: continue # noqa: E241,E271,E272,E701 - if k == "nextStep" and not __v: continue # noqa: E241,E271,E272,E701 if k == "PerformanceProfile" and __v: continue # noqa: E241,E271,E272,E701 + if k == "Stored" and not __v: continue # noqa: E241,E271,E272,E701 + if k == "nextStep" and not __v: continue # noqa: E241,E271,E272,E701 if k == "noDetails": continue # noqa: E241,E271,E272,E701 if isinstance(__v, Persistence.Persistence): __v = __v.values() @@ -555,7 +557,7 @@ class _SCDViewer(GenericCaseViewer): __text += "%s_config['Data'] = %s\n"%(__command, __v) __text = __text.replace("''", "'") __vectorIsScript = True - elif __k in ('Stored', 'Checked', 'ColMajor', 'InputFunctionAsMulti', 'nextStep'): + elif __k in ('Stored', 'Checked', 'ColMajor', 'CrossObs', 'SyncObs', 'InputFunctionAsMulti', 'nextStep'): if bool(__v): __text += "%s_config['%s'] = '%s'\n"%(__command, __k, int(bool(__v))) elif __k in ('PerformanceProfile', 'noDetails'): @@ -723,12 +725,14 @@ class _ReportViewer(GenericCaseViewer): if k not in __local: continue # noqa: E701 __v = __local[k] if __v is None: continue # noqa: E701 + if k == "SyncObs" and not __v: continue # noqa: E241,E271,E272,E701 if k == "Checked" and not __v: continue # noqa: E241,E271,E272,E701 - if k == "Stored" and not __v: continue # noqa: E241,E271,E272,E701 if k == "ColMajor" and not __v: continue # noqa: E241,E271,E272,E701 + if k == "CrossObs" and not __v: continue # noqa: E241,E271,E272,E701 if k == "InputFunctionAsMulti" and not __v: continue # noqa: E241,E271,E272,E701 - if k == "nextStep" and not __v: continue # noqa: E241,E271,E272,E701 if k == "PerformanceProfile" and __v: continue # noqa: E241,E271,E272,E701 + if k == "Stored" and not __v: continue # noqa: E241,E271,E272,E701 + if k == "nextStep" and not __v: continue # noqa: E241,E271,E272,E701 if k == "noDetails": continue # noqa: E241,E271,E272,E701 if k == "Concept": continue # noqa: E241,E271,E272,E701 if k == "self": continue # noqa: E241,E271,E272,E701 diff --git a/src/daComposant/daCore/Persistence.py b/src/daComposant/daCore/Persistence.py index 9bb55de..f814f38 100644 --- a/src/daComposant/daCore/Persistence.py +++ b/src/daComposant/daCore/Persistence.py @@ -100,9 +100,23 @@ class Persistence(object): if self.__dynamic: self.__replots() __step = len(self.__values) - 1 - for hook, parameters, scheduler in self.__dataobservers: + for hook, parameters, scheduler, order, osync, dovar in self.__dataobservers: if __step in scheduler: - hook( self, parameters ) + if order is None or dovar is None: + hook( self, parameters ) + else: + if not isinstance(order, (list, tuple)): + continue + if not isinstance(dovar, dict): + continue + if not bool(osync): # Async observation + hook( self, parameters, order, dovar ) + else: # Sync observations + for v in order: + if len(dovar[v]) != len(self): + break + else: + hook( self, parameters, order, dovar ) def pop(self, item=None): """ @@ -765,12 +779,13 @@ class Persistence(object): raise TypeError("Base type is incompatible with numpy") # --------------------------------------------------------- - def setDataObserver(self, HookFunction = None, HookParameters = None, Scheduler = None): + def setDataObserver(self, HookFunction = None, HookParameters = None, Scheduler = None, Order = None, OSync = True, DOVar = None): """ - Association à la variable d'un triplet définissant un observer + Association à la variable d'un triplet définissant un observer. - Le Scheduler attendu est une fréquence, une simple liste d'index ou un - range des index. + Les variables Order et DOVar sont utilisées pour un observer + multi-variable. Le Scheduler attendu est une fréquence, une simple + liste d'index ou un range des index. """ # # Vérification du Scheduler @@ -787,13 +802,13 @@ class Persistence(object): # # Stockage interne de l'observer dans la variable # ----------------------------------------------- - self.__dataobservers.append( [HookFunction, HookParameters, Schedulers] ) + self.__dataobservers.append( [HookFunction, HookParameters, Schedulers, Order, OSync, DOVar] ) def removeDataObserver(self, HookFunction = None, AllObservers = False): """ Suppression d'un observer nommé sur la variable. - On peut donner dans HookFunction la meme fonction que lors de la + On peut donner dans HookFunction la même fonction que lors de la définition, ou un simple string qui est le nom de la fonction. Si AllObservers est vrai, supprime tous les observers enregistrés. """ @@ -808,7 +823,7 @@ class Persistence(object): # ih = -1 index_to_remove = [] - for [hf, hp, hs] in self.__dataobservers: + for [hf, _, _, _, _, _] in self.__dataobservers: ih = ih + 1 if name is hf.__name__ or AllObservers: index_to_remove.append( ih ) diff --git a/src/daComposant/daCore/PlatformInfo.py b/src/daComposant/daCore/PlatformInfo.py index 8777c01..147d613 100644 --- a/src/daComposant/daCore/PlatformInfo.py +++ b/src/daComposant/daCore/PlatformInfo.py @@ -72,6 +72,7 @@ class PathManagement(object): parent = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) self.__paths = {} self.__paths["daNumerics"] = os.path.join(parent, "daNumerics") + self.__paths["pst4mod"] = os.path.join(parent, "daNumerics", "pst4mod") # for v in self.__paths.values(): if os.path.isdir(v): diff --git a/src/daComposant/daNumerics/Models/TwoDimensionalRosenbrockFunctionR1960.py b/src/daComposant/daNumerics/Models/TwoDimensionalRosenbrockFunctionR1960.py index d7c36e2..7f7b27a 100644 --- a/src/daComposant/daNumerics/Models/TwoDimensionalRosenbrockFunctionR1960.py +++ b/src/daComposant/daNumerics/Models/TwoDimensionalRosenbrockFunctionR1960.py @@ -101,8 +101,12 @@ class LocalTest(unittest.TestCase): def test001(self): numpy.random.seed(123456789) Equation = TwoDimensionalRosenbrockFunctionR1960() - optimum = Equation.ValueZ( [1, 1] ) - self.assertTrue( max(optimum) <= 0.) + + optimum = Equation.FunctionH( [1, 1] ) + self.assertTrue( max(optimum.flat) <= 0.) + + optimum = Equation.FunctionH( [0.5, 0.25], a=0.5 ) + self.assertTrue( max(optimum.flat) <= 0.) def tearDown(cls): print("\n Tests OK\n") diff --git a/src/daComposant/daNumerics/pst4mod/modelica_libraries/around_simulation.py b/src/daComposant/daNumerics/pst4mod/modelica_libraries/around_simulation.py index 3785b4d..8b605e4 100644 --- a/src/daComposant/daNumerics/pst4mod/modelica_libraries/around_simulation.py +++ b/src/daComposant/daNumerics/pst4mod/modelica_libraries/around_simulation.py @@ -628,11 +628,11 @@ class Around_Simulation(object): except KeyError: #The variable is not found try: - node_pos = re.search("\[\d+\]",res_name_var).span() #Position of the node number if any (in case the error is due to a remesh of the model) + node_pos = re.search(r"\[\d+\]",res_name_var).span() #Position of the node number if any (in case the error is due to a remesh of the model) except AttributeError: #No node number found try: - node_pos = re.search("\[\]",res_name_var).span() #Position of empty brakets (all array values are due) + node_pos = re.search(r"\[\]",res_name_var).span() #Position of empty brakets (all array values are due) except AttributeError : pass else: @@ -640,7 +640,7 @@ class Around_Simulation(object): print('Filters and prefixes still have to be implemented for arrays of unknown size') else : print(f'{name_var}: unknown size -> Taking all values in .mat (if any)') - tmp = f'{res_name_var[:node_pos[0]]}\[\d\{res_name_var[node_pos[0]+1:]}' + tmp = rf'{res_name_var[:node_pos[0]]}\[\d\{res_name_var[node_pos[0]+1:]}' expanded_vars = structData.varNames(tmp) for tmp_var in expanded_vars : self.__dict_iter_var[tmp_var] = structData.values(tmp_var)[1][mom] -- 2.39.2