]> SALOME platform Git repositories - modules/adao.git/blob - src/daComposant/daNumerics/pst4mod/modelica_libraries/change_working_point.py
Salome HOME
Documentation and models update, pst4mod
[modules/adao.git] / src / daComposant / daNumerics / pst4mod / modelica_libraries / change_working_point.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 logging
23 from copy import copy
24 from os import path,mkdir,remove
25 from shutil import copytree,rmtree
26
27 from .automatic_simulation import Automatic_Simulation
28 from .functions import get_cur_time
29 ###############################################################################
30 #                     CLASS Working_Point_Modification                        #
31 ###############################################################################
32
33 class Working_Point_Modification(object):
34     """
35     Class Working_Point_Modification aims at making it easier to make a Modelica model
36     converge when one (or several) of its variables has (have) to be changed. Depending on the impact of
37     the variable, changing the value by a few percent can indeed be very hard.
38
39     Generally, when the initial state is too far from the reference one for Dymola
40     to be able to converge towards the solution with its reference initialisation
41     script, it is possible to give ramps to Dymola (or other software) to help
42     it converging. Yet, if the model contains inverted values that should be
43     inverted only in a stationary state, giving ramps to help the model
44     converging does not work (the parameters that have been calculated at t=0
45     would be wrong).
46
47     Class Working_Point_Modification aims at helping solving this difficulty.
48     It can also be used for a model which does not contain any inverted values, in
49     case the modeler does not want to change his model so that the parameters could
50     be given as ramps.
51
52     It runs several iterations, converging iteratively from the reference initial
53     state to the desired state.
54
55     Dymola must be able to simulate the Modelica model in the reference state.
56
57     Required material:
58         main_dir            String
59                             Path of the directory in which directories will be created
60         simu_material_dir   String
61                             Name of the subdirectory of main_dir which contains all the required material to run a simulation (dymosim.exe, dsin.txt, .dll files, files that are necesssary to create an initialization script)
62         around_simulation0  Object of type Around_Simulation (class defined in around_simulation)
63                             Contains the material to create an initialization script
64         dict_var_to_fix     Dictionary
65                             Contains the names of the variables that must be imposed to
66                             different values between reference state and the target state,
67                             and the values they must be given at each state between the
68                             reference and target states (information contained by a function)
69     The other arguments are optional, but more information can be found in the attributes getters
70
71     New features introduced 02/2019:
72     - the possibility of using a dymosim situated in a given folder (featured introduced in automatic_simulation.py)
73     is introduced
74     - the previous use of this module should remain unchanged compared to previous version
75     - possibility of running this script in Linux (not tested)
76     """
77     def __init__(self,main_dir,simu_material_dir,
78                  around_simulation0,dict_var_to_fix,
79                  dymosim_path=None,
80                  simu_dir='SIMUS_DIR',store_res_dir='Working_point_change',
81                  linux=False,
82                  timeout=60,cur_step=1.,nit_max=20,var_to_follow_path='var_to_follow.csv',
83                  gen_scripts_ini=False,pause=5.,min_step_val = 0.01,check_inputs=[]):
84
85         self.__main_dir = path.abspath(main_dir)
86         self.__simu_material_dir = simu_material_dir
87
88         self.__store_res_dir = store_res_dir
89         around_simulation0.verbose = False #To avoid repetitive messages
90         self.__around_simulation0 = around_simulation0
91         self.__dict_var_to_fix = dict_var_to_fix
92         self.__simu_dir = simu_dir
93         self.__linux = linux
94         self.__timeout = timeout
95         self.__cur_step = cur_step
96         self.__nit_max = nit_max
97         self.__var_to_follow_path = var_to_follow_path
98         self.__gen_scripts_ini = gen_scripts_ini
99         self.__pause = pause
100         self.__min_step_val = min_step_val
101
102         self.__around_simulation = None
103         self.__dict_inputs = {}
104         self.__val = 0.
105         self.__ref_success_code = 99
106         self.__success_code = 99
107         self.__nit = 0
108         self.__converging_val = None
109         self.__list_inputs = []
110         self.__res = None
111
112         if dymosim_path is None:
113             self.__dymosim_path = None
114         elif path.isdir(dymosim_path):
115             if self.linux:
116                 self.__dymosim_path = path.join(path.abspath(dymosim_path),'dymosim')
117             else:
118                 self.__dymosim_path = path.join(path.abspath(dymosim_path),'dymosim.exe')
119         elif path.isfile(dymosim_path):
120             self.__dymosim_path = dymosim_path
121         else:
122             raise ValueError('Model file or directory not found using %s'%str(dymosim_path))
123
124         if check_inputs is True:
125             self.__check_inputs = dict_var_to_fix.keys()
126         else:
127             self.__check_inputs = check_inputs
128
129 ###############################################################################
130 #                                  GETTERS                                    #
131 ###############################################################################
132     @property
133     def main_dir(self):
134         """
135         Type: String
136         Path of the main directory - relative path from the path where the script
137         is executed or absolute path.
138         """
139         return self.__main_dir
140
141     @property
142     def check_inputs(self):
143         """
144         Variable used to select inputs that have to be checked: set value vs. used
145         Possible values:
146          - True : all variables in dict_inputs are checked
147          - List of variables name to be checked
148         """
149         return self.__check_inputs
150
151     @property
152     def simu_material_dir(self):
153         """
154         Type: String
155         Path of the main directory which contains all the files that are required to
156         simulate the model. These files will be copied and this directory will
157         not be changed - relative path from main_dir
158         """
159         return self.__simu_material_dir
160
161     @property
162     def store_res_dir(self):
163         """
164         Type: String
165         Name of the directory where the simulation results will be stored
166         """
167         return self.__store_res_dir
168
169     @property
170     def around_simulation0(self):
171         """
172         Type: Around_Simulation
173         around_simulation0 must contain enough information to create a script that
174         can make the simulation converge in the reference state
175         More information about object of type Around_Simulation in file
176         around_simulation.py.
177         """
178         return self.__around_simulation0
179
180     @property
181     def dict_var_to_fix(self):
182         """
183         Type: Dictionary
184         Dictionary which associates
185             key     (Modelica) name of the variable whose value must be fixed
186             value   Function which returns the value of variable key (0-> ref_val,
187                     1 -> target_val)
188         dict_var_to_fix can be generated using class Dict_Var_To_Fix.
189         """
190         return self.__dict_var_to_fix
191
192     @property
193     def simu_dir(self):
194         """
195         Type: String
196         Path of the directory in which all the simulations will be run
197         """
198         return self.__simu_dir
199
200     def dymosim_path(self):
201         """
202         Path of the directory that contains the dymosim.exe (or equivalent) file,
203         can be different of simu_dir
204         """
205         return self.__dymosim_path
206
207     @property
208     def linux(self):
209         """
210         If the script has to be run on Linux, this attribute should be setted to True.
211         On Linux, the exe file is indeed named "dymosim" instead of "dymosim.exe".
212         """
213         return self.__linux
214
215     @property
216     def timeout(self):
217         """
218         Type: Integer
219         If a simulation has not converged after timeout, the siumlation is stopped.
220         """
221         return self.__timeout
222
223     @property
224     def cur_step(self):
225         """
226         Type: Float
227         Current step (0: reference value;1: target value)
228         -> with cur_step=0.5,the simulation will try to converge in 2 steps.
229         """
230         return self.__cur_step
231
232     @property
233     def nit_max(self):
234         """
235         Type: Integer
236         Maximum number of iterations
237         """
238         return self.__nit_max
239
240     @property
241     def var_to_follow_path(self):
242         """
243         Type: String
244         Path of the file containing the values of the interesting
245         variables which are saved each time a simulation converges
246         """
247         return self.__var_to_follow_path
248
249     @property
250     def gen_scripts_ini(self):
251         """
252         Type: Boolean
253         If True, each time a simulation has converged, the associated
254         initialization script is generated.
255         """
256         return self.__gen_scripts_ini
257
258     @property
259     def pause(self):
260         """
261         Type: Float
262         Number of seconds that are given to the system to copy the files
263         I could try to replace it by calling a subprocess and waiting for the end
264         of this subprocess
265         """
266         return self.__pause
267
268     @property
269     def around_simulation(self):
270         """
271         Type: Around_Simulation
272         around_simulation contains enough information to generated initialization
273         scripts.
274
275         More information about object of type Around_Simulation in file
276         around_simulation.py.
277         """
278         return self.__around_simulation
279
280     @property
281     def dict_inputs(self):
282         """
283         Type: Dictionary
284         Dictionary associating to each variable whose value has to be given to
285         dsin.txt (variables whose values must be fixed + iteration variables) its value.
286         """
287         return self.__dict_inputs
288
289     @property
290     def val(self):
291         """
292         Type: float
293         Caracterise the distance from the reference state and to the target state.
294         0. in the reference state
295         1. in the final state when the working point change has reached its target
296         """
297         return self.__val
298
299     @property
300     def success_code(self):
301         """
302         Type: Integer
303         If a simulation converges, this attribute is setted to 0
304         If a simulation fails to initialize, this attribute is setted to 1
305         If a simulation fails after the initialization, this attribute is setted to 2
306         If a simulation does not fail but does not converge either, this attribute is setted to 3
307         Default value: 99
308         """
309         return self.__success_code
310
311     @property
312     def ref_success_code(self):
313         """
314         Type: Integer
315         Before trying to reach the target state, the user should try to simulate
316         the reference state.
317         If this "reference simulation" converges, this attribute is setted to 0.
318         If this "reference simulation" fails to initialize, this attribute is setted to 1
319         If this "reference simulation" fails after the initialization, this attribute is setted to 2
320         If this "reference simulation" does not fail but does not converge either, this attribute is setted to 3
321         Default value: 99
322
323         If the value of this attribute is 1, no further calculation will be made.
324         """
325         return self.__ref_success_code
326
327     @property
328     def nit(self):
329         """
330         Type: Integer
331         Number of simulation that have been run
332         """
333         return self.__nit
334
335     @property
336     def converging_val(self):
337         """
338         Type: Float
339         Highest value for which the simulation has converged
340         0 -> reference state
341         1 -> target state
342         """
343         return self.__converging_val
344
345     @property
346     def min_step_val(self):
347         """
348         Type: Float
349         Mininum allowed value for the step. If the script tries to use a value
350         lower than min_step_val, it will stop.
351         """
352         return self.__min_step_val
353
354     @property
355     def list_inputs(self):
356         """
357         Type: List
358         List of the input variable (Modelica) names
359         """
360         return self.__list_inputs
361
362     @property
363     def res(self):
364         """
365         Type: Dictionary
366         It is a dictionary containing the values of self.list_inputs at the end
367         of a simulation (the values of the iteration variables in this attribute
368         will therefore be injected in the next simulation)
369         """
370         return self.__res
371 ###############################################################################
372 #                                  SETTERS                                    #
373 ###############################################################################
374
375     def set_success_code(self,value):
376         """
377         Inputs
378             value   Integer
379
380         See the description of attribute success_code for more information
381         """
382         self.__success_code = value
383
384     def set_ref_success_code(self,value):
385         """
386         Inputs
387             value   Integer
388         See the description of attribute success_code for more information
389         """
390         self.__ref_success_code = value
391
392     def set_converging_val(self,value):
393         """
394         Inputs
395             value   Float
396         Highest value for which the simulation has converged up to the moment
397         this method is called
398         """
399         self.__converging_val = value
400
401 ###############################################################################
402 #                                 RESETTERS                                   #
403 ###############################################################################
404     def reset_val(self):
405         """
406         Reset value of attribute val to its reference value: 0.
407         """
408         self.__val = 0.
409
410 ###############################################################################
411 #                               MAIN METHODS                                  #
412 ###############################################################################
413
414     def increase_nit(self):
415         """
416         Increase value of attribute nit.
417         This method must be called when a simulation is run.
418         """
419         self.__nit += 1
420
421     def get_path_store_res_dir(self):
422         """
423         Return the path in which the results are saved.
424         """
425         return path.abspath(path.join(self.main_dir,self.store_res_dir))
426
427     def create_working_directory(self):
428         """
429         A directory in which all the simulations will be made is created.
430         The files that are in self.simu_material_dir are copie into this new directory.
431         If self.store_res_dir does not exist in main_dir, a directory named self.store_res_dir is created
432         """
433         if path.exists(path.join(self.main_dir,self.simu_dir)):
434             rmtree(path.join(self.main_dir,self.simu_dir), ignore_errors=True)   #added 19_07_2018
435         copytree(path.abspath(path.join(self.main_dir,self.simu_material_dir)),path.join(self.main_dir,self.simu_dir))
436
437         if path.exists(path.join(self.main_dir,self.store_res_dir)):
438             rmtree(path.join(self.main_dir,self.store_res_dir), ignore_errors=True)  #added 19_07_2018
439         mkdir(path.join(self.main_dir,self.store_res_dir))
440
441     def reset_res(self):
442         """
443         Reset value of attribute res to {} (=a dictionary without any keys)
444         """
445         self.__res = {}
446
447     def set_res(self):
448         """
449         Set the value of attribute res.
450         This method has te be called when a simulation has converged and before
451         the result is written into a file by method write_res
452         """
453         self.reset_res()
454
455         for var in self.list_inputs:
456             if var in self.dict_var_to_fix:
457                 self.__res[var] = self.dict_inputs[var]
458             else:
459                 #After a simulation, self.dict_inputs is not modified (it would
460                 #be a nonsense to change self.dict_inputs if no simulation has
461                 #to be done), but self.around_simulation.dict_iter_var is modified,
462                 #that is the reason why it is necessary to extract the values
463                 #in self.around_simulation.dict_iter_var.
464                 self.__res[var] = self.around_simulation.dict_iter_var[var]
465
466
467     def set_dict_inputs(self,ref=False):
468         """
469         Set the value of attribute dict_inputs
470         The values of the iterations variables comes from the last simulation
471         which has converged. The values of the parameters that have to be
472         fixed are calculated with the functions of self.dict_var_to_fix.
473
474         If ref=True, val is given the value 0 and the iteration variables are
475         given the values they have in self.simulation0.dict_iter_var.
476         """
477         if ref:
478             self.reset_val()
479             self.__dict_inputs = copy(self.around_simulation0.dict_iter_var)
480
481         else:
482             #Get the iteration variables and their values
483             self.__dict_inputs = copy(self.around_simulation.dict_iter_var)
484
485         #Get the values of the variables that have to be fixed
486         for var_to_fix in self.dict_var_to_fix:
487             self.dict_inputs[var_to_fix] = self.dict_var_to_fix[var_to_fix](self.val)
488
489         #Set the value of attribute list_inputs if it has no value
490         if self.list_inputs == []:
491             self.__list_inputs = list(self.dict_inputs.keys())
492             self.list_inputs.sort()
493
494     def write_res(self,ref=False):
495         """
496         Write in self.var_to_follow_path the values of the inputs at the end
497         of the simulation
498         """
499         with open(self.var_to_follow_path,'a') as output:
500             if ref:
501                 output.write('val'.ljust(50)+';')
502                 for var in self.list_inputs[:-1]:
503                     output.write(var.ljust(50)+';')
504                 output.write(self.list_inputs[-1]+'\n')
505
506             output.write(str(self.val).ljust(50)+';')
507             for var in self.list_inputs[:-1]:
508                 output.write(str(self.res[var]).ljust(50)+';')
509             output.write(str(self.res[var])+'\n')
510
511     def clean_var_to_follow(self):
512         """
513         If the file self.var_to_follow_path exists, it is erased when this
514         method is called
515         """
516         if path.exists(self.var_to_follow_path):
517             remove(self.var_to_follow_path)
518
519     def fauto_simu(self,ref=False,final_cleaning=False):
520         """
521         Erase the old simulation files from the simulation directory and run a
522         simulation with the current values of the iteration variables
523         """
524         #Create an object of type Automatic_Simulation, which will make it easy
525         #to run the simulation
526         #Check inputs only for the target case
527         logging.debug('Val %s' % self.val)
528         if (not ref) and (self.val == 1.) :
529             tmp_check = self.check_inputs
530             logging.debug('Performing input check on the target case (run %s)' % self.nit)
531         else :
532             tmp_check = []
533
534         logging.debug('Input of the current case:')
535         for key in self.dict_inputs.keys():
536             logging.debug('%s = %s' % (key,self.dict_inputs[key]))
537
538         auto_simu = Automatic_Simulation(simu_dir=path.join(self.main_dir,self.simu_dir),
539                               dict_inputs=self.dict_inputs,
540                               dymosim_path = self.__dymosim_path,
541                               logfile=True,
542                               timeout=self.timeout,
543                               copy_dsres_to=path.join(self.get_path_store_res_dir(),str(self.val)+'.mat'),
544                               check_inputs=tmp_check,
545                               linux = self.linux,
546                               without_modelicares = True) # A garder à False car sinon write_in_dsin efface dic_inputs... update 12/2022: writ_in_dsin modifié pour ne pas vider dict_inputs
547
548         #Write in log file
549         logging.info('Time = '+get_cur_time()+' \t'+'Current value: '+str(self.val))
550
551         self.set_success_code(99)
552         self.increase_nit()
553
554         #Run the simulation
555         auto_simu.single_simulation()
556
557
558         #If the simulation has converged
559         if auto_simu.success_code == 0:
560
561             #If an initialization script has to be created
562             if self.gen_scripts_ini:
563                 #If the simulation state is the reference state
564                 if ref:
565                     self.around_simulation0.generated_script_options={'file_name':path.join(self.get_path_store_res_dir(),str(self.val)+'.mos')}
566                     self.around_simulation0.create_script_ini()
567                 else:
568                     # -----> Option should be added to the dictionary and not reset !!!!!
569                     self.around_simulation.generated_script_options={'file_name':path.join(self.get_path_store_res_dir(),str(self.val)+'.mos')}
570                     self.around_simulation.create_script_ini()
571
572             if ref:
573                 logging.info('Time = '+get_cur_time()+' \t'+'Reference simulation has converged')
574                 self.set_ref_success_code(0)
575                 self.__around_simulation = copy(self.around_simulation0)
576             else:
577                 logging.info('Time = '+get_cur_time()+' \t'+'Simulation has converged')
578
579             #If this simulation is the first to converge, the next values of the iteration variables will be built from the mat result file.
580             if self.converging_val == None:
581                 self.around_simulation.iter_var_values_options['source'] = 'from_mat'
582
583
584             self.set_success_code(0)
585             self.set_converging_val(self.val)
586             self.around_simulation.mat = path.join(self.get_path_store_res_dir(),str(self.val)+".mat")
587             self.around_simulation.set_dict_iter_var()
588             self.set_res()
589             self.write_res(ref=ref)
590
591         else:
592             self.set_success_code(auto_simu.success_code)
593
594             if ref:
595                 self.set_ref_success_code(auto_simu.success_code)
596                 logging.info('Time = '+get_cur_time()+' \t'+'Reference simulation has not converged. Success_code = '+str(self.ref_success_code))
597             else:
598                 logging.info('Time = '+get_cur_time()+' \t'+'Simulation has not converged. Success_code = '+str(self.success_code))
599         if final_cleaning:
600             auto_simu.cleaning()
601
602     def check_ref_simulation(self):
603         """
604         Run the simulation in the reference state
605         """
606         #Erase the file in which the values of the inputs at the end of the
607         #simulations are saved
608         self.clean_var_to_follow()
609
610         #reset the values of the inputs
611         self.set_dict_inputs(ref=True)
612
613         #Run the simulation
614         self.fauto_simu(ref=True)
615
616     def change_val(self):
617         """
618         Adapt the step if it is necessary and change the value of attribute val.
619         The step is divided by 2 when a simulation does not converge.
620         """
621         if self.success_code == 1:
622             self.__cur_step = float(self.cur_step)/2.
623         elif self.success_code in [2,3]:
624             logging.info('Time = '+get_cur_time()+' \t'+'Method change_val has been called whereas the success_code is in [2,3] which \
625             is not normal. The methods designed in this script can only help to initialize the simulations. If the simulation fails \
626             whereas the initialization has been a success, it is not useful to decrease the step')
627             self.__cur_step = 0
628         else:
629             if self.val+self.cur_step > 1:
630                 self.__cur_step = 1.-self.val
631         if self.converging_val == None:
632             self.__val = self.cur_step
633         else:
634             self.__val = self.converging_val + self.cur_step
635
636     def working_point_modification(self,final_cleaning=False,skip_reference_simulation=False):
637         """
638         Main method of this class.
639         It tries to reach the target state from the reference state.
640             The step is adapted with change_val depending on the success of the simulations
641             Before a simulation is run, the variables are initialized to
642                 Their calculated value if it is a variable whose value should be imposed
643                 The value calculated by Dymola in the last simulation that has converged if it is an iteration variable
644         """
645         if skip_reference_simulation:
646             self.skip_reference_simulation()
647
648         if self.ref_success_code == 0 or skip_reference_simulation:
649             while self.nit < self.nit_max and (self.converging_val == None or self.converging_val < 1) and self.success_code not in [2,3] :
650                 self.change_val()
651                 if self.crit_check() :
652                     break
653                 if self.cur_step < self.min_step_val : #Check after change_val call
654                     break
655                 self.set_dict_inputs(ref=False)
656                 logging.debug(".nit : %s\n.converging_val : %s\n.around_simulation.mat : %s\n.around_simulation._source_ : %s" %(self.nit,self.converging_val,self.around_simulation.mat,self.around_simulation.iter_var_values_options['source']))
657                 self.fauto_simu(ref=False,final_cleaning=final_cleaning)
658
659             if not(self.nit < self.nit_max):
660                 logging.info('Time = '+get_cur_time()+' \t'+'STOP: maximum number of iterations reached.')
661             if self.converging_val != None and not(self.converging_val < 1):
662                 logging.info('Time = '+get_cur_time()+' \t'+'FINISHED: Target values reached.')
663             if self.success_code in [2,3]:
664                 logging.info('Time = '+get_cur_time()+' \t'+'STOP: Simulation initialization has succeeded, but simulation has failed or was not given enough time to converge')
665
666     def crit_check(self):
667         """
668         Check whether the step-size and the max-number-of-iterations criteria has been met
669         """
670         check = False
671         if self.success_code == 1 : #Check criteria only if the previous iteration did not initialize
672             if (self.cur_step < self.min_step_val) : #Minimum step size criterium
673                 logging.info('Time = '+get_cur_time()+' \t'+'STOP: The step ('+str(self.cur_step)+') has crossed the minimum allowed value: '+str(self.min_step_val))
674                 check = True
675             elif ( (self.val + self.cur_step*(self.nit_max - self.nit - 1)) < 1. ) : #Max number of step criterium (with "forecast")
676                 logging.info('Time = '+get_cur_time()+' \t'+'STOP: Impossible to success before hitting the number of iterations limit: '+str(self.nit_max))
677                 logging.info('Time = '+get_cur_time()+' \t'+'STOP: Completed iterations: '+str(self.nit)+'; next value: '+str(self.val)+'; current step: '+str(self.cur_step))
678                 check = True
679         return check
680
681     def skip_reference_simulation(self):
682         logging.info('Time = '+get_cur_time()+' \t'+'Reference simulation is not launched. It is safer to choose skip_reference_simulation=False')
683         self.__around_simulation = copy(self.around_simulation0)
684
685 ###############################################################################
686 #                            CLASS Dict_Var_To_Fix                            #
687 ###############################################################################
688
689 class Dict_Var_To_Fix(object):
690     """
691     This class makes it easier to create the attribute dict_var_to_fix of the objects
692     of class Working_Point_Modification in the case there are only a ref value and
693     a target value.
694     """
695     def __init__(self,option,dict_var_to_fix=None,dict_auto_var_to_fix=None):
696         if dict_var_to_fix == None:
697             dict_var_to_fix = {}
698
699         self.__option = option
700         self.__dict_var_to_fix = dict_var_to_fix
701         self.__dict_auto_var_to_fix = dict_auto_var_to_fix
702
703     @property
704     def option(self):
705         """
706         Type: String
707         Available values for option are
708             'manual'        -> dict_var_to_fix is directly used
709             'automatic'     -> dict_var_to_fix is built from dict_auto_var_to_fix
710         """
711         return self.__option
712
713     @property
714     def dict_var_to_fix(self):
715         """
716         Type: Dictionary
717         Dict_var_to_fix is a dictionary which associates
718             key     name of the variable whose value must be fixed
719             value   Function which returns the value of variable key when val is x
720                     0 -> reference value of key
721                     1 -> target value of key
722         """
723         return self.__dict_var_to_fix
724
725     @property
726     def dict_auto_var_to_fix(self):
727         """
728         Type: Dictionary
729         This dictionary is used if option == 'automatic'
730         It associates
731             key     name of the variable whose value must be fixed
732             value   (ref_val,target_val) : A 2-tuple composed of the value of
733                     the variable key in the reference state and the target value
734                     of the variable key
735         """
736         return self.__dict_auto_var_to_fix
737
738     def set_dict_var_to_fix(self):
739         """
740         Set the value of attribute dict_var_to_fix when option is 'automatic'.
741         If option is 'manual', attribute dict_var_to_fix already has a value.
742
743         Create a function for each variable, which returns
744             function(0) -> ref_val
745             function(1) -> target_val
746         """
747         if self.option == 'automatic':
748             for var in self.dict_auto_var_to_fix:
749                 ref_val = self.dict_auto_var_to_fix[var][0]
750                 target_val = self.dict_auto_var_to_fix[var][1]
751                 self.__dict_var_to_fix[var] = self.f_creator(ref_val,target_val)
752
753     def f_creator(self,ref_val,target_val):
754         """
755         Return the linear function returning
756             ref_val if it is given 0
757             target_val if ti is given 1
758         This function is used in method set_dict_var_to_fix of class Dict_Var_To_Fix
759         when option == 'automatic'
760         """
761         def f(x):
762             if x<0 or x>1:
763                 exit("x has to be between 0 and 1")
764             else:
765                 return ref_val+x*(target_val-ref_val)
766         return f
767
768 if __name__ == '__main__':
769     print('\n  AUTODIAGNOSTIC\n  ==============\n')