1""" 2PySCeS - Python Simulator for Cellular Systems (http://pysces.sourceforge.net) 3 4Copyright (C) 2004-2020 B.G. Olivier, J.M. Rohwer, J.-H.S Hofmeyr all rights reserved, 5 6Brett G. Olivier (bgoli@users.sourceforge.net) 7Triple-J Group for Molecular Cell Physiology 8Stellenbosch University, South Africa. 9 10Permission to use, modify, and distribute this software is given under the 11terms of the PySceS (BSD style) license. See LICENSE.txt that came with 12this distribution for specifics. 13 14NO WARRANTY IS EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 15Brett G. Olivier 16""" 17from __future__ import division, print_function 18from __future__ import absolute_import 19from __future__ import unicode_literals 20 21from pysces.version import __version__ 22__doc__ = "PySCeS parser module -- uses PLY 1.5 or newer" 23 24 25import os, copy 26import pysces.lib.lex 27import pysces.lib.yacc 28from getpass import getuser 29from time import sleep, strftime 30 31try: 32 from importlib import reload # Python 3 33except ImportError: 34 pass 35 36# use net stoichiometry, disable for StomPy 37__USE_NET_STOICH__ = True 38 39RESERVEDTERMS = (\ 40 'Vvec',\ 41 '__CDvarUpString__',\ 42 '__SALL__',\ 43 '__SI__',\ 44 '__epmatrix__',\ 45 '__evmatrix__',\ 46 '__fixed_species__',\ 47 '__kmatrix__',\ 48 '__kzeromatrix__',\ 49 '__lmatrix__',\ 50 '__lzeromatrix__',\ 51 '__mapFunc_R__',\ 52 '__mapFunc__',\ 53 '__mode_suppress_info__',\ 54 '__modifiers__',\ 55 '__nmatrix__',\ 56 '__nrmatrix__',\ 57 '__parameters__',\ 58 '__reactions__',\ 59 '__s_varFunc__',\ 60 '__species__',\ 61 '__vFunc__',\ 62 'cc_all',\ 63 'cc_all_col',\ 64 'cc_all_row',\ 65 'cc_conc',\ 66 'cc_conc_col',\ 67 'cc_conc_row',\ 68 'cc_flux',\ 69 'cc_flux_col',\ 70 'cc_flux_row',\ 71 'conservation_matrix',\ 72 'conservation_matrix_col',\ 73 'conservation_matrix_row',\ 74 'display_debug',\ 75 'eModeExe_dbl',\ 76 'eModeExe_int',\ 77 'elas_epar_upsymb',\ 78 'elas_evar_upsymb',\ 79 'elas_par',\ 80 'elas_par_col',\ 81 'elas_par_row',\ 82 'elas_par_u',\ 83 'elas_var',\ 84 'elas_var_col',\ 85 'elas_var_row',\ 86 'elas_var_u',\ 87 'emode_file',\ 88 'emode_intmode',\ 89 'emode_userout',\ 90 'fintslv_range',\ 91 'fintslv_rmult',\ 92 'fintslv_step',\ 93 'fintslv_tol',\ 94 'fixed_species',\ 95 'hybrd_epsfcn',\ 96 'hybrd_factor',\ 97 'hybrd_maxfev',\ 98 'hybrd_mesg',\ 99 'hybrd_xtol',\ 100 'info_flux_conserve',\ 101 'info_moiety_conserve',\ 102 'init_species_array',\ 103 'kel_unscaled',\ 104 'kmatrix',\ 105 'kmatrix_col',\ 106 'kmatrix_row',\ 107 'kmatrix_scaled',\ 108 'kzeromatrix',\ 109 'kzeromatrix_col',\ 110 'kzeromatrix_row',\ 111 'lmatrix',\ 112 'lmatrix_col',\ 113 'lmatrix_row',\ 114 'lmatrix_scaled',\ 115 'lsoda_atol',\ 116 'lsoda_h0',\ 117 'lsoda_hmax',\ 118 'lsoda_hmin',\ 119 'lsoda_mesg',\ 120 'lsoda_mxordn',\ 121 'lsoda_mxords',\ 122 'lsoda_mxstep',\ 123 'lsoda_rtol',\ 124 'lzeromatrix',\ 125 'lzeromatrix_col',\ 126 'lzeromatrix_row',\ 127 'mach_floateps',\ 128 'mca_ccall_altout',\ 129 'mca_ccall_concout',\ 130 'mca_ccall_fluxout',\ 131 'mca_ccj_upsymb',\ 132 'mca_ccs_upsymb',\ 133 'mca_ci',\ 134 'mca_ci_col',\ 135 'mca_ci_row',\ 136 'mca_cjd',\ 137 'mca_cjd_col',\ 138 'mca_cjd_row',\ 139 'mca_csd',\ 140 'mca_csd_col',\ 141 'mca_csd_row',\ 142 'misc_write_arr_lflush',\ 143 'mode_eigen_output',\ 144 'mode_elas_deriv',\ 145 'mode_elas_deriv_dx',\ 146 'mode_elas_deriv_factor',\ 147 'mode_elas_deriv_min',\ 148 'mode_elas_deriv_order',\ 149 'mode_mca_scaled',\ 150 'mode_number_format',\ 151 'mode_sim_init',\ 152 'mode_sim_max_iter',\ 153 'mode_solver',\ 154 'mode_solver_fallback',\ 155 'mode_solver_fallback_integration',\ 156 'mode_state_init',\ 157 'mode_state_init2_array',\ 158 'mode_state_init3_factor',\ 159 'mode_state_mesg',\ 160 'modifiers',\ 161 'nleq2_ibdamp',\ 162 'nleq2_iormon',\ 163 'nleq2_iscal',\ 164 'nleq2_iter',\ 165 'nleq2_jacgen',\ 166 'nleq2_mesg',\ 167 'nleq2_mprerr',\ 168 'nleq2_nonlin',\ 169 'nleq2_qnscal',\ 170 'nleq2_qrank1',\ 171 'nleq2_rtol',\ 172 'nmatrix',\ 173 'nmatrix_col',\ 174 'nmatrix_row',\ 175 'nrmatrix',\ 176 'nrmatrix_col',\ 177 'nrmatrix_row',\ 178 'parameters',\ 179 'pitcon_abs_tol',\ 180 'pitcon_allow_badstate',\ 181 'pitcon_filter_neg',\ 182 'pitcon_filter_neg_res',\ 183 'pitcon_fix_small',\ 184 'pitcon_flux_gen',\ 185 'pitcon_init_par',\ 186 'pitcon_iter',\ 187 'pitcon_jac_opt',\ 188 'pitcon_jac_upd',\ 189 'pitcon_limit_point_idx',\ 190 'pitcon_limit_points',\ 191 'pitcon_max_grow',\ 192 'pitcon_max_step',\ 193 'pitcon_max_steps',\ 194 'pitcon_min_step',\ 195 'pitcon_output_lvl',\ 196 'pitcon_par_opt',\ 197 'pitcon_par_space',\ 198 'pitcon_rel_tol',\ 199 'pitcon_start_dir',\ 200 'pitcon_start_step',\ 201 'pitcon_targ_val',\ 202 'pitcon_targ_val_idx',\ 203 'pitcon_target_points',\ 204 'reactions',\ 205 'scan1_dropbad',\ 206 'scan1_mca_mode',\ 207 'scan_in',\ 208 'scan_out',\ 209 'scan_res',\ 210 'sim_end',\ 211 'sim_points',\ 212 'sim_res',\ 213 'sim_start',\ 214 'sim_time',\ 215 'species',\ 216 'state_flux',\ 217 'state_set_conserve',\ 218 'state_species',\ 219 'write_array_header',\ 220 'write_array_html_footer',\ 221 'write_array_html_format',\ 222 'write_array_html_header',\ 223 'write_array_spacer',\ 224 'zero_val',\ 225 ) 226 227class PySCeSParser: 228 """The original PySCeS parser has been rewritten and extended to include 229 many features that allow for SBML compatibility such as MathML 2.0 support, 230 function definitions, assignment/rate rules and events.""" 231 232 MathmlToNumpy_funcs = { 233 'pow' : 'pow', 'root' : 'pow', 'abs' : 'abs', 234 'exp' : 'math.exp', 'ln' : 'math.log', 'log' : 'math.log10', 235 'floor' : 'numpy.floor', 'ceiling' : 'numpy.ceil', 'factorial' : None, 236 'sin' : 'numpy.sin', 'cos' : 'numpy.cos', 'tan' : 'numpy.tan', 237 'sec' : None, 'csc' : None, 'cot' : None, 238 'sinh' : 'numpy.sinh', 'cosh' : 'numpy.cosh','tanh' : 'numpy.tanh', 239 'sech' : None, 'csch' : None, 'coth' : None, 240 'arcsin' : 'numpy.arcsin', 'arccos' : 'numpy.arccos', 'arctan' : 'numpy.arctan', 241 'arcsec' : None, 'arccsc' : None, 'arccot' : None, 242 'arcsinh' : 'numpy.arcsinh', 'arccosh' : 'numpy.arccosh', 'arctanh' : 'numpy.arctanh', 243 'arcsech' : None, 'arccsch' : None, 'arccoth' : None, 244 'eq' : 'operator.eq', 'neq' : 'operator.ne', 245 'gt' : 'operator.gt', 'geq' : 'operator.ge', 246 'lt' : 'operator.lt', 'leq' : 'operator.le', 247 'ceil' : 'numpy.ceil', 'sqrt' : 'numpy.sqrt', # libsbml aliases 248 'equal' : 'operator.eq', 'not_equal' : 'operator.ne', # numpy2numpy aliases 249 'greater' : 'operator.gt', 'greater_equal' : 'operator.ge', # numpy2numpy aliases 250 'less' : 'operator.lt', 'less_equal' : 'operator.le', # numpy2numpy aliases 251 'ne' : 'operator.ne', 'ge' : 'operator.ge', 'le' : 'operator.le', # operator2operator 252 'xor' : 'operator.xor', 'piecewise' : 'self._piecewise_', '_piecewise_' : 'self._piecewise_', 253 'not' : 'operator.not_', 'not_' : 'operator.not_', 254 'max':'max', 'min' : 'min' 255 } 256 257 MathmlToNumpy_symb = { 258 'notanumber' : 'numpy.NaN', 'pi' : 'numpy.pi', 259 'infinity' : 'numpy.Infinity', 'exponentiale' : 'numpy.e', 260 'true' : 'True', 'false' : 'False', 'True' : 'True', 'False' : 'False' 261 } 262 263 SymbolReplacements = None 264 265 MathmlToInfix = { 266 'and' : 'and', 'or' : 'or', 'true' : 'True', 'false' : 'False', 'xor' : 'xor' 267 } 268 269 BOOLEANTRUE = ('True','TRUE','true') 270 BOOLEANFALSE = ('False','False','false') 271 272 precedence = ( 273 ('left', 'PLUS', 'MINUS'), 274 ('left', 'TIMES', 'DIVIDE'), 275 ('left', 'POWER'), 276 ('right', 'UMINUS') 277 ) 278 279 # List of token names 280 tokens = ('FIXDEC', 281 'FUNCDEC', 282 'EVENTDEC', 283 'COMPDEC', 284 'RRULEDEC', 285 'OBJFUNCDEC', 286 'FLUXBNDDEC', 287 'USERCNSTRDEC', 288 'IRREV', 289 'REAL', 290 'INT', 291 'PLUS', 292 'MINUS', 293 'TIMES', 294 'DIVIDE', 295 'POWER', 296 'LPAREN', 297 'RPAREN', 298 'EQUALS', 299 'SYMBEQUI', 300 'COMMA', 301 'REACTION_ID', 302 'STOICH_COEF', 303 'NAME', 304 'FORCEFUNC', 305 'TIMEFUNC', 306 'USERFUNC', 307 'INITFUNC', 308 'IN', 309 'POOL', 310 'KEYWORD', 311 'KEYWORDB', 312 'UNIT', 313 'MULTICOMMENT') 314 315 ReservedTerms = RESERVEDTERMS 316 317################################################################# 318################################################################# 319 320 def __init__(self,debug=0): 321 self.display_debug = debug 322 # Lists 323 self.LexErrors = [] # List of lexing errors 324 self.ParseErrors = [] 325 self.SymbolErrors = [] 326 self.ParseOK = True 327 self.LexOK = True 328 self.ModelUsesNumpyFuncs = False 329 330 self.ReactionIDs = [] # List of reaction names 331 self.Names = [] # List of all reagent, parameter and function names 332 self.nDict = {} # Dictionary containing all reaction information 333 self.InDict = {} # spatial dictionary 334 self.sDict = {} 335 self.pDict = {} #parameter dict 336 self.uDict = {'substance': {'exponent': 1, 'multiplier': 1.0, 'scale': 0, 'kind': 'mole'}, 337 'volume': {'exponent': 1, 'multiplier': 1.0, 'scale': 0, 'kind': 'litre'}, 338 'time': {'exponent': 1, 'multiplier': 1.0, 'scale': 0, 'kind': 'second'}, 339 'length': {'exponent': 1, 'multiplier': 1.0, 'scale': 0, 'kind': 'metre'}, 340 'area': {'exponent': 2, 'multiplier': 1.0, 'scale': 0, 'kind': 'metre'} 341 } 342 self.KeyWords = {'Modelname' : None, 343 'Description' : None, 344 'Species_In_Conc' : None, 345 'Output_In_Conc' : None 346 } 347 self.compartments = {} 348 self.InitStrings = [] # Initialisation strings 349 self.Inits = [] # Initialised entities 350 self.Reagents = [] # All reagents found during parsing of reactions 351 self.species = [] # Variable reagents that occur in reactions 352 self.FixedReagents = [] # Fixed reagents 353 self.ReacParams = [] # Temporary list of reaction parameters 354 self.InitParams = [] # Initialised parameters 355 356 self.ForceFunc = [] # Forcing function definition 357 self.TimeFunc = [] # Time function definition 358 self.UserFunc = [] # User function definition 359 self.InitFunc = [] # Intitialization function definition 360 self.Functions = {} # new Function blocks 361 self.Events = {} # new event blocks 362 self.AssignmentRules = {} # new forcing function blocks 363 self.UserFuncs = {} # new user function blocks 364 self.ModelInit = {} # new model initialisation blocks 365 self.cbm_FluxBounds = {} # flux bounds in a CB model 366 self.cbm_ObjectiveFunctions = {} # objective functions for a CB model 367 self.cbm_UserFluxConstraints = {} # objective functions for a CB model 368 369 370 self.mach_spec_eps2k = 2.2204460492503131e-14 371 self.AllRateEqsGiven = 1 # Flag to check that all rate equations have been given 372 self.Debug = 0 373 374 # elementary regular expressions used as building blocks 375 self.Int = r'\d+' # Integer 376 self.Dec = self.Int + '\.' + self.Int # Decimal 377 self.Exp = r'([E|e][\+|\-]?)' + self.Int # Exponent 378 self.Real = self.Dec + '(' + self.Exp + ')?' + '|' + self.Int + self.Exp # Real - dec w/o optional exp or int with exp 379 380 # Simple tokens 381 self.t_IRREV = r'>' 382 self.t_REAL = self.Real 383 self.t_INT = self.Int 384 self.t_PLUS = r'\+' 385 self.t_MINUS = r'-' 386 self.t_TIMES = r'\*' 387 self.t_DIVIDE = r'/' 388 self.t_POWER = '\*\*' 389 self.t_LPAREN = r'\(' 390 self.t_RPAREN = r'\)' 391 self.t_EQUALS = r'=' 392 self.t_COMMA = r',' 393 self.t_POOL = r'\$pool' 394 self.t_IN = r'@' 395 396 t_ignore = ' \t\r' # Ignore spaces and tabs --- and windows return - brett 20040229 397 398 def t_comment(self,t): 399 r'\#.+\n' # Match from # to newline 400 t.lineno += 1 # Increment line number 401 402 def t_multilinecomment(self,t): 403 r'"""(.|\n)*?"""' 404 t.type = 'MULTICOMMENT' 405 406 def t_newline(self,t): 407 r'\n+' # Match newline 408 t.lineno += len(t.value) # Increment with number of consecutive newlines 409 410 def t_SYMBEQUI(self,t): 411 r'!=|<' 412 t.type = 'SYMBEQUI' 413 print('t', t) 414 return t 415 416 def t_FIXDEC(self,t): 417 r'FIX:' 418 t.type = 'FIXDEC' 419 t.value = 'FIX:' 420 return t 421 422 def t_KW_Name(self, t): 423 r'Modelname:.+\n' 424 t.type = 'KEYWORD' 425 return t 426 427 def t_KW_Description(self, t): 428 r'Description:(.| )+\n' 429 t.type = 'KEYWORD' 430 return t 431 432 def t_KW_ModelType(self, t): 433 r'ModelType:(.| )+\n' 434 t.type = 'KEYWORD' 435 return t 436 437 def t_KW_Species_In_Conc(self, t): 438 r'Species_In_Conc:.+\n' 439 t.type = 'KEYWORDB' 440 return t 441 442 def t_KW_Output_In_Conc(self, t): 443 r'Output_In_Conc:.+\n' 444 t.type = 'KEYWORDB' 445 return t 446 447 def t_Unit(self, t): 448 r'Unit(Substance|Time|Length|Area|Volume):.+\n' 449 t.type = 'UNIT' 450 return t 451 452 def t_FUNCDEC(self, t): 453 r'Function:(.|\n)*?}' 454 t.type = 'FUNCDEC' 455 return t 456 457 def t_RATERULEDEC(self, t): 458 r'RateRule:(.|\n)*?}' 459 t.type = 'RRULEDEC' 460 return t 461 462 def t_EVENTDEC(self, t): 463 r'Event:(.|\n)*?}' 464 t.type = 'EVENTDEC' 465 return t 466 467 def t_OBJFUNCDEC(self,t): 468 r'ObjectiveFunctions:(.|\n)*?}' 469 t.type = 'OBJFUNCDEC' 470 return t 471 472 def t_FLUXBNDDEC(self,t): 473 r'FluxBounds:(.|\n)*?}' 474 t.type = 'FLUXBNDDEC' 475 return t 476 477 def t_USERCNSTRDEC(self,t): 478 r'UserFluxConstraints:(.|\n)*?}' 479 t.type = 'USERCNSTRDEC' 480 return t 481 482 483 def t_COMPDEC(self, t): 484 r'Compartment:.+\n' # match from cc<space> to end of line 485 t.type = 'COMPDEC' 486 return t 487 488 def t_FORCEFUNC(self,t): 489 r'!F\ .+\n' # match from !F<space> to end of line 490 t.type = 'FORCEFUNC' 491 t.lineno += 1 # Increment line number 492 t.value = t.value[3:] 493 return t 494 495 def t_TIMEFUNC(self,t): 496 r'!T\ .+\n' # match from !T<space> to end of line 497 t.type = 'TIMEFUNC' 498 t.lineno += 1 # Increment line number 499 t.value = t.value[3:] 500 return t 501 502 def t_USERFUNC(self,t): 503 r'!U\ .+\n' # match from !U<space> to end of line 504 t.type = 'USERFUNC' 505 t.lineno += 1 # Increment line number 506 t.value = t.value[3:] 507 return t 508 509 def t_INITFUNC(self,t): 510 r'!I\ .+\n' # match from !I<space> to end of line 511 t.type = 'INITFUNC' 512 t.lineno += 1 # Increment line number 513 t.value = t.value[3:] 514 return t 515 516 def t_REACTION_ID(self,t): 517 r'[a-zA-Z_][\w@]*:' # Match any letter in [a-zA-Z0-9_] up to a colon 518 # allow "_" to startswith - brett 20050823 519 t.type = 'REACTION_ID' 520 t.value = t.value[:-1] # remove the colon 521 522 #print t.value, self.ReactionIDs 523 #sleep(1) 524 if '@' in t.value: 525 ts = t.value.split('@') 526 t.value = ts[0] 527 self.InDict.update({ts[0] : ts[1]}) 528 if t.value in self.ReactionIDs: 529 # brett I think this is hyperactive reporting ... removing 20050321 530 # self.LexErrors.append(('Duplicate ReactionID ', t.lineno, t.value, t.type)) 531 pass 532 else: 533 self.ReactionIDs.append(t.value) 534 # possible alternative to above - brett 535 #if t.value not in self.ReactionIDs: 536 # self.ReactionIDs.append(t.value) 537 return t 538 539 def t_STOICH_COEF(self,t): 540 r'\{[\d+|\.|E|e|\+|\-]+\}' 541 542 # old regex upgraded to handle anything that resembles a number, might cause downstream problems 543 # r'\{\d+\}|\{\d+\.\d+\}' 544 t.type = 'STOICH_COEF' 545 t.value = t.value[1:-1] # Remove left and right curly brackets 546 return t 547 548 def t_NAME(self,t): 549 r'numpy\.[\w]*|math\.[\w]*|operator\.[\w]*|random\.[\w]*|[a-zA-Z_][\w@]*' 550 SciCons = False 551 if '@' in t.value: 552 ts = t.value.split('@') 553 t.value = ts[0] 554 self.InDict.update({ts[0] : ts[1]}) 555 if t.value in self.MathmlToNumpy_symb: 556 if self.MathmlToNumpy_symb[t.value] == None: 557 self.SymbolErrors.append(t.value) 558 print('\nSymbol \"%s\" not yet supported by PySCeS.' % t.value) 559 gt = 'unknown_symbol_' + t.value 560 t.value = gt 561 else: 562 gt = self.MathmlToNumpy_symb[t.value] 563 t.value = gt 564 self.ModelUsesNumpyFuncs = 1 565 SciCons = True 566 elif self.SymbolReplacements != None and t.value in self.SymbolReplacements: 567 if t.value not in self.Names: 568 self.Names.append('self.' + self.SymbolReplacements[t.value]) 569 gt = 'self.' + self.SymbolReplacements[t.value] 570 t.value = gt 571 elif t.value not in self.Names and t.value != '_TIME_': # Only add to list if absent in list 572 self.Names.append('self.' + t.value) 573 if t.value[:6] == 'numpy.' or t.value[:5] == 'math.' or t.value[:9] == 'operator.' or t.value[:7] == 'random.': 574 pass 575 elif t.value not in self.MathmlToNumpy_funcs and not SciCons: # make class attributes, ignore function names 576 gt = 'self.' + t.value 577 t.value = gt 578 t.type = 'NAME' 579 return t 580 581 def t_error(self,t): 582 ## try: 583 ## self.LexErrors.append(('Lexer error ', t.lineno, t.value, t.type)) 584 ## except: 585 ## print 'Lexer error' 586 ## #print 'Illegal character, Line ' + str(t.lineno) + ' :' + str(t.value[0]) 587 ## t.skip(1) 588 589 print("Illegal character '%s'" % t.value[0]) 590 self.LexErrors.append(t.value[0]) 591 self.LexOK = False 592 t.lexer.skip(1) 593 594 ############## 595 # The parser # 596 ############## 597 598 def Show(self,name,tok): 599 if self.Debug: 600 print(name,tok) 601 602 def p_error(self,t): 603 try: 604 ## self.ParseErrors.append(('Syntax error ', t.lineno, t.value, t.type)) 605 self.ParseErrors.append(t) 606 except: 607 print('p_error generated a parsing error') 608 tok = pysces.lib.yacc.token() 609 while tok and tok.type != 'REACTION_ID': 610 tok = pysces.lib.yacc.token() 611 self.ParseOK = False 612 return tok 613 614 def p_model(self,t): 615 '''Model : Statement 616 | Model Statement ''' 617 618 self.Show('Model',t[0]) 619 620 def p_statement(self,t): 621 '''Statement : Fixed 622 | FunctionDec 623 | RateRuleDec 624 | EventDec 625 | ObjFuncDec 626 | FluxBndDec 627 | UserCnstrDec 628 | CompartmentDec 629 | ReactionLine 630 | Initialise 631 | Forcedfunc 632 | Timefunc 633 | Userfunc 634 | Initfunc 635 | NameInName 636 | KeyWord 637 | KeyWordB 638 | Unit 639 | MultiComment 640 | SymbEqui''' 641 self.Show('Statement',t[0]) 642 643 def p_nameinname(self, t): 644 '''NameInName : NAME IN NAME''' 645 print('This should never fire, what we need to do is ...') 646 647 def p_inequalities_symb(self, t): 648 '''SymbEqui : SYMBEQUI''' 649 print('p_inequalities_symb', t[:]) 650 t[0] = t[1] 651 652 def p_multicomment(self, t): 653 '''MultiComment : MULTICOMMENT''' 654 self.Show('KeyWord:',t[0]) 655 656 def p_keyword(self, t): 657 '''KeyWord : KEYWORD''' 658 ## print 'KeyWord:', t[:] 659 kpair = [e.strip() for e in t[1].split(':')] 660 if kpair[0] in self.KeyWords: 661 self.KeyWords.update({kpair[0] : kpair[1]}) 662 ## print self.KeyWords 663 self.Show('KeyWord:',t[0]) 664 665 def p_keywordboolean(self, t): 666 '''KeyWordB : KEYWORDB''' 667 ## print 'KeyWordB:', t[:] 668 kpair = [e.strip() for e in t[1].split(':')] 669 if kpair[0] in self.KeyWords: 670 if kpair[1] in self.BOOLEANTRUE: 671 self.KeyWords.update({kpair[0] : True}) 672 elif kpair[1] in self.BOOLEANFALSE: 673 self.KeyWords.update({kpair[0] : False}) 674 ## print self.KeyWords 675 self.Show('KeyWordB:',t[0]) 676 677 def p_unit(self, t): 678 '''Unit : UNIT''' 679 ## print 'u', t[:] 680 u = t[1].split(',') 681 u[0] = u[0].split(':') 682 u.append(u[0][1]) 683 u[0] = u[0][0].lower() 684 ## print u 685 u[0] = u[0].replace('unit','') 686 ## print u 687 for i in range(len(u)): 688 u[i] = u[i].strip() 689 if i in [1,2,3]: 690 u[i] = float(u[i]) 691 ## print u 692 self.uDict.update({u[0] : {'multiplier': u[1], 693 'scale': u[2], 694 'exponent': u[3], 695 'kind': u[4] 696 } 697 }) 698 self.Show('Unit:',t[0]) 699 700 def p_fixed(self,t): 701 '''Fixed : FIXDEC FixList''' 702 self.Show('Fixed:',t[0]) 703 704 def p_functiondec(self, t): 705 '''FunctionDec : FUNCDEC''' 706 ## print 'p_functiondec', t[:] 707 rawf = t[1].replace('Function:','').lstrip() 708 args = rawf[:rawf.find('{')].strip().split(',') 709 name = args.pop(0) 710 func = rawf[rawf.find('{')+1:rawf.find('}')] 711 self.Functions.update({name : { 712 'name' : name, 713 'args' : args, 714 'formula' : func 715 } 716 }) 717 self.Show('FunctionDec:',t[0]) 718 719 def p_rateruledec(self, t): 720 '''RateRuleDec : RRULEDEC''' 721 ## print 'p_functiondec', t[:] 722 rawf = t[1].replace('RateRule:','').lstrip() 723 name = rawf[:rawf.find('{')].strip() 724 func = rawf[rawf.find('{')+1:rawf.find('}')] 725 self.AssignmentRules.update({name : {'name' : name, 726 'formula' : func.strip(), 727 'type' : 'rate' 728 } 729 }) 730 self.Show('RateRuleDec:',t[0]) 731 732 def p_eventdec(self, t): 733 '''EventDec : EVENTDEC''' 734 rawf = t[1].replace('Event:','').lstrip() 735 args = rawf[:rawf.find('{')].strip().split(',') 736 name = args.pop(0) 737 delay = float(args.pop(-1)) 738 trigger = '' 739 for a in args: 740 trigger = trigger + a + ',' 741 trigger = trigger[:-1] 742 743 rawF = rawf[rawf.find('{')+1:rawf.find('}')].split('\n') 744 assignments = {} 745 for ass in rawF: 746 if len(ass.strip()) > 0: 747 ass = ass.split('=') 748 assignments.update({ass[0].strip() : ass[1].strip()}) 749 self.Events.update({name : { 'delay' : delay, 750 'name' : name, 751 'trigger' : trigger, 752 'assignments' : assignments, 753 'tsymb' : None 754 } 755 }) 756 self.Show('EventDec:',t[0]) 757 758 def p_objfuncdec(self, t): 759 '''ObjFuncDec : OBJFUNCDEC''' 760 rawf = t[1].replace('ObjectiveFunctions:','').lstrip() 761 args = rawf[:rawf.find('{')].strip().split(',') 762 rawf = [r.strip() for r in rawf[rawf.find('{')+1:rawf.find('}')].split('\n')] 763 objectives = {} 764 for oo in rawf: 765 print(rawf) 766 print(oo) 767 if len(oo.strip()) > 0: 768 oS = [t.strip() for t in oo.split(':')] 769 active = False 770 if oS[0] == args[0]: 771 active = True 772 objectives.update({oS[0] : {'id' : oS[0], 773 'active' : active, 774 'osense' : oS[1], 775 'fluxes' : oS[2]} 776 }) 777 self.cbm_ObjectiveFunctions.update(objectives) 778 self.Show('ObjFuncDec:',t[0]) 779 780 def p_fluxbnddec(self, t): 781 '''FluxBndDec : FLUXBNDDEC''' 782 rawf = t[1].replace('FluxBounds:','').lstrip() 783 rawf = [r.strip() for r in rawf[rawf.find('{')+1:rawf.find('}')].split('\n')] 784 785 fluxbnds = {} 786 cntr = 0 787 for oo in rawf: 788 if len(oo.strip()) > 0: 789 operator = None 790 lhs = None 791 rhs = None 792 fid = 'fbnd_%i' % cntr 793 for oper in ['>=','<=','>','<','=']: 794 if oper in oo: 795 bnd = oo.split(oper) 796 operator = oper 797 lhs = bnd[0].strip() 798 rhs = float(bnd[1].strip()) 799 break 800 fluxbnds.update({fid : {'id' : fid, 801 'flux' : lhs, 802 'rhs' : rhs, 803 'operator' : operator} 804 }) 805 cntr += 1 806 self.cbm_FluxBounds.update(fluxbnds) 807 self.Show('FluxBndDec:',t[0]) 808 809 def p_usercnstrdec(self, t): 810 '''UserCnstrDec : USERCNSTRDEC''' 811 rawf = t[1].replace('UserFluxConstraints:','').lstrip() 812 rawf = [r.strip() for r in rawf[rawf.find('{')+1:rawf.find('}')].split('\n')] 813 814 fluxcnstr = {} 815 for oo in rawf: 816 if len(oo.strip()) > 0: 817 oS = [t.strip() for t in oo.split(':')] 818 id = oS[0] 819 operator = None 820 lhs = None 821 rhs = None 822 for oper in ['>=','<=','>','<','=']: 823 if oper in oS[1]: 824 cnstr = oS[1].split(oper) 825 operator = oper 826 lhs = [c.strip() for c in cnstr[0].split(',')] 827 lhs2 = [] 828 for c2 in lhs: 829 c = c2.split(' ') 830 if len(c) == 1: 831 coeff = '+1' 832 val = c[0] 833 else: 834 coeff = c[0] 835 val = c[1] 836 lhs2.append((float(coeff),val)) 837 rhs = float(cnstr[1].strip()) 838 break 839 fluxcnstr.update({oS[0] : {'id' : oS[0], 840 'constraint' : tuple(lhs2), 841 'rhs' : rhs, 842 'operator' : operator} 843 }) 844 self.cbm_UserFluxConstraints.update(fluxcnstr) 845 self.Show('UserCnstrDec:',t[0]) 846 847 848 849 850 self.Show('UserCnstrDec:',t[0]) 851 852 853 854 855 856 def p_compartmentdec(self,t): 857 '''CompartmentDec : COMPDEC''' 858 rawf = t[1].strip().replace('Compartment:','') 859 strAr = [st.strip() for st in rawf.split(',')] 860 if len(strAr) <= 3: 861 name = strAr[0] 862 size = strAr[1] 863 dimensions = strAr[2] 864 if len(strAr) == 4: 865 area = strAr[3] 866 else: 867 area = None 868 869 self.compartments.update({name:{'name':name, 870 'size': float(size), 871 'dimensions' : int(dimensions), 872 'compartment': None, 873 'area' : None 874 ## 'volume' : None 875 }}) 876 877 def p_forcedfunc(self,t): 878 '''Forcedfunc : FORCEFUNC''' 879 self.ForceFunc.append(t[1]) 880 ar = t[1].split('=') 881 name = ar[0].replace('self.','').strip() 882 func = ar[1].replace('self.','').strip() 883 self.AssignmentRules.update({name : {'name' : name, 884 'formula' : func, 885 'type' : 'assignment' 886 } 887 }) 888 self.Show('Forcedfunc:',t[0]) 889 890 def p_timefunc(self,t): 891 '''Timefunc : TIMEFUNC''' 892 self.TimeFunc.append(t[1]) 893 self.Show('Timefunc:',t[0]) 894 895 def p_userfunc(self,t): 896 '''Userfunc : USERFUNC''' 897 self.UserFunc.append(t[1]) 898 ar = t[1].split('=') 899 name = ar[0].replace('self.','').strip() 900 func = ar[1].replace('self.','').strip() 901 self.UserFuncs.update({name : {'name' : name, 902 'formula' : func, 903 'type' : 'userfuncs'} 904 }) 905 self.Show('Userfunc:',t[0]) 906 907 def p_initfunc(self,t): 908 '''Initfunc : INITFUNC''' 909 self.InitFunc.append(t[1]) 910 ar = t[1].split('=') 911 name = ar[0].replace('self.','').strip() 912 value = ar[1].replace('self.','').replace('\'','').replace('"','').strip() 913 self.ModelInit.update({name : value}) 914 self.Show('Initfunc:',t[0]) 915 916 def p_fixedreagents(self,t): 917 '''FixList : NAME 918 | NAME FixList''' 919 if t[1] != None: 920 self.FixedReagents.append(t[1]) 921 t[0] = [t[1]] 922 try: 923 t[0] += t[2] 924 except: 925 pass 926 self.Show('FixList', t[0]) 927 928 # TODO: 929 def p_initialise(self,t): 930 '''Initialise : NAME EQUALS Expression''' 931 value = None 932 name = t[1].replace('self.','') 933 try: 934 value = eval(t[3]) 935 # 20090402 we need to keep track of species parameter initialisations and then 936 # perform a lookup or implement a proxy class that can store 937 # evaluated assignments 938 except Exception as ex: 939 print('Initialisation error: %s' % t[3]) 940 print(ex) 941 942 self.sDict.update({name : {'name' : name, 943 'initial' : value 944 } 945 }) 946 t[0] = t[1] + t[2] + t[3] 947 948 self.InitStrings.append(t[0]) 949 self.Inits.append(t[1]) 950 self.Show('Initialisation',t[0]) 951 952 def p_reaction_line(self,t): 953 '''ReactionLine : REACTION_ID ReactionEq 954 | REACTION_ID ReactionEq Expression''' 955 956 #global AllRateEqsGiven, self.ReacParams 957 ReacID = t[1] 958 if ReacID in self.nDict: 959 self.ParseErrors.append(('Duplicate Reaction ', t.lineno, ReacID, None)) 960 self.nDict[ReacID] = {} # Reaction dictionary for ReacID 961 ## self.nDict[ReacID]['Reagents'] = {} # Reagent dictionary within ReacID 962 self.nDict[ReacID].update({'Reagents' : {}, 'name' : ReacID}) 963 # a list of all reagents specified in the stoichiometry, unmodified and not taken into consideration for anything 964 self.nDict[ReacID].update({'AllReagents' : t[2][0]}) 965 966 # brett: if an index exists sum the coefficients instead of adding a new one 967 # this seems to deal with multiple definitions like X + X > Y and 2{X} + Y > Z + X 968 for i in t[2][0]: # First tuple member of ReactionEq contains list of (name,stoichcoef) 969 if i[0] in self.nDict[ReacID]['Reagents']: 970 self.nDict[ReacID]['Reagents'][i[0]] = self.nDict[ReacID]['Reagents'][i[0]] + i[1] 971 else: 972 self.nDict[ReacID]['Reagents'][i[0]] = i[1] # Key for reagent with stoichcoef value 973 killList = [] 974 if __USE_NET_STOICH__: 975 # brett: however for the case of X + Y > Y + Z where the sum of the coefficients 976 # is zero we can delete the key (Y) out of the reaction list altgether (hopefully!) 977 for i in self.nDict[ReacID]['Reagents']: 978 if abs(self.nDict[ReacID]['Reagents'][i]) < self.mach_spec_eps2k: 979 killList.append(i) 980 #print self.mach_spec_eps2k, self.nDict[ReacID]['Reagents'] 981 #print killList, self.nDict[ReacID]['Reagents'] 982 # brett: and the easiest way of doing this is putting the zero keys in a list 983 # and deleting them out of the dictionary 984 if len(killList) != 0: 985 for i in killList: 986 del self.nDict[ReacID]['Reagents'][i] 987 #print killList, self.nDict[ReacID]['Reagents'] 988 989 self.nDict[ReacID]['Type'] = t[2][1] # Second tuple member of ReactionEq contains type 990 try: # Save rate equation and create parameter list 991 self.nDict[ReacID]['RateEq'] = t[3] 992 self.nDict[ReacID]['Params'] = self.ReacParams 993 self.ReacParams = [] # Reset global self.ReacParams list 994 except: 995 self.nDict[ReacID]['RateEq'] = '' 996 self.nDict[ReacID]['Params'] = [] 997 self.AllRateEqsGiven = 0 # Set global flag to false 998 self.Show('ReactionLine',t[0]) 999 1000 def p_reaction_eq(self,t): 1001 '''ReactionEq : LeftHalfReaction EQUALS RightHalfReaction 1002 | LeftHalfReaction IRREV RightHalfReaction 1003 | POOL EQUALS RightHalfReaction 1004 | POOL IRREV RightHalfReaction 1005 | LeftHalfReaction EQUALS POOL 1006 | LeftHalfReaction IRREV POOL''' 1007 1008 ReacType = '' 1009 if t[2] == '=': 1010 ReacType = 'Rever' 1011 elif t[2] == '>': 1012 ReacType = 'Irrev' 1013 1014 # Yeah well you know the story ... 4 hrs of trying the "cool, other" way of doing it at work 1015 # and then solving the original problem in 90 mins with 14 lines of code at home ... 1016 # oh almost forgot: anonymous source and sink pools now work in PySCeS - brett 20050908 1017 if t[1] == '$pool': 1018 t[0] = (t[3], ReacType) 1019 elif t[3] == '$pool': 1020 t[0] = (t[1], ReacType) 1021 else: 1022 t[0] = (t[1] + t[3], ReacType) 1023 #print 'reaction_eq',t[0] 1024 self.Show('ReactionEq',t[0]) 1025 1026 def p_left_half_reaction(self,t): 1027 ''' LeftHalfReaction : SubstrateTerm 1028 | SubstrateTerm PLUS LeftHalfReaction''' 1029 # Make a list of substrate terms 1030 t[0] = [t[1]] 1031 try: 1032 t[0] += t[3] 1033 except: 1034 pass 1035 self.Show('LeftHalfReaction', t[0]) 1036 1037 def p_right_half_reaction(self,t): 1038 ''' RightHalfReaction : ProductTerm 1039 | ProductTerm PLUS RightHalfReaction''' 1040 1041 t[0] = [t[1]] 1042 1043 try: 1044 t[0] += t[3] 1045 except: 1046 pass 1047 self.Show('RightHalfReaction', t[0]) 1048 1049 def p_substrate_term(self,t): 1050 '''SubstrateTerm : STOICH_COEF NAME 1051 | NAME''' 1052 1053 # Make tuple of NAME and stoichiometric coefficient 1054 # (< 0 because substrate) 1055 try: 1056 t[0] = (t[2], -float(t[1])) 1057 if t[2] not in self.Reagents:# and t[2] != 'self.pool': 1058 self.Reagents.append(t[2]) 1059 except: 1060 t[0] = (t[1], -1.0) 1061 if t[1] not in self.Reagents:# and t[1] != 'self.pool': 1062 self.Reagents.append(t[1]) 1063 self.Show('SubstrateTerm', t[0]) 1064 1065 def p_product_term(self,t): 1066 '''ProductTerm : STOICH_COEF NAME 1067 | NAME''' 1068 # Make tuple of NAME and stoichiometric coefficient 1069 # (> 0 because product) 1070 try: 1071 t[0] = (t[2], float(t[1])) 1072 if t[2] not in self.Reagents:# and t[2] != 'self.pool': 1073 self.Reagents.append(t[2]) 1074 except: 1075 t[0] = (t[1], 1.0) 1076 if t[1] not in self.Reagents:# and t[1] != 'self.pool': 1077 self.Reagents.append(t[1]) 1078 self.Show('ProductTerm', t[0]) 1079 1080 def p_rate_eq(self,t): 1081 '''Expression : Expression PLUS Expression 1082 | Expression MINUS Expression 1083 | Expression TIMES Expression 1084 | Expression DIVIDE Expression 1085 | Power 1086 | Number 1087 | Func''' 1088 # |UMINUS : add if the 1089 # alternative for p_uminus is used 1090 1091 if len(t.slice)==4: 1092 t[0] = t[1] + t[2] + t[3] 1093 else: 1094 t[0] = t[1] 1095 1096 def p_power(self,t): 1097 '''Power : Expression POWER Expression''' 1098 1099 t[0] = 'pow('+ t[1] + ',' + t[3] + ')' #changed to make it DeriVar compatible 1100 ## t[0] = t[1] + '**' + t[3] 1101 1102 def p_uminus(self,t): 1103 '''Expression : MINUS Expression %prec UMINUS''' 1104 # Alternative '''UMINUS : MINUS Expression''' 1105 1106 t[0] = t[1] + t[2] 1107 1108 def p_number(self,t): 1109 '''Number : REAL 1110 | INT 1111 | NAME''' 1112 1113 # Build list of entities 1114 ttype = 'param' 1115 try: 1116 tx = float(t[1]) 1117 ttype = 'float' 1118 except ValueError as v: 1119 pass 1120 ## try: 1121 ## t[1] = complex(t[1]) 1122 ## ttype = 'complex' 1123 ## except: 1124 ## pass 1125 if ttype in ['complex','float']: 1126 t[1] = str(tx) 1127 else: 1128 # if this is not a number add it as a parameter EXCEPT if it is a function's 1129 # name or numpy.constant 1130 if (t[1] not in self.ReacParams) and\ 1131 (t[1].replace('numpy.','').replace('math.','').replace('operator.','') not in self.MathmlToNumpy_funcs) and\ 1132 (t[1].replace('numpy.','').replace('math.','').replace('operator.','') not in self.MathmlToNumpy_symb): # ignore function names and duplications 1133 self.ReacParams.append(t[1]) 1134 t[0] = t[1] 1135 1136 # new Core2 based InfixParser code 1137 def p_function(self,t): 1138 '''Func : LPAREN ArgList RPAREN 1139 | NAME LPAREN ArgList RPAREN''' 1140 1141 # convert root(degree,<expr>) to pow(<expr>, 1/degree) 1142 if t[1].strip() == 'root': 1143 t[1] = self.MathmlToNumpy_funcs[t[1]] 1144 t[3] = '%s, %s' % (t[3][t[3].index(',')+1:], 1.0/float(t[3][:t[3].index(',')]) ) 1145 t[0] = t[1] + t[2] + t[3] + t[4] 1146 elif t[1] in self.MathmlToNumpy_funcs: 1147 if self.MathmlToNumpy_funcs[t[1]] == None: 1148 self.SymbolErrors.append(t[1]) 1149 print('\nFunction \"%s\" not supported by PySCeS' % t[1]) 1150 t[0] = 'unknown_function_'+t[1] + t[2] + t[3] + t[4] 1151 else: 1152 try: 1153 t[0] = self.MathmlToNumpy_funcs[t[1]] + t[2] + t[3] + t[4] 1154 except Exception as EX: 1155 print('Function Parse error 1 (please report!)\n', EX) 1156 self.ModelUsesNumpyFuncs = True 1157 # differentiate between bracketed functions and expressions: 1158 # func( S1 ) and ( S/S05 ) 1159 elif len(t) == 4: 1160 t[0] = t[1] + t[2] + t[3] 1161 else: 1162 # assume some arbitrary function definition 1163 if t[1][:6] == 'numpy.' or t[1][:5] == 'math.' or t[1][:9] == 'operator.': # NEW UNTESTED 1164 t[0] = t[1] + t[2] + t[3] + t[4] 1165 else: 1166 t[0] = t[1] + t[2] + t[3] + t[4] 1167 1168 # arbitrary number of arguments in an argument list 1169 # adapted from Andrew Dalke's GardenSnake 1170 # http://www.dalkescientific.com/writings/diary/GardenSnake.py 1171 def p_arglist(self,t): 1172 '''ArgList : Expression 1173 | ArgList COMMA Expression''' 1174 try: 1175 if len(t) == 2: 1176 t[0] = t[1] 1177 elif len(t) == 4: 1178 t[0] = t[1] + ',' + t[3] 1179 else: 1180 pass 1181 except Exception as EX: 1182 print('Function ArgList error (please report!)\n', EX) 1183 1184################################################################### 1185################################################################### 1186 1187 def ParsePSC(self,modelfile,modeldir,modeloutput): 1188 """ 1189 ParsePSC(modelfile,modeldir,modeloutput) 1190 1191 Parse the .psc file into a Network Dictionary and associated property arrays 1192 1193 modelfile: PSC filename 1194 modeldir: PSC input directory 1195 modeloutput: working directory for lex/parse temporary files 1196 1197 """ 1198 # we clear stuff so we have a shiny new instance 1199 self.nDict = {} # Dictionary containing all reaction information 1200 self.InDict = {} 1201 self.sDict = {} 1202 self.pDict = {} #parameter dict 1203 self.uDict = {'substance': {'exponent': 1, 'multiplier': 1.0, 'scale': 0, 'kind': 'mole'}, 1204 'volume': {'exponent': 1, 'multiplier': 1.0, 'scale': 0, 'kind': 'litre'}, 1205 'time': {'exponent': 1, 'multiplier': 1.0, 'scale': 0, 'kind': 'second'}, 1206 'length': {'exponent': 1, 'multiplier': 1.0, 'scale': 0, 'kind': 'metre'}, 1207 'area': {'exponent': 2, 'multiplier': 1.0, 'scale': 0, 'kind': 'metre'} 1208 } 1209 self.KeyWords = {'Modelname' : None, 1210 'Description' : None, 1211 'Species_In_Conc' : None, 1212 'Output_In_Conc' : None, 1213 'ModelType' : None 1214 } 1215 self.compartments = {} 1216 self.ReactionIDs = [] # List of reaction names 1217 self.InitStrings = [] # Initialisation strings 1218 self.species = [] # Variable reagents that occur in reactions 1219 self.FixedReagents = [] # Fixed reagents 1220 self.InitParams = [] # Initialised parameters 1221 self.Names = [] # List of all reagent, parameter and function names 1222 self.Inits = [] # Initialised entities 1223 self.Reagents = [] # All reagents found during parsing of reactions 1224 self.ReacParams = [] # Temporary list of reaction parameters 1225 self.LexErrors = [] # List of lexing errors 1226 self.ParseErrors = [] 1227 self.SymbolErrors = [] 1228 self.ForceFunc = [] 1229 self.TimeFunc = [] 1230 self.UserFunc = [] 1231 self.InitFunc = [] 1232 self.Functions = {} 1233 self.Events = {} 1234 self.AssignmentRules = {} 1235 self.UserFuncs = {} 1236 self.ModelInit = {} 1237 self.cbm_FluxBounds = {} # flux bounds in a CB model 1238 self.cbm_ObjectiveFunctions = {} # objective functions for a CB model 1239 self.cbm_UserFluxConstraints = {} # objective functions for a CB model 1240 1241 self.ModelUsesNumpyFuncs = False 1242 1243 # 4 hrs of debugging and these two lines solve the problem .... I'm out of here! 1244 reload(pysces.lib.lex) 1245 reload(pysces.lib.yacc) 1246 # fixes the obscure reload <--> garbage collection reference overload bug ... who said scripted lang's were 1247 # easy to use :-) - brett 20040122 1248 1249 self.AllRateEqsGiven = 1 1250 self.ParseOK = True 1251 self.LexOK = True 1252 1253 # check to see if the last line of the file is an os defined empty line 1254 self.CheckLastLine(os.path.join(modeldir,modelfile)) 1255 1256 # load model data from file 1257 Data = open(os.path.join(modeldir,modelfile),'r') 1258 self.__Model = Data.read() 1259 Data.close() 1260 1261 # try and find a temporary workspace or use ModelOutput 1262 if 'TMP' in os.environ: 1263 tempDir = os.environ['TMP'] 1264 elif 'TEMP' in os.environ: 1265 tempDir = os.environ['TEMP'] 1266 elif os.path.isdir(modeloutput): 1267 tempDir = modeloutput 1268 else: 1269 tempDir = os.getcwd() 1270 1271 os.chdir(tempDir) 1272 1273 # fix filenames for intermediary files - brett 1274 if not modelfile[:-4].isalnum(): 1275 FileL = list(modelfile) 1276 FileT = '' 1277 for let in FileL: 1278 if let.isalnum(): 1279 FileT += let 1280 self.debugfile = '_pys' + FileT[:-3] + ".dbg" 1281 self.tabmodule = '_pys' + FileT[:-3] + "_" + "parsetab" 1282 else: 1283 self.debugfile = '_pys' + modelfile[:-4] + ".dbg" 1284 self.tabmodule = '_pys' + modelfile[:-4] + "_" + "parsetab" 1285 1286 if self.display_debug: 1287 print(self.tabmodule) 1288 print(self.debugfile) 1289 print('cwd: ', os.getcwd()) 1290 1291 self.__lexer = pysces.lib.lex.lex(module=self, debug=self.display_debug) 1292 self.__lexer.input(self.__Model) 1293 self.__parser = pysces.lib.yacc.yacc(module=self, 1294 debug=self.display_debug, 1295 debugfile=self.debugfile, 1296 tabmodule=self.tabmodule, 1297 outputdir=tempDir) 1298 1299 while 1: 1300 tok = self.__lexer.token() 1301 if not tok: break 1302 1303 while 1: 1304 p = self.__parser.parse(self.__Model) 1305 if not p: break 1306 1307 # get to our work directory 1308 os.chdir(modeloutput) 1309 1310 # we have the dictionary get rid of this stuff 1311 del self.__Model, self.__lexer, self.__parser, p 1312 1313 # Create forcing function code blocks - brett 20050621 1314 Fstr = '' 1315 Tstr = '' 1316 Ustr = '' 1317 Istr = '' 1318 for f in self.ForceFunc: 1319 if os.sys.platform != 'win32' and f[-2] == '\r': 1320 Fstr = Fstr + f[:-2] + '\n' 1321 else: 1322 Fstr += f 1323 for t in self.TimeFunc: 1324 if os.sys.platform != 'win32' and t[-2] == '\r': 1325 Tstr = Tstr + t[:-2] + '\n' 1326 else: 1327 Tstr += t 1328 for u in self.UserFunc: 1329 if os.sys.platform != 'win32' and u[-2] == '\r': 1330 Ustr = Ustr + u[:-2] + '\n' 1331 else: 1332 Ustr += u 1333 for i in self.InitFunc: 1334 if os.sys.platform != 'win32' and i[-2] == '\r': 1335 Istr = Istr + i[:-2] + '\n' 1336 else: 1337 Istr += i 1338 1339 self.ForceFunc = Fstr 1340 self.TimeFunc = Tstr 1341 self.UserFunc = Ustr 1342 self.InitFunc = Istr 1343 del Fstr,Tstr,Ustr,Istr 1344 1345 # Create list of variable species 1346 for reag in self.Reagents: 1347 if reag not in self.FixedReagents: 1348 self.species.append(reag) 1349 1350 # Warn if no reagents have been fixed 1351 if self.FixedReagents == []: 1352 print('Info: No reagents have been fixed') 1353 else: # Warn if a fixed reagent does not occur in a reaction equation 1354 for reag in self.FixedReagents: 1355 if reag not in self.Reagents: 1356 print('Warning: species "%s" (fixed) does not occur in any reaction' % reag.replace('self.','')) 1357 1358 # for the initialised ones 1359 for s in list(self.sDict.keys()): 1360 if 'self.'+s in self.FixedReagents+self.Reagents: 1361 fixed = False 1362 compartment = None 1363 isamount = False 1364 initial = self.sDict[s]['initial'] 1365 name = self.sDict[s]['name'] 1366 1367 if 'self.'+s in self.FixedReagents: 1368 fixed = True 1369 if s in list(self.InDict.keys()): 1370 compartment = self.InDict[s] 1371 self.sDict.update({s : {'name' : name, 1372 'initial' : initial, 1373 'compartment' : compartment, 1374 'fixed' : fixed, 1375 'isamount' : isamount 1376 }}) 1377 else: 1378 self.pDict.update({s : self.sDict.pop(s)}) 1379 1380 1381 # for the uninitialised ones 1382 ## print self.FixedReagents, self.Reagents, self.Inits 1383 for s in self.species+self.FixedReagents: 1384 if s.replace('self.','') not in self.sDict: 1385 fixed = False 1386 compartment = None 1387 isamount = False 1388 initial = None 1389 name = s.replace('self.','') 1390 1391 if s in self.FixedReagents: 1392 fixed = True 1393 if name in list(self.InDict.keys()): 1394 compartment = self.InDict[name] 1395 self.sDict.update({name : {'name' : name, 1396 'initial' : initial, 1397 'compartment' : compartment, 1398 'fixed' : fixed, 1399 'isamount' : isamount 1400 }}) 1401 1402 1403 # Create list of initialised parameters 1404 # eventually this must be switched over to pDict 1405 for elem in self.Inits: 1406 el = elem.replace('self.', '') 1407 names = [self.sDict[s]['name'] for s in self.sDict] 1408 if el not in names: 1409 self.InitParams.append(elem) 1410 if el in names and self.sDict[el]['fixed'] == True: 1411 self.InitParams.append(elem) 1412 1413 # In self.nDict, clean rate equation pameter list of variables that occur in that reaction 1414 for id in list(self.nDict.keys()): 1415 # create 'Modifiers' key for each reaction - brett 20050606 1416 self.nDict[id].update({'Modifiers':[]}) 1417 reagcomp = None 1418 if id in list(self.InDict.keys()): 1419 reagcomp = self.InDict[id] 1420 self.nDict[id].update({'compartment' : reagcomp}) 1421 for reag in self.species: 1422 if reag in self.nDict[id]['Params']: 1423 # if the reagent is a reaction reagent delete - brett 20050606 1424 if reag in self.nDict[id]['Reagents']: 1425 self.nDict[id]['Params'].remove(reag) 1426 # if not it is a modifier ... add to modifiers and delete - brett 20050606 1427 else: 1428 #print 'INFO: variable modifier \"' + reag.replace('self.','') + '\" detected in', id 1429 self.nDict[id]['Modifiers'].append(reag) 1430 self.nDict[id]['Params'].remove(reag) 1431 1432 # Check whether all parameters have been initialised and if not add to dictionary 1433 for id in list(self.nDict.keys()): 1434 cnames = [self.compartments[s]['name'] for s in self.compartments] 1435 for param in self.nDict[id]['Params']: 1436 ## print param 1437 ## print self.InitParams 1438 if param not in self.InitParams and param.replace('self.','') not in cnames: 1439 ## print param, self.InitParams, self.pDict 1440 print('Warning: %s parameter "%s" has not been initialised' % (id, param.replace('self.',''))) 1441 pname = param.replace('self.','') 1442 self.pDict.update({pname : {'name':pname, 'initial':None}}) 1443 self.InitParams.append(param) 1444 1445 # Check whether all variable reagents have been initialised 1446 for reag in self.species+self.FixedReagents: 1447 if reag not in self.Inits and reag in self.species: 1448 print('Warning: species "%s" has not been initialised' % reag.replace('self.','')) 1449 if reag not in self.Inits and reag in self.FixedReagents: 1450 print('Warning: species "%s" (fixed) has not been initialised' % reag.replace('self.','')) 1451 1452 # Check that all initialised parameters actually occur in self.Inits 1453 known = 0 1454 for param in self.InitParams: 1455 for id in list(self.nDict.keys()): 1456 if param in self.nDict[id]['Params']: 1457 known = 1 1458 break 1459 else: 1460 known = 0 1461 if not known: print('Info: "%s" has been initialised but does not occur in a rate equation' % param.replace('self.','')) 1462 1463 # find modifiers for each reaction - brett 20050606 1464 self.modifiers = [] 1465 for i in self.nDict: 1466 for j in self.nDict[i]['Params']: 1467 if j in self.FixedReagents and j not in self.nDict[i]['Reagents']: 1468 self.nDict[i]['Modifiers'].append(j) 1469 self.modifiers.append((i,tuple([j.replace('self.','') for j in self.nDict[i]['Modifiers']]))) 1470 1471 self.fixed_species = copy.copy(self.FixedReagents) 1472 self.parameters = copy.copy(self.InitParams) 1473 self.reactions = copy.copy(self.ReactionIDs) 1474 1475 for x in range(len(self.fixed_species)): 1476 self.fixed_species[x] = self.fixed_species[x].replace('self.','') 1477 for x in range(len(self.species)): 1478 self.species[x] = self.species[x].replace('self.','') 1479 for x in range(len(self.parameters)): 1480 self.parameters[x] = self.parameters[x].replace('self.','') 1481 1482 if self.display_debug == 1: 1483 print(self.fixed_species) 1484 print(self.species) 1485 print(self.parameters) 1486 print(self.reactions) 1487 1488 if self.display_debug == 1: 1489 print('\nDebugging Part 1') 1490 print('\nFixedReagents') 1491 print(self.FixedReagents) 1492 print('\nInitStrings') 1493 print(self.InitStrings) 1494 print('\nInitParams') 1495 print(self.InitParams) 1496 print('\nReactionIDs') 1497 print(self.ReactionIDs) 1498 print('\nspecies') 1499 print(self.species) 1500 print('\nNetworkDict') 1501 print(self.nDict) 1502 print('\n\n') 1503 1504 def KeywordCheck(self,inputarr,bad=[]): 1505 """ 1506 KeywordCheck(inputarr,bad=[]) 1507 1508 Check a list of names for reserved PySCeS keywords 1509 1510 Arguments: 1511 ========= 1512 inputarr: a list of names to check 1513 bad [default=[]]: a list of illegal keywords (new if not supplied 1514 1515 """ 1516 for x in inputarr: 1517 if x.replace('self.','') in self.ReservedTerms: 1518 bad.append(x.replace('self.','')) 1519 print(x.replace('self.',''), '\t\tERROR: keyword') 1520 return bad 1521 1522 def CheckLastLine(self,name): 1523 """ 1524 CheckLastLine(name) 1525 1526 Checks to see if file ends with an empty line ... if not it adds one 1527 1528 Arguments: 1529 ========= 1530 name: filename of the PySCeS input file 1531 1532 """ 1533 F = open(name,'r+') 1534 #F.seek(-1,2) 1535 F.seek(0, os.SEEK_END) 1536 F.seek(F.tell() - 1, os.SEEK_SET) 1537 if F.read() != '\n': 1538 if os.sys.platform == 'win32': 1539 F.read() 1540 try: 1541 F.write('\n') 1542 except Exception as e: 1543 print('EOL add error', e) 1544 print('Adding \\n to input file') 1545 F.close() 1546 1547## # you REALLY do NOT want to know what this was for! - brett 1548## if gc.isenabled() == 1: 1549## gc.collect() 1550## del gc.garbage[:] 1551## gc.set_debug(0) 1552## print 'gc debug level = ' + `gc.get_debug()` 1553 1554