]> SALOME platform Git repositories - modules/adao.git/blob - src/daComposant/daNumerics/pst4mod/modelica_libraries/around_simulation.py
Salome HOME
Minor documentation and source update for compatibilities
[modules/adao.git] / src / daComposant / daNumerics / pst4mod / modelica_libraries / around_simulation.py
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2016-2024 EDF R&D
4 #
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License.
9 #
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18
19 ###############################################################################
20 #                            LIBRARIES IMPORT                                 #
21 ###############################################################################
22 import re
23 from buildingspy.io.outputfile import Reader # To read dsres.mat
24 from os import path,getcwd,pardir,mkdir
25 from numpy import mean
26 from sys import exit
27 import pandas as pd
28 import numpy as np
29 import os
30
31 from .functions import get_cur_time, Edit_File
32
33 class NotImplementedError(Exception) :
34     """
35     This exception is raised when there is only one alive (not enough to continue)
36     """
37     pass
38
39
40 ###############################################################################
41 #                        CLASS Simulation_Files                               #
42 ###############################################################################
43
44 class Around_Simulation(object):
45     """
46     Aim: creating an efficient and user-friendly software dedicated to creating
47     initialization scripts for Modelica based simulation tools.
48
49     Notes:
50     - Commentaries are written directly in the code
51     - Examples are provided with the code to make it easier to use
52     - It is recommended to create the build the list of the iteration variables from
53     the information that is displayed in the log printed by Dymola during the
54     translation (ie: choose source_list_iter_var = 'from_dymtranslog' when it is possible)
55     - The derivative of the variables are never considered as iteration variables
56     - It should be possible to create the list of the iteration variables from file
57     dslog.txt and this script gives an option to do it, but this option has never been
58     tested and may also require some modifications.
59
60     Required libraries that are not in the standard distribution:
61     - buildingspy
62     """
63
64     def __init__(self,dymola_version='Unknown',curdir=getcwd(),source_list_iter_var=None,\
65     mo=None,mos=None,copy_from_dym_trans_log=None,dslog_txt=None,mat=None,\
66     iter_var_values_options=None,generated_script_options=None,alphabetical_order=True,verbose=True):
67
68         self.verbose = verbose #Boolean, if set to True, some informations are printed to the stdout.
69
70         if iter_var_values_options == None:
71             iter_var_values_options={'source':'from_mat','moment':'initial','display':False,'updated_vars':False,'GUI':'Dymola'}
72
73         if generated_script_options == None:
74             generated_script_options={'file_name':'Script_ini.mos','dest_dir':None,'pref':'','filters':{},'renamed':None,'compute':{}}
75
76         #Attributes whose values are setted without specific setters
77         self.__vdymola = dymola_version
78         self.__curdir = path.abspath(curdir)
79
80         #Call setters
81         self.source_list_iter_var = source_list_iter_var
82         self.mo = mo    #The mo file could be used to check that no fixed value has been added to the list of the iteration variables
83         self.mos = mos
84         self.copy_from_dym_trans_log = copy_from_dym_trans_log
85         self.dslog_txt = dslog_txt
86         self.mat = mat
87         self.iter_var_values_options = iter_var_values_options
88         self.generated_script_options = generated_script_options
89         self.__alphabetical_order = alphabetical_order
90         #Attributes that cannot be given a value at the creation of the object
91         self.__not_found_iter_var = []
92         self.__not_found_complex_iter_var = []
93         self.__list_iter_var = []
94         self.__dict_iter_var = {}
95         self.__list_complex_iter_var = []
96         self.__dict_complex_iter_var = {}
97         self.__dict_iter_var_from_ini = {}  # pour récupérer la valeur du fichier ini.txt
98         self.computed_variables = [] #List to store nale of variable computed by user defined function (if any)
99
100
101 ###############################################################################
102 #                                  GETTERS                                    #
103 ###############################################################################
104
105     @property
106     def vdymola(self):
107         """
108         Dymola version
109         """
110         return self.__vdymola
111
112     @property
113     def curdir(self):
114         """
115         Directory in which the files are contained
116         """
117         return self.__curdir
118
119     @property
120     def source_list_iter_var(self):
121         """
122         Type of the file in which the iteration variables will be extracted.
123         Its values is setted by set_source_list_iter_var
124         """
125         return self.__source_list_iter_var
126
127     @property
128     def mo(self):
129         """
130         Path of the mo file
131         """
132         return self.__mo
133
134     @property
135     def mos(self):
136         """
137         Path of the mos file
138         """
139         return self.__mos
140
141     @property
142     def copy_from_dym_trans_log(self):
143         """
144         Path of the file which contains a copy of the text automatically
145         written by Dymola in the translation part of the log window when
146         Advanced.LogStartValuesForIteration = true.
147         """
148         return self.__copy_from_dym_trans_log
149
150     @property
151     def dslog_txt(self):
152         """
153         Path of dslog.txt
154         """
155         return self.__dslog_txt
156
157     @property
158     def mat(self):
159         """
160         Path of the mat file or list of paths of mat file
161         """
162         return self.__mat
163
164     @property
165     def not_found_iter_var(self):
166         """
167         List of the iteration variables whose values have been searched and not
168         found
169         """
170         return self.__not_found_iter_var
171
172     @property
173     def not_found_complex_iter_var(self):
174         """
175         List of the images of complex iteration variables whose values has not
176         been found
177         """
178         return self.__not_found_complex_iter_var
179
180     @property
181     def list_iter_var(self):
182         """
183         List of the iteration variables
184         """
185         return self.__list_iter_var
186
187     @property
188     def list_complex_iter_var(self):
189         """
190         List of iteration variables which are given the value of another variable.
191         Classic example: Tp is given the value of T0.
192         These values must be identified because the variable that is to be written
193         in the initialization script in the case of the example would be T0 and not Tp.
194         This list is included in list_iter_var
195         """
196         return self.__list_complex_iter_var
197
198     @property
199     def dict_iter_var(self):
200         """
201         Dictionary which associates the name of an iteration variable to its value
202         """
203         return self.__dict_iter_var
204
205     @property
206     def dict_iter_var_from_ini(self):  # pour récupérer la valeur du fichier ini.txt
207         """
208         Dictionary which associates the name of an iteration variable to its value in ini.txt file
209         """
210         return self.__dict_iter_var_from_ini
211
212     @property
213     def dict_complex_iter_var(self):
214         """
215         Dictionary that is necessary to initialize the values of the iteration
216         variables that are in list_complex_iter_var
217         Dictionary that associates the value
218             key     Variable which should be given a value so that the associated
219                     iteration variables are initialized
220             value   Dictionary which associates
221                 key     Associated iteration variable
222                 value   Value of the associated iteration variables
223         Example:
224         'Tp1' is initialized with the value 'T0'
225         'Tp2' is initialized with the value 'T0'
226         dict_complex_iter_var = {'T0':{'Tp1':Tp1_value,'Tp2':Tp2_value}}
227         In the initialization script, the mean of Tp1_value and Tp2_value will be written
228
229         This method can only work when the list of the iteration variables has been
230         built with the text written in the log during translation (source_list_iter_var = 'from_dymtranslog')
231         """
232         return self.__dict_complex_iter_var
233
234     @property
235     def iter_var_values_options(self):
236         """
237         Dictionary containing the options that will be used to create the dictionary
238         containing the values of the iteration variables
239         """
240         return self.__iter_var_values_options
241
242     @property
243     def generated_script_options(self):
244         """
245         Dictionary continaing the options that will be used to generate the
246         .mos script file
247         """
248         return self.__generated_script_options
249
250     @property
251     def alphabetical_order(self):
252         """
253         If True, the list of the iteration variables is sorted according to the
254         alphabetical order. If False, it is sorted as in the source which is used
255         to create the list of the iteration variables.
256         """
257         return self.__alphabetical_order
258
259 ###############################################################################
260 #                                  SETTERS                                    #
261 ###############################################################################
262     @source_list_iter_var.setter
263     def source_list_iter_var(self,source_list_iter_var):
264         """
265         Set the value of attribute source_list_iter_var.
266
267         Available values are:
268             'from_mos'          The list is created from a .mos file
269             The mos file is supposed to be written as follows
270                 nom_var1 = ...;
271                 nom_var2 = ...;
272             'from_dymtranslog"  The list is created from a .txt file which contains
273                                 the text automatically written by Dymola in the translation
274                                 part of the log window when
275                                 Advanced.LogStartValuesForIteration = true
276             'from_dslog'        The list is created from a dslog.txt file
277         """
278         available_sources = ['from_mos','from_dymtranslog','from_dslog']
279         if source_list_iter_var == None:
280             print('No value given to self.__source_list_iter_var')
281             self.__source_list_iter_var = None
282         elif source_list_iter_var in available_sources:
283             self.__source_list_iter_var = source_list_iter_var
284         else:
285             print('The chosen value for source_list_iter_var is not valid. The only valid values are\
286             from_mos, from_dymtranslog, from_dslog')
287
288     @mo.setter
289     def mo(self,mo_file_name):
290         """
291         Set the path of the mo file
292         """
293         if mo_file_name == None:
294             self.__mo = None
295         else:
296             self.__mo = path.join(self.curdir,mo_file_name)
297
298     @mos.setter
299     def mos(self,mos_file_name):
300         """
301         Set the path of the mos file
302         """
303         if mos_file_name == None:
304             self.__mos = None
305         else:
306             self.__mos = path.join(self.curdir,mos_file_name)
307
308     @copy_from_dym_trans_log.setter
309     def copy_from_dym_trans_log(self,copy_from_dym_trans_log):
310         """
311         Set the path of the file which contains a copy of the text automatically
312         written by Dymola in the translation part of the log window when
313         Advanced.LogStartValuesForIteration = true.
314         """
315         if copy_from_dym_trans_log == None:
316             self.__copy_from_dym_trans_log = None
317         else:
318             self.__copy_from_dym_trans_log = path.join(self.curdir,copy_from_dym_trans_log)
319
320     @dslog_txt.setter
321     def dslog_txt(self,dslog_txt):
322         """
323         Set the path of dslog.txt
324         """
325         if dslog_txt == None:
326             self.__dslog_txt = None
327         else:
328             self.__dslog_txt = path.join(self.curdir,dslog_txt)
329
330     @mat.setter
331     def mat(self,mat_file_name):
332         """
333         Set the path of the mat file
334         """
335         if not(isinstance(mat_file_name,list)):
336             if mat_file_name == None:
337                 self.__mat = None
338             else:
339                 self.__mat = [path.join(self.curdir,mat_file_name)]
340         else:
341             self.__mat = []
342             for mat_file_name_elem in mat_file_name:
343                 self.__mat.append(path.join(self.curdir,mat_file_name_elem))
344
345
346     @iter_var_values_options.setter
347     def iter_var_values_options(self,iter_var_values_options):
348         """
349         Set the value of attribute iter_var_values_options.
350         This attribute has to be a dictionary conataining the following keys:
351             source (compulsory)
352                 Available values for source are "from_mat" and "from_mos"
353             moment (optional)
354                 Available values for moment are "initial" and "final"
355             display (optional)
356                 Available values for display are False and True
357             updated_vars (optional)
358                 Available values for updated_vars are False and True
359         """
360         iter_var_values_options_base = {'source':'from_mat','moment':'initial','display':False,'updated_vars':False,'GUI':'Dymola'}
361
362         for key in list(iter_var_values_options_base.keys()):
363             try:
364                 iter_var_values_options_base[key] = iter_var_values_options[key]
365             except:
366                 if self.verbose :
367                     print('Default value used for generated_script_options[\''+str(key)+'\']')
368
369         iter_var_values_options_base['updated_vars'] = iter_var_values_options_base['updated_vars'] and iter_var_values_options_base['display'] #Comparison is only possible if the file is changed on the fly
370
371         try:
372             for key in list(iter_var_values_options.keys()):
373                 if not key in list(iter_var_values_options_base.keys()):
374                     print('iter_var_values_options[\''+str(key)+'\'] will not be used: only the following keys are allowed:')
375                     for  ki in iter_var_values_options_base.keys() :
376                         print(ki)
377         except:
378             print('iter_var_values_options should be a dictionary')
379
380         self.__iter_var_values_options = iter_var_values_options_base
381
382     @generated_script_options.setter
383     def generated_script_options(self,generated_script_options):
384         #Default value. It garantees that the structure will always be the same
385         generated_script_options_base={'file_name':'Script_ini.mos','dest_dir':None,'pref':'','filters':{},'renamed':None,'compute':{}}
386
387         for key in list(generated_script_options_base.keys()):
388             try:
389                 generated_script_options_base[key] = generated_script_options[key]
390             except:
391                 if self.verbose :
392                     print('Default value used for generated_script_options[\''+str(key)+'\']')
393
394         try:
395             for key in list(generated_script_options.keys()):
396                 if not key in list(generated_script_options_base.keys()):
397                     print('generated_script_options[\''+str(key)+'\'] will not be used:only values associated to "file_name", "dest_dir", "pref", "filters", "compute" and "renamed" can be taken into account')
398
399         except:
400             print('generated_script_options should be a dictionary')
401
402         self.__generated_script_options = generated_script_options_base
403 ###############################################################################
404 #                                 RESETTERS                                   #
405 ###############################################################################
406
407     def reset_list_iter_var(self):
408         """
409         Reset the value of attributes list_iter_var and list_complex_iter_var
410         """
411         self.__list_iter_var = []
412         self.__list_complex_iter_var = []
413
414     def reset_not_found_iter_var(self):
415         """
416         Reset the value of attributes not_found_iter_var and not_found_images_of_complex_iter_var
417         """
418         self.__not_found_iter_var = []
419         self.__not_found_complex_iter_var = []
420
421 ###############################################################################
422 #                               MAIN METHODS                                  #
423 ###############################################################################
424
425     def set_list_iter_var(self):
426         """
427         Set the value of attribute list_iter_var, which contains the list of the iteration variables.
428         This list can be created from
429             - file dslog.txt (created in case of a translation error) - to be used carefully
430             - a .mos file that contains the iteration variables
431             - a .txt file that contains the text written by Dymola if option
432             Advanced.LogStartValuesForIteration is activated (text written in
433             the translation part of the log window)
434
435         This method will look for the iteration variables in a source defined by
436         self.source_list_iter_var.
437         """
438         self.reset_list_iter_var()
439
440         if self.source_list_iter_var == 'from_mos':
441
442             #Regular expression to find the interesting lines - be careful: it does not return the whole line
443             input_line_regex = re.compile(r'^[^\\][A-Za-z0-9_.[\]\s]+=\s*-?[0-9]+')
444
445             with open(self.mos,"r") as lines:
446                 for line in lines:
447                     if input_line_regex.search(line):
448                         words = [word.strip() for word in line.split('=')]
449                         #Add the name of the current iteration variable to the list of iteration variables
450                         self.list_iter_var.append(words[0])
451                         self.__dict_iter_var_from_ini[words[0]] = words[1]  # valeur du .mos brute_init
452
453         #Only option that can handle the iteration variables that are given the value of other variables
454         elif self.source_list_iter_var == 'from_dymtranslog':
455             if self.iter_var_values_options['updated_vars'] :
456                 old_list = self.dymtranslog_read(oldlist=True)
457             if self.iter_var_values_options['display'] : #If required, open the 'ini.txt' file to allow modifications (copy)
458                 #file_display = Popen("Notepad " + self.copy_from_dym_trans_log)
459                 #file_display.wait()
460                 Edit_File(self.copy_from_dym_trans_log)
461             self.dymtranslog_read()
462             if self.iter_var_values_options['updated_vars'] :
463                 print('Exiting variables: %s' % ' ,'.join(set(old_list).difference(self.list_iter_var)))
464                 print('Entering variables: %s' % ' ,'.join(set(self.list_iter_var).difference(old_list)))
465
466         elif self.source_list_iter_var == 'from_dslog':
467             if self.iter_var_values_options['GUI'] == 'OM' :
468                 raise NotImplementedError("Automatic reading of the log is still not implemented for OpenModelica") from None
469             input_line_regex = re.compile(r'^[^\\][A-Za-z0-9_.[\]\s]+=\s*-?[0-9]+')
470             read = False
471             with open(self.dslog_txt,'r') as lines:
472                 for line in lines:
473                     if ('Last value of the solution:' in line) or ('Last values of solution vector' in line):
474                         read = True
475                     elif ('Last value of the solution:' in line) or ('Last values of solution vector' in line):
476                         read = False
477                     if read and input_line_regex.search(line):
478                          words = [word.strip() for word in line.split('=')]
479                          #Add the name of the current iteration variable to the list of iteration variables
480                          self.list_iter_var.append(words[0])
481                          self.__dict_iter_var_from_ini[words[0]] = words[1]  # valeur du dslog.txt brute_init
482         else:
483             exit('Option given to method set_list_iter_var is not defined. \n\
484             Available options are "from_mos", "from_dymtranslog" and "from_dslog".')
485
486         #Alphabetical order
487         if self.alphabetical_order:
488             self.list_iter_var.sort()
489
490     def dymtranslog_read(self,oldlist=False):
491         """
492         Method dedicated to the dymtranslog reading
493         """
494         if oldlist:
495             out = []
496         else:
497             out = self.list_iter_var
498         #Regular expression to find the interesting lines
499         var_and_start_regex = re.compile(r'[A-Za-z]+[A-Za-z0-9_.[,\s\]]*')
500         with open(self.copy_from_dym_trans_log,'r') as lines:
501             for line in lines:
502                 if ('=' in line) and not ('der(' == line[0:4] or 'der (' == line[0:5]) : #Should be a mathematical expression; then, no derivative.
503                     unknown_length=False
504                     tmp_words = var_and_start_regex.findall(line)
505                     words = [word.strip() for word in tmp_words]
506
507                     #Elimination of start
508                     try:
509                         del words[words.index('start')]
510                     except ValueError : #The line does not contain "start" word : this line does not talk about initialization variable -> skip
511                         try :
512                             del words[words.index('each start')]
513                         except ValueError :
514                             continue
515
516                     if self.iter_var_values_options['GUI'] == 'OM' :
517                         del words[words.index('nominal')] #Added for OpenModelica
518
519                     #If 'E' appears in the right part of a line, it should not be considered as a variable name
520                     #because it is almost certainly the 'E' of the scientific writing
521                     #Do not call 'E' any of your variable!!
522                     if any(s in words[1:] for s in ['E','e']) :
523                         words.reverse()
524                         try:
525                             words.pop(words.index('E'))
526                         except ValueError :
527                             words.pop(words.index('e')) #Added for OpenModelica
528                         words.reverse()
529
530                     #Add the name of the current iteration variable to the list of iteration variables
531                     if self.iter_var_values_options['GUI'] == 'OM' :
532                         out.append(words[0].split(' ',1)[1]) #OpenModelica : Errors lines copied from the compilation log contains the 'unit' as first word (Integer, Real...)
533                     else: #Dymola, default case
534                         out.append(words[0])
535                         self.__dict_iter_var_from_ini[words[0]] = [word.strip() for word in line.split('=')][1][:-1]  # valeur du ini.txt brute_init
536
537
538                     #Detection of the iteration variables that are given the values of other variables
539                     if not(oldlist) and len(words) >1:
540                         self.list_complex_iter_var.append(words[0])
541                         self.dict_complex_iter_var[words[1]] = {words[0]:None}
542         if oldlist:
543             return out
544
545     def set_dict_iter_var(self):
546         """
547         Set the value of attribute dict_iter_var (and not_found_iter_var).
548         dict_iter_var contains the values of the iteration variables that should
549         be given to Dymola to help the simulation to initialize.
550         These values can be found in a .mat file (containing the result of a simulation)
551         or in a .mos file (which has been constructed either manually or automatically).
552
553         This method will use the source specified in self.iter_var_values_options['source']
554         to find the values of the iteration variables. It will use the final value of each
555         variable if self.iter_var_values_options['source']== 'final' and the initial
556         value of each variable if self.iter_var_values_options['source']== 'initial'
557
558         self.iter_var_values_options is a dictionary whose entries must be the following ones:
559             'source' (compulsory)
560             Possible values:
561                 - 'from_mat'
562                 - 'from_mos'
563
564             'moment' (compulsory if the value associated to key 'source' is 'from_mat')
565             Possible values:
566                 - 'initial'
567                 - 'final'
568
569             'display' (optional)
570             Possible values:
571                 - False
572                 - True
573         """
574         #Get special_options
575         pref = self.generated_script_options['pref']
576
577         #Reset the list of the iteration variables that have not been found
578         self.reset_not_found_iter_var()
579
580         #Create a dictionary to store the intermediate variable values to handle
581         #the iteration variables that are given values of other variables
582         dict_intermediate_values = {}
583
584         #Get value of filters
585         filters = self.generated_script_options['filters']
586
587         if self.iter_var_values_options['source'] == 'from_mat':#It is recommended to use this option
588
589             #Start or final value to be taken from the mat file
590             if self.iter_var_values_options['moment'] == 'initial':
591                 mom = 0
592             elif self.iter_var_values_options['moment'] == 'final':
593                 mom = -1
594             else :
595                 exit("'moment' option can be set only to 'initial' or 'final'")
596
597             # Read file .mat which contains simulation results
598             var_to_drop = [] #Because of array expansion
599             for mat_file in self.mat:
600                 structData = Reader(mat_file,'dymola')
601
602                 for name_var in self.list_iter_var:
603                     varTmp = None #Initialisation
604                     res_name_var = name_var
605
606                     if pref != '':
607                         if name_var[:len(pref)] == pref:
608                             #+1 because of the '.' that is next to pref
609                             res_name_var = name_var[len(pref)+1:]
610
611                     # Only if pref is not used (conservative, even if not necessary...)
612                     elif not(self.generated_script_options['renamed'] is None):
613                         for newname in self.generated_script_options['renamed'].keys() : #For each of renamed component
614                             if res_name_var.startswith(newname) : #Check whether the new name in the dictionary matches the current variable
615                                 res_name_var = self.generated_script_options['renamed'][newname] + res_name_var[len(newname):] #Substitute the new name with the old one (who has to be looked for in the .mat file)
616
617                     for cur_filter in filters:
618                     #If the end of the variable name is in list(filters.keys()) -> ex: h_vol,
619                     #the corresponding part of the variable is replaced by filers[key] -> ex:h.
620                     #The value which will be looked for will therefore be module1.h if the iteration
621                     #variable is module1.h_vol. In the script, the name of the iteration variable
622                     #(module1.h_vol) will also be given the value calculated for module1.h
623                         if cur_filter == name_var[-len(cur_filter):]:
624                             res_name_var = res_name_var[:-len(cur_filter)]+filters[cur_filter]
625
626                     try:
627                         varTmp = structData.values(res_name_var)[1][mom]
628                     except KeyError: #The variable is not found
629
630                         try:
631                             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)
632                         except AttributeError: #No node number found
633
634                             try:
635                                 node_pos = re.search(r"\[\]",res_name_var).span()  # Position of empty brakets (all array values are due)
636                             except AttributeError :
637                                 pass
638                             else:
639                                 if res_name_var != name_var :
640                                     print('Filters and prefixes still have to be implemented for arrays of unknown size')
641                                 else :
642                                     print(f'{name_var}: unknown size -> Taking all values in .mat (if any)')
643                                     tmp = rf'{res_name_var[:node_pos[0]]}\[\d\{res_name_var[node_pos[0]+1:]}'
644                                     expanded_vars = structData.varNames(tmp)
645                                     for tmp_var in expanded_vars :
646                                         self.__dict_iter_var[tmp_var] = structData.values(tmp_var)[1][mom]
647                                     if expanded_vars :
648                                         var_to_drop.append(name_var)
649                                         continue
650
651                         else: #If a node number is found
652                             name_start = res_name_var[:node_pos[0]+1]
653                             name_end = res_name_var[node_pos[1]-1:]
654                             node = int(res_name_var[node_pos[0]+1:node_pos[1]-1]) #Requested node
655                             for ii in range(node):
656                                 try:
657                                     varTmp = structData.values(name_start + str(node-ii-1) + name_end)[1][mom] #Look for an upstream node
658                                     break
659                                 except KeyError:
660                                     pass
661
662                     if varTmp is None : #If varTmp not defined (name_var not found, not due to remesh)
663                         #Try to compute it
664                         for var,f in self.generated_script_options['compute'].items() :
665                             if res_name_var.endswith(var) :
666                                 #Collect function inputs
667                                 root = res_name_var[:-len(var)] #Root path of the variable
668                                 f_inputs = {}
669                                 for k,v in f['f_inputs'].items() :
670                                     f_inputs[k] = structData.values(root+v)[1][mom]
671                                 varTmp = f['user_f'](f_inputs)
672                                 self.computed_variables.append(name_var)
673
674                     if varTmp is None : #If varTmp not defined (name_var not found, not due to remesh, not computed)
675                         self.not_found_iter_var.append(name_var)
676                     else :
677
678                         #Retrieve the initialization value from the .mat
679                         if name_var in self.list_complex_iter_var:
680                             dict_intermediate_values[name_var] = varTmp
681                         else:
682                             self.__dict_iter_var[name_var] = varTmp
683
684                 for image_of_complex_var in list(self.dict_complex_iter_var.keys()):
685                     for complex_iter_var in list(self.dict_complex_iter_var[image_of_complex_var].keys()):
686                         try:
687                             self.dict_complex_iter_var[image_of_complex_var][complex_iter_var] = dict_intermediate_values[complex_iter_var]
688                         except:#In case the value of variable complex_iter_var has not been found in the mat file
689                             if complex_iter_var not in self.not_found_complex_iter_var:
690                                 self.not_found_complex_iter_var.append(complex_iter_var)
691                             else:
692                                 pass
693
694             for i in var_to_drop :
695                 self.list_iter_var.remove(i)
696
697         elif self.iter_var_values_options['source'] == 'from_mos':
698             #Regular expression to find the interesting lines - be careful: it does not return the whole line
699             input_line_regex = re.compile(r'^[^\\][A-Za-z0-9_.[\]\s]+=\s*-?[0-9]+')
700             value_regex = re.compile(r'[0-9e+-.]*')
701
702             with open(self.mos,'r') as lines:
703                 for line in lines:
704                     if input_line_regex.search(line):
705                         words = [word.strip() for word in line.split("=")]
706                         name_var = words[0]
707                         if pref+name_var in self.list_iter_var:
708                             #The following lines eliminates spaces, ;, and other non-numerical elements
709                             value = float(value_regex.findall(words[1])[0])
710                             #Add the name of the current iteration variable to the list of iteration variables
711                             self.__dict_iter_var[name_var] = value
712
713             #All the iteration variables in self.list_iter_var are added to self.not_found_iter_var if their value has not been found
714             for name_var in self.list_iter_var:
715                 if not name_var in self.dict_iter_var:
716                     self.not_found_iter_var.append(name_var)
717
718         else:
719             print('Option given to method set_list_iter_var is not defined. \n\
720             Available options are "from_mat" and "from_mos".')
721
722         #Sort the not found iteration variables lists
723         #Warning: these sorting method puts all the variables whose first letter
724         #is uppercase before the variables whose first letter is lowercase.
725         self.not_found_iter_var.sort()
726         self.not_found_complex_iter_var.sort()
727
728
729     def create_script_ini(self):
730         """
731         Create a .mos initialization script.
732         The list of the iteration variables must have been created before using this method
733         with set_list_iter_var. The values of the iteration variables must have been found
734         before using this method with set_dict_iter_var.
735         This whole process can be done in one single step with method create_quickly_script_ini.
736
737         self.generated_script_options is a dictionary whose entries must be the following ones:
738             file_name   String
739                         Name of the .mos file that will be generated
740             dest_dir    String
741                         path of the directory in which the file should be saved
742             pref        String
743                         pref + '.' will be added at the beginning of all variables.
744                         It is useful if a model has been inserted in a module of a new model
745             filters     Dictionary
746                         Modifications that have to be applied to the names of the iteration variables
747                         Example: {'h_vol':'h'} -> the variable whose name finishes by h_vol will be given the value of the variable whose name finishes by h. Be careful with the use of this filter
748         """
749         #Put the values of self.generated_script_options in short varaibles to make it easier to write the following function
750         file_name,dest_dir = self.generated_script_options['file_name'],self.generated_script_options['dest_dir']
751         if dest_dir == None:
752             dest_dir=self.curdir
753
754         dest_file_path = path.join(dest_dir,file_name)
755
756         with open(dest_file_path,'w') as output:
757             output.write('///////////////////////////////////////////////////////////////////\n')
758             output.write('// Script automatically generated by Python ')
759
760             #Date of the creation of the file
761             output.write('on '+get_cur_time()+'\n')
762
763             #Dymola version
764             output.write('// Dymola version: '+self.vdymola+'\n')
765
766             #Source of the list of iteration variables
767             output.write('// List of the iteration variables from file ')
768             if self.source_list_iter_var == 'from_mos':
769                 output.write(self.mos+'\n')
770             elif self.source_list_iter_var == 'from_dymtranslog':
771                 output.write(self.copy_from_dym_trans_log+'\n')
772             elif self.source_list_iter_var == 'from_dslog':
773                 output.write(self.dslog_txt+'\n')
774
775             #Source of the values of the iteration variables
776             output.write('// Values of the iteration variables from file ')
777             if self.iter_var_values_options['source'] == 'from_mos':
778                 output.write(self.mos+'\n')
779             elif self.iter_var_values_options['source'] == 'from_mat':
780                 output.write(self.mat[0]+'\n') #The name of the first mat file is kept if several are given
781             output.write('///////////////////////////////////////////////////////////////////\n\n\n')
782
783             #Iteration variables whose values have not been found
784             if len(self.not_found_iter_var) > 0:
785                 output.write('///////////////////////////////////////////////////////////////////\n')
786                 output.write('// Iteration variables whose values have not been found:\n')
787                 for var_name in self.not_found_iter_var:
788                     if var_name not in self.not_found_complex_iter_var:
789                         output.write('// '+var_name+'\n')
790                 output.write('///////////////////////////////////////////////////////////////////\n\n')
791
792             #Iteration variables whose values have not been found
793             if len(self.not_found_complex_iter_var) > 0:
794                 output.write('///////////////////////////////////////////////////////////////////\n')
795                 output.write('// Complex iteration variables whose values have not been found:\n')
796                 for var_name in self.not_found_complex_iter_var:
797                     output.write('// '+var_name+'\n')
798
799                 #Explain the user which values he should give to which variables
800                 output.write('//It would have no effect to give these complex variables a value in an initialization script.\n')
801                 output.write('//The initialization should be done by giving the following values to the following variables:\n')
802                 for complex_iter_var in self.not_found_complex_iter_var:
803                     for image_of_complex_iter_var in list(self.dict_complex_iter_var.keys()):
804                         if complex_iter_var in list(self.dict_complex_iter_var[image_of_complex_iter_var].keys()):
805                             current_iter_vars = list(self.dict_complex_iter_var[image_of_complex_iter_var].keys())
806                             output.write('//'+image_of_complex_iter_var+' should be given the mean of the following variables:\n')
807                             for cur_var in current_iter_vars:
808                                 output.write('//- '+cur_var+'\n')
809                 output.write('///////////////////////////////////////////////////////////////////\n\n')
810
811             #Values of the iteration variables
812             output.write('// Iteration variables values:\n')
813             #for var_name in self.__list_iter_var:
814             #Appending the additional elements to the end of the list
815             tmp_not_originally_present = set(self.__list_iter_var) ^ set(self.__dict_iter_var.keys())
816             print(f'Variables from expansion: {tmp_not_originally_present}')
817             tmp_list = self.__list_iter_var + list(tmp_not_originally_present)
818             for var_name in tmp_list :
819                 if not var_name in self.not_found_iter_var:
820                     if not var_name in self.list_complex_iter_var:
821                         cmp_str = '' if var_name not in self.computed_variables else '\t//Computed by user defined function'
822                         var_value = self.__dict_iter_var[var_name]
823                         output.write(var_name.ljust(40)+'\t\t=\t\t'+str(var_value)+';'+cmp_str+'\n')
824
825             #Variables that are given the value of other variables
826             if len(list(self.dict_complex_iter_var.keys())) > 0:
827                 if self.verbose :
828                     print("WARNING: Some of the initialising values depends on model's parameters values : automatic changes are unsafe!")
829                     print("WARNING: However, some proposals based on previous results are wrote to the script, but commented.")
830                     print("WARNING: If required to successfully initialize, the user is invited to check the proposed values and, potentially, uncomment them.")
831                 output.write('\n// Complex iteration variables values:\n')
832                 output.write('// WARNING: Some of the following variables may be key parameters of your model.\n')
833                 output.write('// WARNING: To change their values automatically may be dangerous since it may change the model itself.\n')
834                 output.write('// WARNING: That is why the following lines are disabled (commented) by default.\n')
835                 output.write('// WARNING: However, some of them may be initialization parameters (Tstart, ...) and may be safely set.\n')
836                 output.write('// WARNING: In this case, if you still have initialisation issues, you can safely change their values (uncomment the lines).\n\n')
837                 for var_name in list(self.dict_complex_iter_var.keys()):
838                     #Get the list of values associated to var_name
839                     intermediate_list = [self.dict_complex_iter_var[var_name][key] for key in list(self.dict_complex_iter_var[var_name].keys())]
840                     #Eliminate values None
841                     intermediate_list2 = [elem for elem in intermediate_list if elem != None]
842                     #If the list contains at least one value
843                     if intermediate_list2 != []:
844                         #The mean of the values of the list is calculated
845                         var_value = mean([elem for elem in intermediate_list if elem != None])
846                         #The mean value is written in the initialization script
847                         #Check if any of the values is computed by user function
848                         computed = [i for i in intermediate_list if i in self.computed_variables]
849                         cmp_str = '' if not computed else '\t//Computed by user defined function'
850                         output.write('//'+var_name.ljust(40)+'\t\t=\t\t'+str(var_value)+';'+cmp_str+'\n')
851
852     def create_quickly_script_ini(self):
853         """
854         Method that:
855             - looks for the list of iteration variables with set_list_iter_var;
856             - looks for the values of the iteration variables with set_dict_iter_var;
857             - crearte a initialization script with create_script_ini.
858
859         """
860         self.set_list_iter_var()
861         self.set_dict_iter_var()
862         self.create_script_ini()
863
864 if __name__ == '__main__':
865     print('\n  AUTODIAGNOSTIC\n  ==============\n')