1""" 2PySCeS - Python Simulator for Cellular Systems (http://pysces.sourceforge.net) 3 4Copyright (C) 2004-2022 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 21""" 22TODO: Parameter elasticities wrt the compartments 23""" 24 25from pysces.version import __version__ 26 27__doc__ = ''' 28 PyscesModel 29 ----------- 30 31 This module contains the core PySCeS classes which 32 create the model and associated data objects 33 34 ''' 35import os, copy, gc, time 36import math, operator, re 37import pprint, pickle, io 38import warnings 39 40try: 41 input = raw_input # Py2 compatibility 42except NameError: 43 pass 44import numpy 45import scipy 46import scipy.linalg 47import scipy.integrate 48 49ScipyDerivative = None 50HAVE_SCIPY_DERIV = False 51try: 52 ScipyDerivative = scipy.derivative 53 HAVE_SCIPY_DERIV = True 54except AttributeError: 55 try: 56 import scipy.misc 57 58 ScipyDerivative = scipy.misc.derivative 59 HAVE_SCIPY_DERIV = True 60 except ImportError as AttributeError: 61 pass 62if not HAVE_SCIPY_DERIV: 63 raise RuntimeError('\nSciPy derivative function not available') 64 65from getpass import getuser 66 67from . import model_dir as MODEL_DIR 68from . import output_dir as OUTPUT_DIR 69from . import install_dir as INSTALL_DIR 70from . import PyscesRandom as random 71from .PyscesScan import Scanner 72from .core2.InfixParser import MyInfixParser 73 74from . import ( 75 nleq2, 76 nleq2_switch, 77 pitcon, 78 plt, 79 gplt, 80 PyscesStoich, 81 PyscesParse, 82 __SILENT_START__, 83 __CHGDIR_ON_START__, 84 SED, 85) 86 87if __CHGDIR_ON_START__: 88 CWD = OUTPUT_DIR 89else: 90 CWD = os.getcwd() 91 92interface = None 93 94# this is incredibly crude but effectively masks off unsupported random functions 95del ( 96 random.setstate, 97 random.getstate, 98) # random.division, 99del random.randrange, random.Random, random.choice 100del random.sample, random.shuffle, random.jumpahead 101del random.SystemRandom, random.WichmannHill, random.triangular 102# used by functions random.NV_MAGICCONST, random.SG_MAGICCONST, random.BPF, random.RECIP_BPF 103 104 105# Scipy version check 106if ( 107 int(scipy.__version__.split('.')[0]) < 1 108 and int(scipy.__version__.split('.')[1]) < 6 109): 110 print( 111 '\nINFO: Your version of SciPy (' 112 + scipy.version.version 113 + ') might be too old\n\tVersion 0.6.x or newer is strongly recommended\n' 114 ) 115else: 116 if not __SILENT_START__: 117 print( 118 'You are using NumPy ({}) with SciPy ({})'.format( 119 numpy.__version__, scipy.__version__ 120 ) 121 ) 122 123_HAVE_ASSIMULO = False 124_ASSIMULO_LOAD_ERROR = '' 125 126try: 127 with warnings.catch_warnings(): 128 warnings.filterwarnings("ignore", category=numpy.VisibleDeprecationWarning) 129 from assimulo.solvers import CVode 130 from assimulo.problem import Explicit_Problem 131 _HAVE_ASSIMULO = True 132 if not __SILENT_START__: 133 print('Assimulo CVode available') 134 135except Exception as ex: 136 _ASSIMULO_LOAD_ERROR = '{}'.format(ex) 137 _HAVE_ASSIMULO = False 138 139if _HAVE_ASSIMULO: 140 class EventsProblem(Explicit_Problem): 141 def __init__(self, mod, **kwargs): 142 Explicit_Problem.__init__(self, **kwargs) 143 self.mod = mod 144 self.name = self.mod.__KeyWords__['Modelname'] 145 # needed to track changes in parameters during events for REq evaluation 146 self.parvals = [] 147 self.parvals.append([getattr(self.mod, p) for p in self.mod.parameters]) 148 self.event_times = [] 149 150 def state_events(self, t, y, sw): 151 self.events = self.mod.__events__ 152 eout = numpy.zeros(len(self.events)) 153 self.mod._TIME_ = t 154 for ev in range(len(self.events)): 155 if self.events[ev](t): 156 eout[ev] = 0 157 else: 158 eout[ev] = 1 159 if self.mod.__HAS_RATE_RULES__: 160 exec(self.mod.__CODE_raterule) 161 return eout 162 163 def handle_event(self, solver, event_info): 164 self.event_times.append(solver.t) 165 state_info = event_info[0] 166 idx = state_info.index(-1) 167 ev = self.events[idx] 168 if ev._assign_now: 169 for ass in ev.assignments: 170 if ass.variable in self.mod.L0matrix.getLabels()[1] or ( 171 self.mod.mode_integrate_all_odes 172 and ass.variable in self.mod.__species__ 173 ): 174 assVal = ass.getValue() 175 assIdx = self.mod.__species__.index(ass.variable) 176 if self.mod.__KeyWords__["Species_In_Conc"]: 177 solver.y[assIdx] = assVal * getattr( 178 self.mod, self.mod.__CsizeAllIdx__[assIdx] 179 ) 180 else: 181 solver.y[assIdx] = assVal 182 elif ( 183 not self.mod.mode_integrate_all_odes 184 and ass.variable in self.mod.L0matrix.getLabels()[0] 185 ): 186 print( 187 'Event assignment to dependent species consider setting "mod.mode_integrate_all_odes = True"' 188 ) 189 elif ( 190 self.mod.__HAS_RATE_RULES__ and ass.variable in self.mod.__rate_rules__ 191 ): 192 assVal = ass.getValue() 193 rrIdx = self.mod.__rate_rules__.index(ass.variable) 194 self.mod.__rrule__[rrIdx] = assVal 195 solver.y[self.mod.L0matrix.shape[1] + rrIdx] = assVal 196 setattr(self.mod, ass.variable, assVal) 197 else: 198 ass() 199 # track any parameter changes 200 self.parvals.append([getattr(self.mod, p) for p in self.mod.parameters]) 201 202 203# for future fun 204_HAVE_VPYTHON = False 205_VPYTHON_LOAD_ERROR = '' 206__psyco_active__ = 0 207''' 208try: 209 import visual 210 _HAVE_VPYTHON = True 211 vpyscene = visual.display(x=150, y=50, title='PySCeS '+__version__+' - C Brett G. Olivier, Stellenbosch 2022', width=640, height=480,\ 212 center=(0,0,0), background=(0,0,0)) 213 vpyscene.select() 214 # vpyscene.autocenter = True 215 l1 = visual.label(pos=(0,0,0), text="Welcome to the PySCeS Visualisation Terminal", color=visual.color.red, box=0) 216 l2 = visual.label(pos=(0,0.5,0.5), text="It just works!", color=visual.color.green, box=0) 217except Exception, ex: 218 _VPYTHON_LOAD_ERROR = '%s' % ex 219 _HAVE_VPYTHON = False 220try: 221 import psyco 222 psyco.profile() 223 __psyco_active__ = 1 224 print('PySCeS Model module is now PsycoActive!') 225except: 226 __psyco_active__ = 0 227''' 228# machine specs 229mach_spec = numpy.MachAr() 230# grab a parser 231pscParser = PyscesParse.PySCeSParser(debug=0) 232 233 234def chkpsc(File): 235 """ 236 chkpsc(File) 237 238 Chekc whether the filename "File" has a '.psc' extension and adds one if not. 239 240 Arguments: 241 242 File: filename string 243 244 """ 245 try: 246 if File[-4:] == '.psc': 247 pass 248 else: 249 print('Assuming extension is .psc') 250 File += '.psc' 251 except: 252 print('Chkpsc error') 253 return File 254 255 256def chkmdir(): 257 """ 258 chkmdir() 259 260 Import and grab pysces.model_dir 261 262 Arguments: 263 None 264 265 """ 266 ## from pysces import model_dir as MODEL_DIR 267 pass 268 269 270class BagOfStuff(object): 271 """ 272 A collection of attributes defined by row and column lists 273 used by Response coefficients etc matrix is an array of values 274 while row/col are lists of row colummn name strings 275 """ 276 277 matrix = None 278 row = None 279 col = None 280 281 def __init__(self, matrix, row, col): 282 "Load attributes from arrays" 283 assert len(row) == matrix.shape[0], "\nRow dimension mismatch" 284 assert len(col) == matrix.shape[1], "\nCol dimension mismatch" 285 self.matrix = matrix 286 self.row = list(row) 287 self.col = list(col) 288 289 def load(self): 290 for r in range(self.matrix.shape[0]): 291 for c in range(self.matrix.shape[1]): 292 setattr( 293 self, str(self.row[r]) + '_' + str(self.col[c]), self.matrix[r, c] 294 ) 295 296 def get(self, attr1, attr2): 297 "Returns a single attribute \"attr1_attr2\" or None" 298 try: 299 return getattr(self, attr1 + '_' + attr2) 300 except Exception as ex: 301 print(ex) 302 return None 303 304 def select(self, attr, search='a'): 305 """Return a dictionary of <attr>_<name>, <name>_<attr> : val or {} if none 306 If attr exists as an index for both left and right attr then: 307 search='a' : both left and right attributes (default) 308 search='l' : left attributes only 309 search='r' : right attributes""" 310 output = {} 311 312 if attr in self.row and attr in self.col: 313 if search in ['a', 'l']: 314 row = self.row.index(attr) 315 for col in range(self.matrix.shape[1]): 316 output.setdefault(attr + '_' + self.col[col], self.matrix[row, col]) 317 if search in ['a', 'r']: 318 col = self.col.index(attr) 319 for row in range(self.matrix.shape[0]): 320 output.setdefault(self.row[row] + '_' + attr, self.matrix[row, col]) 321 elif attr in self.row: 322 row = self.row.index(attr) 323 for col in range(self.matrix.shape[1]): 324 output.setdefault(attr + '_' + self.col[col], self.matrix[row, col]) 325 elif attr in self.col: 326 col = self.col.index(attr) 327 for row in range(self.matrix.shape[0]): 328 output.setdefault(self.row[row] + '_' + attr, self.matrix[row, col]) 329 return output 330 331 def list(self): 332 """ 333 Return all attributes as a attr:val dictionary 334 335 """ 336 output = {} 337 for row in range(self.matrix.shape[0]): 338 for col in range(self.matrix.shape[1]): 339 output.setdefault( 340 self.row[row] + '_' + self.col[col], self.matrix[row, col] 341 ) 342 return output 343 344 def listAllOrdered(self, order='descending', absolute=True): 345 """ 346 Return an ordered list of (attr, value) tuples 347 348 - *order* [default='descending'] the order to return as: 'descending' or 'ascending' 349 - *absolute* [default=True] use the absolute value 350 351 """ 352 if order != 'ascending': 353 order = True 354 else: 355 order = False 356 357 output_v = [] 358 output_n = [] 359 for row in range(self.matrix.shape[0]): 360 for col in range(self.matrix.shape[1]): 361 output_v.append(self.matrix[row, col]) 362 output_n.append(self.row[row] + '_' + self.col[col]) 363 if absolute: 364 new_idx = numpy.argsort([abs(v) for v in output_v]) 365 else: 366 new_idx = numpy.argsort(output_v) 367 output = [] 368 if order: 369 new_idx = new_idx.tolist() 370 new_idx.reverse() 371 for i_ in new_idx: 372 output.append((output_n[i_], output_v[i_])) 373 return output 374 375 376class ScanDataObj(object): 377 """ 378 New class used to store parameter scan data (uses StateDataObj) 379 """ 380 381 species = None 382 fluxes = None 383 rules = None 384 xdata = None 385 mod_data = None 386 flux_labels = None 387 species_labels = None 388 rules_labels = None 389 xdata_labels = None 390 mod_data_labels = None 391 invalid_states = None 392 parameters = None 393 parameter_labels = None 394 HAS_FLUXES = False 395 HAS_SPECIES = False 396 HAS_RULES = False 397 HAS_XDATA = False 398 HAS_MOD_DATA = False 399 HAS_SET_LABELS = False 400 ALL_VALID = True 401 OPEN = True 402 403 def __init__(self, par_label): 404 self.species = [] 405 self.fluxes = [] 406 self.rules = [] 407 self.xdata = [] 408 self.mod_data = [] 409 self.invalid_states = [] 410 self.parameters = [] 411 if isinstance(par_label, list): 412 self.parameter_labels = par_label 413 else: 414 self.parameter_labels = [par_label] 415 416 def setLabels(self, ssdata): 417 if ssdata.HAS_SPECIES: 418 self.species_labels = ssdata.species_labels 419 self.HAS_SPECIES = True 420 if ssdata.HAS_FLUXES: 421 self.flux_labels = ssdata.flux_labels 422 self.HAS_FLUXES = True 423 if ssdata.HAS_RULES: 424 self.rules_labels = ssdata.rules_labels 425 self.HAS_RULES = True 426 if ssdata.HAS_XDATA: 427 self.xdata_labels = ssdata.xdata_labels 428 self.HAS_XDATA = True 429 self.HAS_SET_LABELS = True 430 431 def addPoint(self, ipar, ssdata): 432 """takes a list/array of input parameter values and the associated ssdata object""" 433 assert self.OPEN, '\nScan has been finalised no new data may be added' 434 if not self.HAS_SET_LABELS: 435 self.setLabels(ssdata) 436 437 if hasattr(ipar, '__iter__'): 438 self.parameters.append(ipar) 439 else: 440 self.parameters.append([ipar]) 441 if self.HAS_SPECIES: 442 self.species.append(ssdata.getSpecies()) 443 if self.HAS_FLUXES: 444 self.fluxes.append(ssdata.getFluxes()) 445 if self.HAS_RULES: 446 self.rules.append(ssdata.getRules()) 447 if self.HAS_XDATA: 448 self.xdata.append(ssdata.getXData()) 449 if not ssdata.IS_VALID: 450 self.ALL_VALID = False 451 self.invalid_states.append(ipar) 452 453 def addModData(self, mod, *args): 454 if not self.HAS_MOD_DATA: 455 self.HAS_MOD_DATA = True 456 self.mod_data_labels = [attr for attr in args if hasattr(mod, attr)] 457 self.mod_data.append( 458 tuple([getattr(mod, attr) for attr in self.mod_data_labels]) 459 ) 460 461 def closeScan(self): 462 print('\nINFO: closing scan no new data may be added.') 463 if self.HAS_SPECIES: 464 self.species = numpy.array(self.species) 465 if self.HAS_FLUXES: 466 self.fluxes = numpy.array(self.fluxes) 467 if self.HAS_RULES: 468 self.rules = numpy.array(self.rules) 469 if self.HAS_XDATA: 470 self.xdata = numpy.array(self.xdata) 471 if self.HAS_MOD_DATA: 472 self.mod_data = numpy.array(self.mod_data) 473 self.parameters = numpy.array(self.parameters) 474 self.OPEN = False 475 476 def getSpecies(self, lbls=False): 477 if self.OPEN: 478 self.closeScan() 479 output = None 480 labels = None 481 if self.HAS_SPECIES: 482 output = numpy.hstack((self.parameters, self.species)) 483 labels = self.parameter_labels + self.species_labels 484 if lbls: 485 return output, labels 486 else: 487 return output 488 489 def getFluxes(self, lbls=False): 490 if self.OPEN: 491 self.closeScan() 492 output = None 493 labels = None 494 if self.HAS_FLUXES: 495 output = numpy.hstack((self.parameters, self.fluxes)) 496 labels = self.parameter_labels + self.flux_labels 497 if lbls: 498 return output, labels 499 else: 500 return output 501 502 def getRules(self, lbls=False): 503 if self.OPEN: 504 self.closeScan() 505 output = None 506 labels = None 507 if self.HAS_RULES: 508 output = numpy.hstack((self.parameters, self.rules)) 509 labels = self.parameter_labels + self.rules_labels 510 if lbls: 511 return output, labels 512 else: 513 return output 514 515 def getXData(self, lbls=False): 516 if self.OPEN: 517 self.closeScan() 518 output = None 519 labels = None 520 if self.HAS_XDATA: 521 output = numpy.hstack((self.parameters, self.xdata)) 522 labels = self.parameter_labels + self.xdata_labels 523 if lbls: 524 return output, labels 525 else: 526 return output 527 528 def getModData(self, lbls=False): 529 if self.OPEN: 530 self.closeScan() 531 output = None 532 labels = None 533 if self.HAS_MOD_DATA: 534 output = numpy.hstack((self.parameters, self.mod_data)) 535 labels = self.parameter_labels + self.mod_data_labels 536 if lbls: 537 return output, labels 538 else: 539 return output 540 541 def getAllScanData(self, lbls=False): 542 if self.OPEN: 543 self.closeScan() 544 output = self.parameters 545 labels = self.parameter_labels 546 if self.HAS_SPECIES: 547 output = numpy.hstack((output, self.species)) 548 labels = labels + self.species_labels 549 if self.HAS_FLUXES: 550 output = numpy.hstack((output, self.fluxes)) 551 labels = labels + self.flux_labels 552 if self.HAS_RULES: 553 output = numpy.hstack((output, self.rules)) 554 labels = labels + self.rules_labels 555 if self.HAS_XDATA: 556 output = numpy.hstack((output, self.xdata)) 557 labels = labels + self.xdata_labels 558 if self.HAS_MOD_DATA: 559 output = numpy.hstack((output, self.mod_data)) 560 labels = labels + self.mod_data_labels 561 if lbls: 562 return output, labels 563 else: 564 return output 565 566 def getScanData(self, *args, **kwargs): 567 """ 568 getScanData(\*args) feed this method species/flux/rule/mod labels and it 569 will return an array of [parameter(s), sp1, f1, ....] 570 """ 571 if 'lbls' in kwargs: 572 lbls = kwargs['lbls'] 573 else: 574 lbls = False 575 576 output = self.parameters 577 lout = self.parameter_labels 578 for roc in args: 579 if self.HAS_SPECIES and roc in self.species_labels: 580 lout.append(roc) 581 output = numpy.hstack( 582 ( 583 output, 584 self.species.take([self.species_labels.index(roc)], axis=-1), 585 ) 586 ) 587 if self.HAS_FLUXES and roc in self.flux_labels: 588 lout.append(roc) 589 output = numpy.hstack( 590 (output, self.fluxes.take([self.flux_labels.index(roc)], axis=-1)) 591 ) 592 if self.HAS_RULES and roc in self.rules_labels: 593 lout.append(roc) 594 output = numpy.hstack( 595 (output, self.rules.take([self.rules_labels.index(roc)], axis=-1)) 596 ) 597 if self.HAS_XDATA and roc in self.xdata_labels: 598 lout.append(roc) 599 output = numpy.hstack( 600 (output, self.xdata.take([self.xdata_labels.index(roc)], axis=-1)) 601 ) 602 if self.HAS_MOD_DATA and roc in self.mod_data_labels: 603 lout.append(roc) 604 output = numpy.hstack( 605 ( 606 output, 607 self.mod_data.take([self.mod_data_labels.index(roc)], axis=-1), 608 ) 609 ) 610 if not lbls: 611 return output 612 else: 613 return output, lout 614 615 616class StateDataObj(object): 617 """ 618 New class used to store steady-state data. 619 """ 620 621 fluxes = None 622 species = None 623 rules = None 624 xdata = None 625 flux_labels = None 626 species_labels = None 627 rules_labels = None 628 xdata_labels = None 629 HAS_FLUXES = False 630 HAS_SPECIES = False 631 HAS_RULES = False 632 HAS_XDATA = False 633 IS_VALID = True 634 635 ## def setLabels(self, species=None, fluxes=None, rules=None): 636 ## """set the species, rate and rule label lists""" 637 ## if species != None: 638 ## self.species_labels = species 639 ## if fluxes != None: 640 ## self.flux_labels = fluxes 641 ## if rules != None: 642 ## self.rules_labels = rules 643 644 def setSpecies(self, species, lbls=None): 645 """Set the species array""" 646 self.species = species 647 self.HAS_SPECIES = True 648 if lbls != None: 649 self.species_labels = lbls 650 for s in range(len(self.species_labels)): 651 setattr(self, self.species_labels[s], self.species[s]) 652 653 def setFluxes(self, fluxes, lbls=None): 654 """set the flux array""" 655 self.fluxes = fluxes 656 self.HAS_FLUXES = True 657 if lbls != None: 658 self.flux_labels = lbls 659 for f in range(len(self.flux_labels)): 660 setattr(self, self.flux_labels[f], self.fluxes[f]) 661 662 def setRules(self, rules, lbls=None): 663 """Set the results of rate rules""" 664 self.rules = rules 665 self.HAS_RULES = True 666 if lbls != None: 667 self.rules_labels = lbls 668 for r in range(len(self.rules_labels)): 669 setattr(self, self.rules_labels[r], self.rules[r]) 670 671 def setXData(self, xdata, lbls=None): 672 """Sets extra simulation data""" 673 self.xdata = xdata 674 self.HAS_XDATA = True 675 if lbls != None: 676 self.xdata_labels = lbls 677 for x in range(len(self.xdata_labels)): 678 setattr(self, self.xdata_labels[x], self.xdata[x]) 679 680 def getSpecies(self, lbls=False): 681 """return species array""" 682 output = None 683 if self.HAS_SPECIES: 684 output = self.species 685 if not lbls: 686 return output 687 else: 688 return output, self.species_labels 689 690 def getFluxes(self, lbls=False): 691 """return flux array""" 692 output = None 693 if self.HAS_FLUXES: 694 output = self.fluxes 695 if not lbls: 696 return output 697 else: 698 return output, self.flux_labels 699 700 def getRules(self, lbls=False): 701 """Return rule array""" 702 output = None 703 if self.HAS_RULES: 704 output = self.rules 705 if not lbls: 706 return output 707 else: 708 return output, self.rules_labels 709 710 def getXData(self, lbls=False): 711 """Return xdata array""" 712 output = None 713 if self.HAS_XDATA: 714 output = self.xdata 715 if not lbls: 716 return output 717 else: 718 return output, self.xdata_labels 719 720 def getAllStateData(self, lbls=False): 721 """ 722 Return all available data as species+fluxes+rules 723 if lbls=True returns (array,labels) else just array 724 """ 725 labels = [] 726 output = None 727 if self.HAS_SPECIES: 728 output = self.species 729 labels += self.species_labels 730 if self.HAS_FLUXES: 731 if output is None: 732 output = self.fluxes 733 else: 734 output = numpy.hstack((output, self.fluxes)) 735 labels += self.flux_labels 736 if self.HAS_RULES: 737 if output is None: 738 output = self.rules 739 else: 740 output = numpy.hstack((output, self.rules)) 741 labels += self.rules_labels 742 if self.HAS_XDATA: 743 if output is None: 744 output = self.xdata 745 else: 746 output = numpy.hstack((output, self.xdata)) 747 labels += self.xdata_labels 748 if not lbls: 749 return output 750 else: 751 return output, labels 752 753 def getStateData(self, *args, **kwargs): 754 """getSimData(\*args) feed this method species/rate labels and it 755 will return an array of [time, sp1, r1, ....] 756 """ 757 758 if 'lbls' in kwargs: 759 lbls = kwargs['lbls'] 760 else: 761 lbls = False 762 lout = [] 763 output = [] 764 for roc in args: 765 if self.HAS_SPECIES and roc in self.species_labels: 766 lout.append(roc) 767 output.append(self.species[self.species_labels.index(roc)]) 768 elif self.HAS_FLUXES and roc in self.flux_labels: 769 lout.append(roc) 770 output.append(self.fluxes[self.flux_labels.index(roc)]) 771 elif self.HAS_RULES and roc in self.rules_labels: 772 lout.append(roc) 773 output.append(self.rules[self.rules_labels.index(roc)]) 774 elif self.HAS_XDATA and roc in self.xdata_labels: 775 lout.append(roc) 776 output.append(self.xdata[self.xdata_labels.index(roc)]) 777 else: 778 print('I don\'t have an attribute {} ... ignoring.'.format(roc)) 779 if not lbls: 780 return output 781 else: 782 return numpy.array(output), lout 783 784 785class IntegrationDataObj(object): 786 """ 787 This class is specifically designed to store the results of a time simulation 788 It has methods for setting the Time, Labels, Species and Rate data and 789 getting Time, Species and Rate (including time) arrays. However, of more use: 790 791 - getOutput(\*args) feed this method species/rate labels and it will return 792 an array of [time, sp1, r1, ....] 793 - getDataAtTime(time) the data generated at time point "time". 794 - getDataInTimeInterval(time, bounds=None) more intelligent version of the above 795 returns an array of all data points where: time-bounds <= time <= time+bounds 796 """ 797 798 time = None 799 rates = None 800 species = None 801 rules = None 802 xdata = None 803 time_label = 'Time' 804 rate_labels = None 805 species_labels = None 806 rules_labels = None 807 xdata_labels = None 808 HAS_SPECIES = False 809 HAS_RATES = False 810 HAS_RULES = False 811 HAS_TIME = False 812 HAS_XDATA = False 813 IS_VALID = True 814 TYPE_INFO = 'Deterministic' 815 816 def setLabels(self, species=None, rates=None, rules=None): 817 """set the species, rate and rule label lists""" 818 if species != None: 819 self.species_labels = species 820 if rates != None: 821 self.rate_labels = rates 822 if rules != None: 823 self.rules_labels = rules 824 825 def setTime(self, time, lbl=None): 826 """Set the time vector""" 827 self.time = time.reshape(len(time), 1) 828 self.HAS_TIME = True 829 if lbl != None: 830 self.time_label = lbl 831 832 def setSpecies(self, species, lbls=None): 833 """Set the species array""" 834 self.species = species 835 self.HAS_SPECIES = True 836 if lbls != None: 837 self.species_labels = lbls 838 839 def setRates(self, rates, lbls=None): 840 """set the rate array""" 841 self.rates = rates 842 self.HAS_RATES = True 843 if lbls != None: 844 self.rate_labels = lbls 845 846 def setRules(self, rules, lbls=None): 847 """Set the results of rate rules""" 848 self.rules = rules 849 self.HAS_RULES = True 850 if lbls != None: 851 self.rules_labels = lbls 852 853 def setXData(self, xdata, lbls=None): 854 """Sets extra simulation data""" 855 self.xdata = xdata 856 self.HAS_XDATA = True 857 if lbls != None: 858 self.xdata_labels = lbls 859 860 def getTime(self, lbls=False): 861 """return the time vector""" 862 output = None 863 if self.HAS_TIME: 864 output = self.time.reshape(len(self.time),) 865 if not lbls: 866 return output 867 else: 868 return output, [self.time_label] 869 870 def getSpecies(self, lbls=False): 871 """return time+species array""" 872 output = None 873 if self.HAS_SPECIES: 874 output = numpy.hstack((self.time, self.species)) 875 labels = [self.time_label] + self.species_labels 876 else: 877 output = self.time 878 labels = [self.time_label] 879 if not lbls: 880 return output 881 else: 882 return output, labels 883 884 def getRates(self, lbls=False): 885 """return time+rate array""" 886 output = None 887 if self.HAS_RATES: 888 output = numpy.hstack((self.time, self.rates)) 889 labels = [self.time_label] + self.rate_labels 890 else: 891 output = self.time 892 labels = [self.time_label] 893 if not lbls: 894 return output 895 else: 896 return output, labels 897 898 def getRules(self, lbls=False): 899 """Return time+rule array""" 900 ## assert self.rules != None, "\nNo rules" 901 output = None 902 if self.HAS_RULES: 903 output = numpy.hstack((self.time, self.rules)) 904 labels = [self.time_label] + self.rules_labels 905 else: 906 output = self.time 907 labels = [self.time_label] 908 if not lbls: 909 return output 910 else: 911 return output, labels 912 913 def getXData(self, lbls=False): 914 """Return time+xdata array""" 915 ## assert self.rules != None, "\nNo rules" 916 output = None 917 if self.HAS_XDATA: 918 output = numpy.hstack((self.time, self.xdata)) 919 labels = [self.time_label] + self.xdata_labels 920 else: 921 output = self.time 922 labels = [self.time_label] 923 if not lbls: 924 return output 925 else: 926 return output, labels 927 928 def getDataAtTime(self, time): 929 """Return all data generated at "time" """ 930 # TODO add rate rule data 931 t = None 932 sp = None 933 ra = None 934 ru = None 935 xd = None 936 temp_t = self.time.reshape(len(self.time),) 937 for tt in range(len(temp_t)): 938 if temp_t[tt] == time: 939 t = tt 940 if self.HAS_SPECIES: 941 sp = self.species.take([tt], axis=0) 942 if self.HAS_RATES: 943 ra = self.rates.take([tt], axis=0) 944 if self.HAS_RULES: 945 ru = self.rules.take([tt], axis=0) 946 if self.HAS_XDATA: 947 xd = self.xdata.take([tt], axis=0) 948 break 949 950 output = None 951 if t is not None: 952 output = numpy.array([[temp_t[t]]]) 953 if sp is not None: 954 output = numpy.hstack((output, sp)) 955 if ra is not None: 956 output = numpy.hstack((output, ra)) 957 if ru is not None: 958 output = numpy.hstack((output, ru)) 959 if xd is not None: 960 output = numpy.hstack((output, xd)) 961 962 return output 963 964 def getDataInTimeInterval(self, time, bounds=None): 965 """ 966 getDataInTimeInterval(time, bounds=None) returns an array of all 967 data points where: time-bounds <= time <= time+bounds 968 where bound defaults to stepsize 969 """ 970 # TODO add rate rule data 971 temp_t = self.time.reshape(len(self.time),) 972 if bounds == None: 973 bounds = temp_t[1] - temp_t[0] 974 c1 = temp_t >= time - bounds 975 c2 = temp_t <= time + bounds 976 print('Searching ({}:{}:{})'.format(time - bounds, time, time + bounds)) 977 978 t = [] 979 sp = None 980 ra = None 981 982 for tt in range(len(c1)): 983 if c1[tt] and c2[tt]: 984 t.append(tt) 985 output = None 986 if len(t) > 0: 987 output = self.time.take(t) 988 output = output.reshape(len(output), 1) 989 if self.HAS_SPECIES and self.HAS_TIME: 990 output = numpy.hstack((output, self.species.take(t, axis=0))) 991 if self.HAS_RATES: 992 output = numpy.hstack((output, self.rates.take(t, axis=0))) 993 if self.HAS_RULES: 994 output = numpy.hstack((output, self.rules.take(t, axis=0))) 995 if self.HAS_XDATA: 996 output = numpy.hstack((output, self.xdata.take(t, axis=0))) 997 return output 998 999 def getOutput(self, *args, **kwargs): 1000 """ 1001 Old alias for getSimData() 1002 getOutput(\*args) feed this method species/rate labels and it 1003 will return an array of [time, sp1, r1, ....] 1004 """ 1005 return self.getSimData(*args, **kwargs) 1006 1007 def getAllSimData(self, lbls=False): 1008 """ 1009 Return all available data as time+species+rates+rules 1010 if lbls=True returns (array,lables) else just array 1011 """ 1012 labels = [self.time_label] 1013 if self.HAS_SPECIES and self.HAS_TIME: 1014 output = numpy.hstack((self.time, self.species)) 1015 labels += self.species_labels 1016 if self.HAS_RATES: 1017 output = numpy.hstack((output, self.rates)) 1018 labels += self.rate_labels 1019 if self.HAS_RULES: 1020 output = numpy.hstack((output, self.rules)) 1021 labels += self.rules_labels 1022 if self.HAS_XDATA: 1023 output = numpy.hstack((output, self.xdata)) 1024 labels += self.xdata_labels 1025 if not lbls: 1026 return output 1027 else: 1028 return output, labels 1029 1030 def getSimData(self, *args, **kwargs): 1031 """getSimData(\*args) feed this method species/rate labels and it 1032 will return an array of [time, sp1, r1, ....] 1033 """ 1034 output = self.time 1035 ## print argimrgs 1036 if 'lbls' in kwargs: 1037 lbls = kwargs['lbls'] 1038 else: 1039 lbls = False 1040 lout = [self.time_label] 1041 for roc in args: 1042 if self.HAS_SPECIES and roc in self.species_labels: 1043 lout.append(roc) 1044 output = numpy.hstack( 1045 ( 1046 output, 1047 self.species.take([self.species_labels.index(roc)], axis=-1), 1048 ) 1049 ) 1050 if self.HAS_RATES and roc in self.rate_labels: 1051 lout.append(roc) 1052 output = numpy.hstack( 1053 (output, self.rates.take([self.rate_labels.index(roc)], axis=-1)) 1054 ) 1055 if self.HAS_RULES and roc in self.rules_labels: 1056 lout.append(roc) 1057 output = numpy.hstack( 1058 (output, self.rules.take([self.rules_labels.index(roc)], axis=-1)) 1059 ) 1060 if self.HAS_XDATA and roc in self.xdata_labels: 1061 lout.append(roc) 1062 output = numpy.hstack( 1063 (output, self.xdata.take([self.xdata_labels.index(roc)], axis=-1)) 1064 ) 1065 if not lbls: 1066 return output 1067 else: 1068 return output, lout 1069 1070 1071# this must stay in sync with core2 1072class NewCoreBase(object): 1073 """ 1074 Core2 base class, needed here as we use Core2 derived classes 1075 in PySCes 1076 """ 1077 1078 name = None 1079 __DEBUG__ = False 1080 1081 def getName(self): 1082 return self.name 1083 1084 def setName(self, name): 1085 self.name = name 1086 1087 def get(self, attr): 1088 """Return an attribute whose name is str(attr)""" 1089 return self.__getattribute__(attr) 1090 1091 1092# this must stay in sync with core2 1093class NumberBase(NewCoreBase): 1094 """ 1095 Derived Core2 number class. 1096 """ 1097 1098 value = None 1099 value_initial = None 1100 1101 def __call__(self): 1102 return self.value 1103 1104 def getValue(self): 1105 return self.value 1106 1107 def setValue(self, v): 1108 self.value = v 1109 1110 1111# Finally killed my lambda functions - brett07 1112class ReactionObj(NewCoreBase): 1113 """ 1114 Defines a reaction with a KineticLaw *kl8, *formula* and *name* bound 1115 to a model instance, *mod*. 1116 """ 1117 1118 formula = None 1119 mod = None 1120 rate = None 1121 xcode = None 1122 code_string = None 1123 symbols = None 1124 compartment = None 1125 piecewises = None 1126 1127 def __init__(self, mod, name, kl, klrepl='self.'): 1128 """ 1129 mod : model instance 1130 name = reaction name 1131 kl = kinetic law 1132 klrepl = replacement string for KineticLaw 1133 """ 1134 self.mod = mod 1135 self.name = name 1136 self.setKineticLaw(kl, klrepl='self.') 1137 1138 def __call__(self, *args): 1139 exec(self.xcode) 1140 return self.rate 1141 1142 def setKineticLaw(self, kl, klrepl='self.'): 1143 InfixParser.setNameStr('self.mod.', '') 1144 InfixParser.parse(kl.replace(klrepl, '')) 1145 self.symbols = InfixParser.names 1146 self.piecewises = InfixParser.piecewises 1147 formula = InfixParser.output 1148 # this code has to stay together # 1149 for pw in InfixParser.piecewises: 1150 formula = formula.replace( 1151 'self.mod.{}'.format(pw), 'self.mod.{}()'.format(pw) 1152 ) 1153 # this code has to stay together # 1154 self.code_string = 'self.rate={}'.format(formula) 1155 self.xcode = compile(self.code_string, 'Req: {}'.format(self.name), 'exec') 1156 self.formula = ( 1157 kl.replace(klrepl, '') 1158 .replace('numpy.', '') 1159 .replace('math.', '') 1160 .replace('operator.', '') 1161 ) 1162 1163 1164InfixParser = MyInfixParser() 1165InfixParser.buildlexer() 1166InfixParser.buildparser( 1167 debug=0, debugfile='infix.dbg', tabmodule='infix_tabmodule', outputdir=OUTPUT_DIR 1168) 1169InfixParser.setNameStr('self.', '') 1170os.chdir(CWD) 1171 1172# adapted from core2 1173class Function(NewCoreBase): 1174 """ 1175 Function class ported from Core2 to enable the use of functions in PySCeS. 1176 """ 1177 1178 formula = None 1179 code_string = None 1180 xcode = None 1181 value = None 1182 symbols = None 1183 argsl = None 1184 mod = None 1185 piecewises = None 1186 functions = None 1187 1188 def __init__(self, name, mod): 1189 self.name = name 1190 self.argsl = [] 1191 self.functions = [] 1192 self.mod = mod 1193 1194 def __call__(self, *args): 1195 for ar in range(len(args)): 1196 self.__setattr__(self.argsl[ar], args[ar]) 1197 exec(self.xcode) 1198 return self.value 1199 1200 def setArg(self, var, value=None): 1201 self.__setattr__(var, value) 1202 self.argsl.append(var) 1203 1204 def addFormula(self, formula): 1205 self.formula = formula 1206 InfixParser.setNameStr('self.', '') 1207 InfixParser.SymbolReplacements = {'_TIME_': 'mod._TIME_'} 1208 InfixParser.parse(formula) 1209 self.piecewises = InfixParser.piecewises 1210 self.symbols = InfixParser.names 1211 self.functions = InfixParser.functions 1212 self.code_string = 'self.value={}'.format(InfixParser.output) 1213 self.xcode = compile(self.code_string, 'Func: {}'.format(self.name), 'exec') 1214 1215 1216# adapted from core2 1217class EventAssignment(NumberBase): 1218 """ 1219 Event assignments are actions that are triggered by an event. 1220 Ported from Core2 to build an event handling framework fro PySCeS 1221 """ 1222 1223 variable = None 1224 symbols = None 1225 formula = None 1226 code_string = None 1227 xcode = None 1228 mod = None 1229 piecewises = None 1230 __DEBUG__ = False 1231 1232 def __call__(self): 1233 setattr(self.mod, self.variable, self.value) 1234 if self.__DEBUG__: 1235 print('\tAssigning {} = {}'.format(self.variable, self.value)) 1236 return True 1237 1238 def __init__(self, name, mod): 1239 self.setName(name) 1240 self.mod = mod 1241 1242 def setVariable(self, var): 1243 self.variable = var 1244 1245 def setFormula(self, formula): 1246 self.formula = formula 1247 InfixParser.setNameStr('self.mod.', '') 1248 InfixParser.SymbolReplacements = {'_TIME_': 'self.mod._TIME_'} 1249 InfixParser.parse(formula) 1250 self.piecewises = InfixParser.piecewises 1251 self.symbols = InfixParser.names 1252 self.code_string = 'self.value={}'.format(InfixParser.output) 1253 self.xcode = compile(self.code_string, 'EvAs: {}'.format(self.name), 'exec') 1254 ## print '\t', self.name, self.code_string 1255 1256 def evaluateAssignment(self): 1257 exec(self.xcode) 1258 1259 1260# adapted from core2 1261class Event(NewCoreBase): 1262 """ 1263 Events have triggers and fire EventAssignments when required. 1264 Ported from Core2. 1265 """ 1266 1267 trigger = None 1268 delay = 0.0 1269 formula = None 1270 code_string = None 1271 xcode = None 1272 state0 = False 1273 state = False 1274 assignments = None 1275 _TIME_ = 0.0 1276 _ASS_TIME_ = 0.0 1277 _need_action = False 1278 _assign_now = False 1279 symbols = None 1280 _time_symbol = None 1281 piecewises = None 1282 mod = None 1283 __DEBUG__ = True 1284 1285 def __init__(self, name, mod): 1286 self.setName(name) 1287 self.assignments = [] 1288 self.mod = mod 1289 1290 def __call__(self, time): 1291 self._TIME_ = time 1292 exec(self.xcode) 1293 ret = False 1294 if self.state0 and not self.state: 1295 self.state0 = self.state 1296 if not self.state0 and self.state: 1297 for ass in self.assignments: 1298 ass.evaluateAssignment() 1299 self.state0 = self.state 1300 self._need_action = True 1301 self._ASS_TIME_ = time + self.delay 1302 if self.__DEBUG__: 1303 print('\nevent {} is evaluating at {}'.format(self.name, time)) 1304 ret = True 1305 if self._need_action and self._TIME_ >= self._ASS_TIME_: 1306 self._assign_now = True 1307 if self.__DEBUG__: 1308 print( 1309 'event {} is assigning at {} (delay={})'.format( 1310 self.name, time, self.delay 1311 ) 1312 ) 1313 self._need_action = False 1314 ret = True 1315 return ret 1316 1317 def setTrigger(self, formula, delay=0.0): 1318 self.formula = formula 1319 self.delay = delay 1320 InfixParser.setNameStr('self.mod.', '') 1321 if self._time_symbol != None: 1322 InfixParser.SymbolReplacements = {self._time_symbol: '_TIME_'} 1323 else: 1324 InfixParser.SymbolReplacements = {'_TIME_': '_TIME_'} 1325 InfixParser.parse(formula) 1326 self.piecewises = InfixParser.piecewises 1327 self.symbols = InfixParser.names 1328 self.code_string = 'self.state={}'.format(InfixParser.output) 1329 self.xcode = compile(self.code_string, 'Ev: {}'.format(self.name), 'exec') 1330 1331 def setAssignment(self, var, formula): 1332 ass = EventAssignment(var, mod=self.mod) 1333 ass.setVariable(var) 1334 ass.setFormula(formula) 1335 self.assignments.append(ass) 1336 self.__setattr__('_' + var, ass) 1337 1338 def reset(self): 1339 self.state0 = False 1340 self.state = False 1341 self._TIME_ = 0.0 1342 self._ASS_TIME_ = 0.0 1343 1344 1345class PieceWise(NewCoreBase): 1346 """ 1347 Generic piecewise class adapted from Core2 that generates a compiled 1348 Python code block that allows evaluation of arbitrary length piecewise 1349 functions. Piecewise statements should be defined in assignment rules 1350 as `piecewise(<Piece>, <Conditional>, <OtherValue>)` where there can 1351 be an arbitrary number of `<Piece>, <Conditional>` pairs. 1352 1353 - *args* a dictionary of piecewise information generated by the InfixParser as InfixParser.piecewises 1354 1355 """ 1356 1357 name = None 1358 value = None 1359 formula = None 1360 code_string = None 1361 xcode = None 1362 _names = None 1363 _TIME_ = None 1364 1365 def __init__(self, pwd, mod): 1366 pwd = pwd.copy() 1367 self.mod = mod 1368 if pwd['other'] != None: 1369 other = 'self.value = {}'.format( 1370 pwd.pop('other').replace('self.', 'self.mod.') 1371 ) 1372 else: 1373 other = 'pass' 1374 pwd.pop('other') 1375 1376 InfixParser.setNameStr('self.mod.', '') 1377 self._names = [] 1378 if len(list(pwd.keys())) == 1: 1379 formula = pwd[0][0] 1380 InfixParser.parse(formula) 1381 for n in InfixParser.names: 1382 if n not in self._names and n != '_TIME_': 1383 self._names.append(n) 1384 formula = InfixParser.output 1385 thenStat = pwd[0][1].replace('self.', 'self.mod.') 1386 ## thenStat = pwd[0][1] 1387 ## InfixParser.setNameStr('self.mod.', '') 1388 ## InfixParser.parse(thenStat) 1389 ## thenStat = InfixParser.output 1390 self.code_string = 'if {}:\n self.value = {}\nelse:\n {}'.format( 1391 formula, thenStat, other, 1392 ) 1393 self.formula = self.code_string.replace('self.', '') 1394 else: 1395 formula = pwd[0][0] 1396 InfixParser.parse(formula) 1397 for n in InfixParser.names: 1398 if n not in self._names and n != '_TIME_': 1399 self._names.append(n) 1400 formula = InfixParser.output 1401 thenStat = pwd[0][1].replace('self.', 'self.mod.') 1402 ## thenStat = pwd[0][1] 1403 ## InfixParser.setNameStr('self.mod.', '') 1404 ## InfixParser.parse(thenStat) 1405 ## thenStat = InfixParser.output 1406 self.code_string = 'if {}:\n self.value = {}\n'.format(formula, thenStat) 1407 pwd.pop(0) 1408 for p in pwd: 1409 formula = pwd[p][0] 1410 InfixParser.parse(formula) 1411 for n in InfixParser.names: 1412 if n not in self._names and n != '_TIME_': 1413 self._names.append(n) 1414 1415 formula = InfixParser.output 1416 thenStat = pwd[p][1].replace('self.', 'self.mod.') 1417 ## thenStat = pwd[p][1] 1418 ## InfixParser.setNameStr('self.mod.', '') 1419 ## InfixParser.parse(thenStat) 1420 ## thenStat = InfixParser.output 1421 self.code_string += 'elif {}:\n self.value = {}\n'.format( 1422 formula, thenStat, 1423 ) 1424 self.code_string += 'else:\n .format'.format(other) 1425 self.formula = self.code_string.replace('self.', '') 1426 self.xcode = compile(self.code_string, 'PieceWise', 'exec') 1427 1428 def __call__(self): 1429 exec(self.xcode) 1430 return self.value 1431 1432 1433class PysMod(object): 1434 """ 1435 Create a model object and instantiate a PySCeS model so that it can be used for 1436 further analyses. PySCeS model descriptions can be loaded from files or strings 1437 (see the *loader* argument for details). 1438 1439 - *File* the name of the PySCeS input file if not explicit a \*.psc extension is 1440 assumed. 1441 - *dir* if specified, the path to the input file otherwise the default PyscesModel 1442 directory (defined in the pys_config.ini file) is assumed. 1443 - *autoload* autoload the model, pre 0.7.1 call mod.doLoad(). (default=True) **new** 1444 - *loader* the default behaviour is to load PSC file, however, if this argument is 1445 set to 'string' an input file can be supplied as the *fString* argument 1446 (default='file') 1447 - *fString* a string containing a PySCeS model file (use with *loader='string'*) 1448 the *File* argument now sepcifies the new input file name. 1449 1450 """ 1451 1452 __version__ = __version__ 1453 __pysces_directory__ = INSTALL_DIR 1454 __settings__ = None 1455 random = random 1456 # __STOMPY__ = None 1457 1458 def __init__(self, File=None, dir=None, loader='file', fString=None, autoload=True): 1459 """ 1460 Create a model object and instantiate a PySCeS model so that it can be used for further analyses. PySCeS 1461 model descriptions can be loaded from files or strings (see the *loader* argument for details). 1462 1463 - *File* the name of the PySCeS input file if not explicit a \*.psc extension is assumed. 1464 - *dir* if specified, the path to the input file otherwise the default PyscesModel directory (defined in the pys_config.ini file) is assumed. 1465 - *autoload* autoload the model, pre 0.7.1 call mod.doLoad(). (default=True) **new** 1466 - *loader* the default behaviour is to load PSC file, however, if this argument is set to 'string' an input file can be supplied as the *fString* argument (default='file') 1467 - *fString* a string containing a PySCeS model file (use with *loader='string'*) the *File* argument now specifies the new input file name. 1468 1469 """ 1470 1471 # if _HAVE_STOMPY: 1472 # self.__STOMPY__ = StomPyInterface(MODEL_DIR, OUTPUT_DIR) 1473 # print 'PySCeS/StochPy interface active' 1474 # else: 1475 # self.__STOMPY__ = None 1476 1477 self.__settings__ = {} 1478 self.__settings__.update({'enable_deprecated_attr': True}) 1479 self.WorkDir = CWD 1480 if loader == 'file': 1481 self.LoadFromFile(File, dir) 1482 elif loader == 'string': 1483 self.LoadFromString(File, fString) 1484 else: 1485 self.LoadFromFile(File, dir) 1486 # stuff that needs to be done before initmodel 1487 self.__settings__['mode_substitute_assignment_rules'] = False 1488 self.__settings__['display_compartment_warnings'] = False 1489 self._TIME_ = 0.0 # this will be the built-in time 1490 self.piecewise_functions = [] 1491 self.__piecewises__ = {} 1492 self.__HAS_PIECEWISE__ = False 1493 if autoload: 1494 self.ModelLoad() 1495 self.__PSC_auto_load = True 1496 else: 1497 self.__PSC_auto_load = False 1498 1499 def ModelLoad(self, stoich_load=0): 1500 """ 1501 Load and instantiate a PySCeS model so that it can be used for further analyses. This function 1502 replaces the pre-0.7.1 doLoad() method. 1503 1504 - *stoich_load* try to load a structural analysis saved with Stoichiometry_Save_Serial() (default=0) 1505 1506 """ 1507 self.InitialiseInputFile() 1508 assert self.__parseOK, '\nError in input file, parsing could not complete' 1509 self.Stoichiometry_Analyse(override=0, load=stoich_load) 1510 # add new Style functions to model 1511 self.InitialiseFunctions() 1512 self.InitialiseCompartments() 1513 self.InitialiseRules() 1514 self.InitialiseEvents() 1515 self.InitialiseModel() 1516 self.InitialiseRuleChecks() 1517 self.InitialiseOldFunctions() # TODO replace this with initialisation functions 1518 1519 def doLoad(self, stoich_load=0): 1520 """ 1521 Load and instantiate a PySCeS model so that it can be used for further analyses. This function is 1522 being replaced by the ModelLoad() method. 1523 1524 - *stoich_load* try to load a structural analysis saved with Stoichiometry_Save_Serial() (default=0) 1525 1526 """ 1527 if not self.__PSC_auto_load: 1528 self.ModelLoad(stoich_load=stoich_load) 1529 else: 1530 print( 1531 'PySCeS now automatically loads the model on model object instantiation. If you do not want this behaviour pass the autoload=False argument to the constructor, if you really want to reload the model, run reLoad().' 1532 ) 1533 1534 def reLoad(self, stoich_load=0): 1535 """ 1536 Re-load and instantiate a PySCeS model so that it can be used for further analyses. This is just a convenience call to the ModelLoad() method. 1537 1538 - *stoich_load* try to load a structural analysis saved with Stoichiometry_Save_Serial() (default=0) 1539 1540 """ 1541 self.ModelLoad(stoich_load=stoich_load) 1542 1543 def LoadFromString(self, File=None, fString=None): 1544 """ 1545 Docstring required 1546 """ 1547 1548 # grab model directory 1549 chkmdir() 1550 mdir = MODEL_DIR 1551 1552 # check for .psc extension 1553 File = chkpsc(File) 1554 1555 print('Using model directory: ' + mdir) 1556 1557 if not os.path.isdir(os.path.join(MODEL_DIR, "orca")): 1558 os.mkdir(os.path.join(MODEL_DIR, "orca")) 1559 1560 mdir = os.path.join(MODEL_DIR, "orca") 1561 1562 # write string to file 1563 try: 1564 outFile = open(os.path.join(mdir, File), 'w') 1565 outFile.write(fString) 1566 outFile.close() 1567 print('Using file: ' + File) 1568 1569 if os.path.exists(os.path.join(mdir, File)): 1570 print(os.path.join(mdir, File) + ' loading .....', end=' ') 1571 self.ModelDir = mdir 1572 self.ModelFile = File 1573 else: 1574 print(os.path.join(mdir, File) + ' does not exist') 1575 print('Please set with ModelDir and ModelFile .....', end=' ') 1576 self.ModelFile = 'None' 1577 self.ModelDir = mdir 1578 except Exception as e: 1579 print(e) 1580 print( 1581 os.path.join(mdir, File) 1582 + ' does not exist please re-instantiate model .....', 1583 end=' ', 1584 ) 1585 self.__settings__['display_debug'] = 0 1586 self.ModelOutput = OUTPUT_DIR 1587 1588 # Initialize serial directory 1589 self.__settings__['serial_dir'] = os.path.join(self.ModelOutput, 'pscdat') 1590 # Initialize stoichiometric precision 1591 self.__settings__['stoichiometric_analysis_fp_zero'] = mach_spec.eps * 2.0e4 1592 self.__settings__['stoichiometric_analysis_lu_precision'] = self.__settings__[ 1593 'stoichiometric_analysis_fp_zero' 1594 ] 1595 self.__settings__['stoichiometric_analysis_gj_precision'] = ( 1596 self.__settings__['stoichiometric_analysis_lu_precision'] * 10.0 1597 ) 1598 1599 """ 1600 # Initialise elementary modes 1601 if os.sys.platform == 'win32': 1602 self.eModeExe_int = os.path.join(self.__metatool,'meta43_int.exe') 1603 self.eModeExe_dbl = os.path.join(self.__metatool,'meta43_double.exe') 1604 else: 1605 self.eModeExe_int = os.path.join(self.__metatool,'meta43_int') 1606 self.eModeExe_dbl = os.path.join(self.__metatool,'meta43_double') 1607 print 'Done.' 1608 """ 1609 1610 def LoadFromFile(self, File=None, dir=None): 1611 """ 1612 __init__(File=None,dir=None) 1613 1614 Initialise a PySCeS model object with PSC file that can be found in optional directory. 1615 If a a filename is not supplied the pysces.model_dir directory contents is displayed and 1616 the model name can be entered at the promp (<ctrl>+C exits the loading process). 1617 1618 Arguments: 1619 1620 File [default=None]: the name of the PySCeS input file 1621 dir [default=pysces.model_dir]: the optional directory where the PSC file can be found 1622 1623 """ 1624 if dir != None: 1625 if os.path.isdir(dir): 1626 pass 1627 else: 1628 chkmdir() 1629 dir = MODEL_DIR 1630 else: 1631 chkmdir() 1632 dir = MODEL_DIR 1633 1634 mfgo = 0 1635 if File == None: 1636 mfgo = 1 1637 try: 1638 if File != None: 1639 File = chkpsc(File) 1640 if not os.path.exists(os.path.join(dir, File)): 1641 mfgo = 1 1642 except: 1643 mfgo = 1 1644 1645 while mfgo == 1: 1646 print('Models available in your model_dir: \n************') 1647 cntr = 0 1648 namelen = 0 1649 while len(os.listdir(dir)) == 0: 1650 print( 1651 'No models available in model directory, please set using pys_usercfg.ini or call\ 1652 with pysces.model(\'file.psc\',dir=\'path\\to\\models\')' 1653 ) 1654 dir = input('\nPlease enter full path to models <CTRL+C> exits: ') 1655 1656 dirList = os.listdir(dir) 1657 for x in range(len(dirList) - 1, -1, -1): 1658 if dirList[x][-4:] != '.psc': 1659 a = dirList.pop(x) 1660 1661 for x in dirList: 1662 if len(x) > namelen: 1663 namelen = len(x) 1664 for x in dirList: 1665 if cntr < 2: 1666 print(x + ' ' * (namelen - len(x)), end=' ') 1667 cntr += 1 1668 else: 1669 print(x) 1670 cntr = 0 1671 print('\n************\n') 1672 print('\nYou need to specify a valid model file ...\n') 1673 1674 File = input('\nPlease enter filename: ') 1675 try: 1676 File = chkpsc(File) 1677 if os.path.exists(os.path.join(dir, File)): 1678 mfgo = 0 1679 except: 1680 mfgo = 1 1681 1682 print('Using model directory: ' + dir) 1683 1684 try: 1685 if os.path.exists(os.path.join(dir, File)): 1686 print(os.path.join(dir, File) + ' loading .....', end=' ') 1687 self.ModelDir = dir 1688 self.ModelFile = File 1689 else: 1690 print(os.path.join(dir, File) + ' does not exist') 1691 print('Please set with ModelDir and ModelFile .....', end=' ') 1692 self.ModelFile = 'None' 1693 self.ModelDir = dir 1694 except: 1695 print( 1696 os.path.join(dir, File) 1697 + ' does not exist please re-instantiate model .....', 1698 end=' ', 1699 ) 1700 self.__settings__['display_debug'] = 0 1701 self.ModelOutput = OUTPUT_DIR 1702 1703 ## # Initialise elementary modes 1704 ## if os.sys.platform == 'win32': 1705 ## self.eModeExe_int = os.path.join(self.__metatool,'meta43_int.exe') 1706 ## self.eModeExe_dbl = os.path.join(self.__metatool,'meta43_double.exe') 1707 ## else: 1708 ## self.eModeExe_int = os.path.join(self.__metatool,'meta43_int') 1709 ## self.eModeExe_dbl = os.path.join(self.__metatool,'meta43_double') 1710 ## print 'Done.' 1711 1712 # Initialize serial directory 1713 self.__settings__['serial_dir'] = os.path.join(self.ModelOutput, 'pscdat') 1714 # Initialize stoichiometric precision 1715 self.__settings__['stoichiometric_analysis_fp_zero'] = mach_spec.eps * 2.0e4 1716 self.__settings__['stoichiometric_analysis_lu_precision'] = self.__settings__[ 1717 'stoichiometric_analysis_fp_zero' 1718 ] 1719 self.__settings__['stoichiometric_analysis_gj_precision'] = ( 1720 self.__settings__['stoichiometric_analysis_lu_precision'] * 10.0 1721 ) 1722 1723 # def __LoadStomPyInterface__(self): 1724 # """ 1725 # Load the StomPy Stochastic simulation interface 1726 # """ 1727 # if _HAVE_STOMPY and self.__STOMPY__ == None: 1728 # self.__STOMPY__ = StomPyInterface(MODEL_DIR, OUTPUT_DIR) 1729 # print 'PySCeS/StomPy interface active' 1730 1731 def __ParsePiecewiseFunctions__(self, piecewises): 1732 """ 1733 THIS IS HIGHLY EXPERIMENTAL! Takes the a piecewises dictionary 1734 and creates a piecewise object while substituting the object name 1735 in the formula string. 1736 1737 - *piecewises* a piecewise dictionary created by the InfixParser 1738 1739 """ 1740 if piecewises != None and len(list(piecewises.keys())) > 0: 1741 self.__HAS_PIECEWISE__ = True 1742 for p in piecewises: 1743 print('Info: adding piecewise object: {}'.format(p)) 1744 # if len(piecewises[p].keys()) == 2: 1745 # piecewises[p][0].reverse() # only for libsbml generated infix 1746 self.__piecewises__.update({p: piecewises[p]}) 1747 P = PieceWise(piecewises[p], self) 1748 P.setName(p) 1749 self.piecewise_functions.append(P) 1750 setattr(self, p, P) 1751 1752 def InitialiseInputFile(self): 1753 """ 1754 InitialiseInputFile() 1755 1756 Parse the input file associated with the PySCeS model instance and assign the basic model attributes 1757 1758 Arguments: 1759 None 1760 1761 """ 1762 self.__parseOK = 1 # check that model has parsed ok? 1763 try: 1764 if os.path.exists(os.path.join(self.ModelDir, self.ModelFile)): 1765 pass 1766 else: 1767 print( 1768 '\nInvalid self.ModelFile: ' 1769 + os.path.join(self.ModelDir, self.ModelFile) 1770 ) 1771 except: 1772 print( 1773 'WARNING: Problem verifying: ' 1774 + os.path.join(self.ModelDir, self.ModelFile) 1775 ) 1776 1777 if self.ModelFile[-4:] == '.psc': 1778 pass 1779 else: 1780 print('Assuming extension is .psc') 1781 self.ModelFile += '.psc' 1782 1783 print('\nParsing file: {}'.format(os.path.join(self.ModelDir, self.ModelFile))) 1784 1785 pscParser.ParsePSC(self.ModelFile, self.ModelDir, self.WorkDir) 1786 print(' ') 1787 1788 badlist = pscParser.KeywordCheck(pscParser.ReactionIDs) 1789 badlist = pscParser.KeywordCheck(pscParser.Inits, badlist) 1790 1791 if len(badlist) != 0: 1792 print( 1793 '\n******************************\nPSC input file contains PySCeS keywords please rename them and reload:' 1794 ) 1795 for item in badlist: 1796 print(' --> ' + item) 1797 print('******************************\n') 1798 self.__parseOK = 0 1799 # assert len(badlist) != 0, 'Keyword error, please check input file' 1800 1801 if self.__parseOK: 1802 # brett 2008 1803 InfixParser.__pwcntr__ = 0 1804 self.__nDict__ = pscParser.nDict.copy() 1805 self.__sDict__ = pscParser.sDict.copy() 1806 self.__pDict__ = pscParser.pDict.copy() 1807 self.__uDict__ = pscParser.uDict.copy() 1808 1809 # model attributes are now initialised here brett2008 1810 self.__InitDict__ = {} 1811 # set parameters and add to __InitDict__ 1812 for p in list(self.__pDict__.keys()): 1813 setattr(self, self.__pDict__[p]['name'], self.__pDict__[p]['initial']) 1814 self.__InitDict__.update( 1815 {self.__pDict__[p]['name']: self.__pDict__[p]['initial']} 1816 ) 1817 # set species and add to __InitDict__ and set mod.Xi_init 1818 for s in list(self.__sDict__.keys()): 1819 setattr(self, self.__sDict__[s]['name'], self.__sDict__[s]['initial']) 1820 if not self.__sDict__[s]['fixed']: 1821 setattr( 1822 self, 1823 self.__sDict__[s]['name'] + '_init', 1824 self.__sDict__[s]['initial'], 1825 ) 1826 self.__InitDict__.update( 1827 {self.__sDict__[s]['name']: self.__sDict__[s]['initial']} 1828 ) 1829 1830 # setup keywords 1831 self.__KeyWords__ = pscParser.KeyWords.copy() 1832 if self.__KeyWords__['Modelname'] == None: 1833 self.__KeyWords__['Modelname'] = self.ModelFile.replace('.psc', '') 1834 if self.__KeyWords__['Description'] == None: 1835 self.__KeyWords__['Description'] = self.ModelFile.replace('.psc', '') 1836 # if SpeciesTypes undefined assume [] 1837 if self.__KeyWords__['Species_In_Conc'] == None: 1838 self.__KeyWords__['Species_In_Conc'] = True 1839 # if OutputType is undefined assume it is the same as SpeciesType 1840 if self.__KeyWords__['Output_In_Conc'] == None: 1841 if self.__KeyWords__['Species_In_Conc']: 1842 self.__KeyWords__['Output_In_Conc'] = True 1843 else: 1844 self.__KeyWords__['Output_In_Conc'] = False 1845 if self.__KeyWords__['ModelType'] == None: 1846 self.__KeyWords__['ModelType'] = ['Deterministic'] 1847 else: 1848 self.__KeyWords__['ModelType'] = tuple( 1849 [t.strip() for t in self.__KeyWords__['ModelType'].split(',')] 1850 ) 1851 1852 # we now check for modeltype, if it specified as stochastic check if stochpy is available 1853 # if self.__KeyWords__['ModelType'] == ['Stochastic']: 1854 # if _HAVE_STOMPY: 1855 # print 'INFO: This model suggests that it requires discrete simulation and StochPy is installed ... mod.doStochSim() and mod.doStochSimPlot() are available for use.' 1856 # else: 1857 # print 'INFO: This model suggests that it requires stochastic simulation and StochPy (stompy.sf.net) is not installed ... PySCeS will treat this model as continuous.' 1858 1859 # set the species type in sDict according to 'Species_In_Conc' 1860 for s in list(self.__sDict__.keys()): 1861 if not self.__KeyWords__['Species_In_Conc']: 1862 self.__sDict__[s]['isamount'] = True 1863 else: 1864 self.__sDict__[s]['isamount'] = False 1865 1866 # setup compartments 1867 self.__compartments__ = pscParser.compartments.copy() 1868 if len(list(self.__compartments__.keys())) > 0: 1869 self.__HAS_COMPARTMENTS__ = True 1870 else: 1871 self.__HAS_COMPARTMENTS__ = False 1872 1873 # no (self.) 1874 self.__fixed_species__ = copy.copy(pscParser.fixed_species) 1875 self.__species__ = copy.copy(pscParser.species) 1876 self.__parameters__ = copy.copy(pscParser.parameters) 1877 self.__reactions__ = copy.copy(pscParser.reactions) 1878 self.__modifiers__ = copy.copy(pscParser.modifiers) 1879 # Initialize exposed stuff 1880 self.fixed_species = tuple(pscParser.fixed_species) 1881 self.species = tuple(pscParser.species) 1882 self.parameters = tuple(pscParser.parameters) 1883 self.reactions = tuple(pscParser.reactions) 1884 self.modifiers = tuple(pscParser.modifiers) 1885 1886 # Add input file defined fuctions - brett 200500621 1887 # TODO deprecated 1888 # self._Function_time = copy.copy(pscParser.TimeFunc) 1889 self._Function_user = copy.copy(pscParser.UserFunc) 1890 self._Function_init = pscParser.InitFunc 1891 1892 self.__functions__ = pscParser.Functions.copy() 1893 self.__rules__ = pscParser.AssignmentRules.copy() 1894 self.__InitFuncs__ = pscParser.ModelInit.copy() 1895 self.__userfuncs__ = pscParser.UserFuncs.copy() 1896 self.__eDict__ = pscParser.Events.copy() 1897 self.__cbm_fluxbounds__ = pscParser.cbm_FluxBounds.copy() 1898 self.__cbm_objfuncs__ = pscParser.cbm_ObjectiveFunctions.copy() 1899 self.__cbm_userfluxconstraints__ = pscParser.cbm_UserFluxConstraints.copy() 1900 ## if pscParser.ModelUsesNumpyFuncs: 1901 ## print 'Numpy functions detected in kinetic laws.\n' 1902 else: 1903 print('\nERROR: model parsing error, please check input file.\n') 1904 # added in a check for model correctness and human error reporting (1=ok, 0=error) 1905 if len(pscParser.SymbolErrors) != 0: 1906 print('\nUndefined symbols:\n{}'.format(pscParser.SymbolErrors)) 1907 if not pscParser.ParseOK: 1908 print( 1909 '\n\n*****\nModel parsing errors detected in input file ' 1910 + self.ModelFile 1911 + '\n*****' 1912 ) 1913 print('\nInput file errors') 1914 for error in pscParser.LexErrors: 1915 print(error) 1916 ## try: 1917 ## print error[0] + 'in line:\t' + str(error[1]) + ' ('+ error[2][:20] +' ...)' 1918 ## except IndexError: 1919 ## print 'Illegal character:', error.__repr__(), error.__str__() 1920 print('\nParser errors') 1921 for error in pscParser.ParseErrors: 1922 print(error) 1923 ## try: 1924 ## print error[0] + '- ' + error[2][:20] 1925 ## except IndexError: 1926 ## print 'Illegal character:', error 1927 assert pscParser.ParseOK == 1, 'Input File Error' 1928 1929 def InitialiseRules(self): 1930 # we need to detect different types of rules etc 1931 # defmod 1932 self.__HAS_FORCED_FUNCS__ = False 1933 self.__HAS_RATE_RULES__ = False 1934 rate_rules = {} 1935 assignment_rules = {} 1936 for ar in self.__rules__: 1937 if self.__rules__[ar]['type'] == 'assignment': 1938 self.__HAS_FORCED_FUNCS__ = True 1939 assignment_rules.update({ar: self.__rules__[ar]}) 1940 elif self.__rules__[ar]['type'] == 'rate': 1941 self.__HAS_RATE_RULES__ = True 1942 rate_rules.update({ar: self.__rules__[ar]}) 1943 1944 # THE NEW WAY (adds formula parsing for numpy functions etc) 1945 InfixParser.setNameStr('self.', '') 1946 self._NewRuleXCode = {} 1947 if ( 1948 self.__HAS_FORCED_FUNCS__ 1949 and not self.__settings__['mode_substitute_assignment_rules'] 1950 ): 1951 code_string = '' 1952 all_names = [] 1953 for ar in assignment_rules: 1954 name = assignment_rules[ar]['name'] 1955 InfixParser.setNameStr('self.', '') 1956 InfixParser.parse(assignment_rules[ar]['formula']) 1957 assignment_rules[ar]['symbols'] = InfixParser.names 1958 formula = InfixParser.output 1959 # this code has to stay together # 1960 for pw in InfixParser.piecewises: 1961 formula = formula.replace( 1962 'self.{}'.format(pw), 'self.{}()'.format(pw) 1963 ) 1964 self.__ParsePiecewiseFunctions__(InfixParser.piecewises) 1965 # this code has to stay together # 1966 assignment_rules[ar]['code_string'] = formula 1967 all_names += InfixParser.names 1968 1969 keep = [] 1970 rules = list(assignment_rules.keys()) 1971 dep = rules 1972 while len(dep) > 0: 1973 dep = [] 1974 indep = [] 1975 for ar in rules: 1976 if ar in all_names: 1977 indep.append(ar) 1978 else: 1979 dep.append(ar) 1980 keep += dep 1981 rules = indep 1982 for ar in indep + keep: 1983 evalCode = 'self.{} = {}\n'.format( 1984 assignment_rules[ar]['name'], assignment_rules[ar]['code_string'], 1985 ) 1986 self._NewRuleXCode.update({assignment_rules[ar]['name']: evalCode}) 1987 code_string += evalCode 1988 1989 print('\nAssignment rule(s) detected.') 1990 self._Function_forced = code_string 1991 elif ( 1992 self.__HAS_FORCED_FUNCS__ 1993 and self.__settings__['mode_substitute_assignment_rules'] 1994 ): 1995 # here we substitute nested assignment rules 1996 InfixParser.setNameStr('self.', '') 1997 symbR = {} 1998 for ass in assignment_rules: 1999 InfixParser.setNameStr('self.', '') 2000 InfixParser.parse(assignment_rules[ass]['formula']) 2001 formula = InfixParser.output 2002 # this code has to stay together # 2003 for pw in InfixParser.piecewises: 2004 formula = formula.replace( 2005 'self.{}'.format(pw), 'self.{}()'.format(pw) 2006 ) 2007 self.__ParsePiecewiseFunctions__(InfixParser.piecewises) 2008 # this code has to stay together # 2009 symbR.update({assignment_rules[ass]['name']: formula}) 2010 for ass in assignment_rules: 2011 InfixParser.setNameStr('self.', '') 2012 InfixParser.FunctionReplacements = symbR 2013 InfixParser.parse(assignment_rules[ass]['formula']) 2014 assignment_rules[ass]['code_string'] = InfixParser.output 2015 self._Function_forced = 'pass\n' 2016 print('Assignment rule(s) detected and substituted.') 2017 else: 2018 self._Function_forced = 'pass\n' 2019 2020 self.__CODE_forced = compile(self._Function_forced, 'AssignRules', 'exec') 2021 # tested in InitialiseRuleChecks() 2022 2023 # defmod 2024 self.__rate_rules__ = [] 2025 self.__rrule__ = None 2026 rr_code_block = '' 2027 rr_map_block = '' 2028 self.__CODE_raterule = None 2029 self._NewRateRuleXCode = {} 2030 if self.__HAS_RATE_RULES__: 2031 # create a rr vector 2032 self.__rrule__ = numpy.ones(len(list(rate_rules.keys())), 'd') 2033 # brett2008 debug stuff doesn't do any harm 2034 cntr = 0 2035 rr_keys = list(rate_rules.keys()) 2036 rr_keys.sort() 2037 for ar in rr_keys: 2038 name = rate_rules[ar]['name'] 2039 InfixParser.setNameStr('self.', '') 2040 InfixParser.parse(rate_rules[ar]['formula']) 2041 formula = InfixParser.output 2042 rate_rules[ar]['symbols'] = InfixParser.names 2043 # this code has to stay together # 2044 for pw in InfixParser.piecewises: 2045 formula = formula.replace( 2046 'self.{}'.format(pw), 'self.{}()'.format(pw) 2047 ) 2048 self.__ParsePiecewiseFunctions__(InfixParser.piecewises) 2049 # this code has to stay together # 2050 rate_rules[ar]['code_string'] = formula 2051 self.__rate_rules__.append(name) 2052 rr_code_block += 'self.__rrule__[{}] = {}\n'.format(cntr, formula) 2053 ## rr_code_block += 'self.%s = self.__rrule__[%s]\n' % (name, cntr) 2054 rr_map_block += 'self.{} = self.__rrule__[{}]\n'.format(name, cntr) 2055 cntr += 1 2056 # create mod.<rule name>_init attributes 2057 setattr(self, '{}_init'.format(name), getattr(self, name)) 2058 print('Rate rule(s) detected.') 2059 else: 2060 rr_code_block = 'pass\n' 2061 rr_map_block = 'pass\n' 2062 # TODO consider putting this in self.__HAS_RATE_RULES__ 2063 self.__CODE_raterule = compile(rr_code_block, 'RateRules', 'exec') 2064 self.__CODE_raterule_map = compile(rr_map_block, 'RateRuleMap', 'exec') 2065 2066 del rate_rules, assignment_rules 2067 2068 def InitialiseRuleChecks(self): 2069 try: 2070 exec(self.__CODE_raterule) 2071 except ZeroDivisionError: 2072 print( 2073 'WARNING: Assignment RateRule ZeroDivision on initialisation (continuing)' 2074 ) 2075 except Exception as ex: 2076 print('WARNING: RateRule initialisation error\n', ex) 2077 2078 zeroDivErr = [] 2079 for k in self._NewRuleXCode: 2080 try: 2081 exec(compile(self._NewRuleXCode[k], 'NewRuleXCode', 'exec')) 2082 except Exception as ex: 2083 zeroDivErr.append(k) 2084 exit = len(list(self._NewRuleXCode.keys())) 2085 while len(zeroDivErr) > 0 and exit > 0: 2086 zeroDivErr2 = [] 2087 for kk in zeroDivErr: 2088 try: 2089 exec(compile(self._NewRuleXCode[kk], 'NewRuleXCode', 'exec')) 2090 if self.__HAS_RATE_RULES__: 2091 exec(self.__CODE_raterule) 2092 exec(self.__CODE_raterule_map) 2093 except Exception as ex: 2094 zeroDivErr2.append(kk) 2095 exit -= 1 2096 zeroDivErr = zeroDivErr2 2097 if exit < 0: 2098 print('WARNING: ZeroDivision elimination failed') 2099 2100 try: 2101 exec(self.__CODE_forced) 2102 # print 'done.' 2103 except ZeroDivisionError: 2104 print('WARNING: Assignment rule ZeroDivision on intialisation') 2105 except Exception as ex: 2106 print('WARNING: Assignment rule error (disabling all rules)\n', ex) 2107 self.__CODE_forced = compile('pass\n', 'AssignRules', 'exec') 2108 2109 def Stoichiometry_Init(self, nmatrix, load=0): 2110 """ 2111 Stoichiometry_Init(nmatrix,load=0) 2112 2113 Initialize the model stoichiometry. Given a stoichiometric matrix N, this method will return an instantiated PyscesStoich instance and status flag. Alternatively, if load is enabled, PySCeS will 2114 attempt to load a previously saved stoichiometric analysis (saved with Stoichiometry_Save_Serial) 2115 and test it's correctness. The status flag indicates 0 = reanalyse stoichiometry or 2116 1 = complete structural analysis preloaded. 2117 2118 Arguments: 2119 2120 nmatrix: The input stoichiometric matrix, N 2121 load [default=0]: try to load a saved stoichiometry (1) 2122 2123 """ 2124 if load: 2125 print('Loading saved stoichiometry ...') 2126 try: 2127 stc = self.Stoichiometry_Load_Serial() 2128 go = 1 2129 except Exception as slx: 2130 print(slx) 2131 go = 0 2132 2133 row, col = nmatrix.shape 2134 if go: 2135 badList = [] 2136 for x in range(row): 2137 if stc.__species__[x] != self.__species__[x]: 2138 badList.append((stc.__species__[x], self.__species__[x])) 2139 go = 0 2140 for y in range(col): 2141 if stc.__reactions__[y] != self.__reactions__[y]: 2142 badList.append( 2143 (stc.__reactions__[y], self.__reactions__[y]) 2144 ) 2145 go = 0 2146 if ( 2147 abs(nmatrix[x, y] - stc.nmatrix[x, y]) 2148 > stc.stoichiometric_analysis_fp_zero 2149 ): 2150 badList.append(((x, y), nmatrix[x, y], stc.nmatrix[x, y])) 2151 go = 0 2152 if not go: 2153 ## print 'Stoichiometry mismatch' 2154 ## for x in badList: 2155 ## print x, 2156 print('\nProblem loading stoichiometry, reanalysing ...') 2157 stc = PyscesStoich.Stoich(nmatrix) 2158 status = 0 2159 else: 2160 print('Stoichiometry verified ... we have liftoff') 2161 status = 1 2162 else: 2163 # print 'Instantiating new stoichiometry ...' 2164 stc = PyscesStoich.Stoich(nmatrix) 2165 status = 0 2166 return stc, status 2167 2168 def Stoichiometry_Save_Serial(self): 2169 """Serialize and save a Stoichiometric instance to binary pickle""" """ 2170 Stoichiometry_Save_Serial() 2171 2172 Serilaise and save the current model stoichiometry to a file with name <model>_stoichiometry.pscdat 2173 in the mod.__settings__['serial_dir'] directory (default: mod.model_output/pscdat) 2174 2175 Arguments: 2176 None 2177 2178 """ 2179 # new plan, I introduce species and reaction arrays into the stoich instance for verification purposes 2180 # brett - 20050831 2181 self.__structural__.__species__ = copy.copy(self.__species__) 2182 self.__structural__.__reactions__ = copy.copy(self.__reactions__) 2183 self.SerialEncode(self.STOICH, self.ModelFile[:-4] + '_stoichiometry') 2184 2185 def Stoichiometry_Load_Serial(self): 2186 """ 2187 Stoichiometry_Load_Serial() 2188 2189 Load a saved stoichiometry saved with mod.Stoichiometry_Save_Serial() and return 2190 a stoichiometry instance. 2191 2192 Arguments: 2193 None 2194 2195 """ 2196 stc = self.SerialDecode(self.ModelFile[:-4] + '_stoichiometry') 2197 return stc 2198 2199 def Stoichiometry_Analyse(self, override=0, load=0): 2200 """ 2201 Stoichiometry_Analyse(override=0,load=0) 2202 2203 Perform a structural analyses. The default behaviour is to construct and analyse the model 2204 from the parsed model information. Overriding this behaviour analyses the stoichiometry 2205 based on the current stoichiometric matrix. If load is specified PySCeS tries to load a 2206 saved stoichiometry, otherwise the stoichiometric analysis is run. The results of 2207 the analysis are checked for floating point error and nullspace rank consistancy. 2208 2209 Arguments: 2210 2211 override [default=0]: override stoichiometric analysis intialisation from parsed data 2212 load [default=0]: load a presaved stoichiometry 2213 2214 """ 2215 if not override: 2216 self.__nmatrix__ = self.__initmodel__() # Creates the model N 2217 # print '\nintializing N\n' 2218 else: 2219 print('\nStoichiometric override active\n') 2220 2221 assert ( 2222 len(self.__nmatrix__) > 0 2223 ), '\nUnable to generate Stoichiometric Matrix! model has:\n{} reactions\n{} species\nwhat did you have in mind?\n'.format( 2224 len(self.__reactions__), len(self.__species__) 2225 ) 2226 2227 self.__Nshape__ = self.__nmatrix__.shape # Get the shape of N 2228 ## self.__Vtemp__ = numpy.zeros((self.__Nshape__[1])) # going going .... 2229 2230 # get stoich instance and whether it was analysed or loaded - brett 20050830 2231 self.__structural__, stc_load = self.Stoichiometry_Init( 2232 self.__nmatrix__, load=load 2233 ) 2234 2235 # if not loaded analyze - brett 20050830 2236 if not stc_load: 2237 # technically this means we can define this on the fly - brett #20051013 2238 self.__structural__.stoichiometric_analysis_fp_zero = self.__settings__[ 2239 'stoichiometric_analysis_fp_zero' 2240 ] 2241 self.__structural__.stoichiometric_analysis_lu_precision = self.__settings__[ 2242 'stoichiometric_analysis_lu_precision' 2243 ] 2244 self.__structural__.stoichiometric_analysis_gj_precision = self.__settings__[ 2245 'stoichiometric_analysis_gj_precision' 2246 ] 2247 self.__structural__.AnalyseL() # Get all L related stuff 2248 self.__structural__.AnalyseK() # Get all K related stuff 2249 2250 # test matrix values against __settings__['stoichiometric_analysis_lu_precision'] 2251 lsmall, lbig = self.__structural__.MatrixValueCompare( 2252 self.__structural__.lzeromatrix 2253 ) 2254 ksmall, kbig = self.__structural__.MatrixValueCompare( 2255 self.__structural__.kzeromatrix 2256 ) 2257 SmallValueError = 0 2258 if ( 2259 abs(lsmall) 2260 < self.__structural__.stoichiometric_analysis_lu_precision * 10.0 2261 ): 2262 print( 2263 '\nWARNING: values in L0matrix are close to stoichiometric precision!' 2264 ) 2265 print( 2266 'Stoichiometric LU precision:', 2267 self.__structural__.stoichiometric_analysis_lu_precision, 2268 ) 2269 print('L0 smallest abs(value)', abs(lsmall)) 2270 print('Machine precision:', mach_spec.eps) 2271 SmallValueError = 1 2272 if ( 2273 abs(ksmall) 2274 < self.__structural__.stoichiometric_analysis_lu_precision * 10.0 2275 ): 2276 print( 2277 '\nWARNING: values in K0matrix are close to stoichiometric precision!' 2278 ) 2279 print( 2280 'Stoichiometric precision:', 2281 self.__structural__.stoichiometric_analysis_lu_precision, 2282 ) 2283 print('K0 smallest abs(value)', abs(ksmall)) 2284 print('Machine precision:', mach_spec.eps) 2285 SmallValueError = 1 2286 if SmallValueError: 2287 input( 2288 '\nStructural Analysis results may not be reliable!!!.\n\nTry change <mod>.__settings__["stoichiometric_analysis_lu_precision"] (see reference manual for details)\n\n\t press any key to continue: ' 2289 ) 2290 2291 # cross check that rank is consistant between K0 and L0 2292 if ( 2293 self.__structural__.kzeromatrix.shape[0] 2294 != self.__structural__.lzeromatrix.shape[1] 2295 ): 2296 print( 2297 '\nWARNING: the rank calculated by the Kand L analysis methods are not the same!' 2298 ) 2299 print( 2300 '\tK analysis calculates the rank as: ' 2301 + repr(self.__structural__.kzeromatrix.shape[0]) 2302 ) 2303 print( 2304 '\tL analysis calculates the rank as: ' 2305 + repr(self.__structural__.lzeromatrix.shape[1]) 2306 ) 2307 print('This is not good! Structural Analysis results are not reliable!!!\n') 2308 assert ( 2309 self.__structural__.kzeromatrix.shape[0] 2310 == self.__structural__.lzeromatrix.shape[1] 2311 ), '\nStructuralAnalysis Error: rank mismatch' 2312 2313 self.__HAS_FLUX_CONSERVATION__ = self.__structural__.info_flux_conserve 2314 self.__HAS_MOIETY_CONSERVATION__ = self.__structural__.info_moiety_conserve 2315 2316 if self.__settings__['enable_deprecated_attr']: 2317 ## self.__nmatrix__ = copy.copy(self.nmatrix) 2318 self.nmatrix = self.__nmatrix__ # done with caution brett2008 2319 self.nmatrix_row = self.__structural__.nmatrix_row 2320 self.nmatrix_col = self.__structural__.nmatrix_col 2321 2322 self.kmatrix = self.__structural__.kmatrix 2323 self.kmatrix_row = self.__structural__.kmatrix_row 2324 self.kmatrix_col = self.__structural__.kmatrix_col 2325 self.kzeromatrix = self.__structural__.kzeromatrix 2326 self.kzeromatrix_row = self.__structural__.kzeromatrix_row 2327 self.kzeromatrix_col = self.__structural__.kzeromatrix_col 2328 2329 self.lmatrix = self.__structural__.lmatrix 2330 self.lmatrix_row = self.__structural__.lmatrix_row 2331 self.lmatrix_col = self.__structural__.lmatrix_col 2332 self.lzeromatrix = self.__structural__.lzeromatrix 2333 self.lzeromatrix_row = self.__structural__.lzeromatrix_row 2334 self.lzeromatrix_col = self.__structural__.lzeromatrix_col 2335 self.conservation_matrix = self.__structural__.conservation_matrix 2336 self.conservation_matrix_row = self.__structural__.conservation_matrix_row 2337 self.conservation_matrix_col = self.__structural__.conservation_matrix_col 2338 self.nrmatrix = self.__structural__.nrmatrix 2339 self.nrmatrix_row = self.__structural__.nrmatrix_row 2340 self.nrmatrix_col = self.__structural__.nrmatrix_col 2341 2342 self.__kmatrix__ = copy.copy(self.kmatrix) 2343 self.__kzeromatrix__ = copy.copy(self.kzeromatrix) 2344 self.__lmatrix__ = copy.copy(self.lmatrix) 2345 self.__lzeromatrix__ = copy.copy(self.lzeromatrix) 2346 self.__nrmatrix__ = copy.copy(self.nrmatrix) 2347 # switch that is set if the stoichiometric analysis is up to date 2348 2349 self.__structural__.species = self.species 2350 self.__structural__.reactions = self.reactions 2351 self.Nmatrix = PyscesStoich.StructMatrix( 2352 self.__structural__.nmatrix, 2353 self.__structural__.nmatrix_row, 2354 self.__structural__.nmatrix_col, 2355 ) 2356 self.Nmatrix.setRow(self.species) 2357 self.Nmatrix.setCol(self.reactions) 2358 2359 self.Nrmatrix = PyscesStoich.StructMatrix( 2360 self.__structural__.nrmatrix, 2361 self.__structural__.nrmatrix_row, 2362 self.__structural__.nrmatrix_col, 2363 ) 2364 self.Nrmatrix.setRow(self.species) 2365 self.Nrmatrix.setCol(self.reactions) 2366 2367 self.Kmatrix = PyscesStoich.StructMatrix( 2368 self.__structural__.kmatrix, 2369 self.__structural__.kmatrix_row, 2370 self.__structural__.kmatrix_col, 2371 ) 2372 self.Kmatrix.setRow(self.reactions) 2373 self.Kmatrix.setCol(self.reactions) 2374 2375 self.K0matrix = PyscesStoich.StructMatrix( 2376 self.__structural__.kzeromatrix, 2377 self.__structural__.kzeromatrix_row, 2378 self.__structural__.kzeromatrix_col, 2379 ) 2380 self.K0matrix.setRow(self.reactions) 2381 self.K0matrix.setCol(self.reactions) 2382 2383 self.Lmatrix = PyscesStoich.StructMatrix( 2384 self.__structural__.lmatrix, 2385 self.__structural__.lmatrix_row, 2386 self.__structural__.lmatrix_col, 2387 ) 2388 self.Lmatrix.setRow(self.species) 2389 self.Lmatrix.setCol(self.species) 2390 2391 self.L0matrix = PyscesStoich.StructMatrix( 2392 self.__structural__.lzeromatrix, 2393 self.__structural__.lzeromatrix_row, 2394 self.__structural__.lzeromatrix_col, 2395 ) 2396 self.L0matrix.setRow(self.species) 2397 self.L0matrix.setCol(self.species) 2398 2399 if self.__structural__.info_moiety_conserve: 2400 self.Consmatrix = PyscesStoich.StructMatrix( 2401 self.__structural__.conservation_matrix, 2402 self.__structural__.conservation_matrix_row, 2403 self.__structural__.conservation_matrix_col, 2404 ) 2405 self.Consmatrix.setRow(self.species) 2406 self.Consmatrix.setCol(self.species) 2407 else: 2408 self.Consmatrix = None 2409 self.__StoichOK = 1 2410 print(' ') 2411 2412 def Stoichiometry_ReAnalyse(self): 2413 """ 2414 Stoichiometry_ReAnalyse() 2415 2416 Reanalyse the stoichiometry using the current N matrix ie override=1 2417 (for use with mod.Stoich_matrix_SetValue) 2418 2419 Arguments: 2420 None 2421 2422 """ 2423 self.Stoichiometry_Analyse(override=1) 2424 self.InitialiseConservationArrays() 2425 2426 def Stoich_nmatrix_SetValue(self, species, reaction, value): 2427 """ 2428 Stoich_nmatrix_SetValue(species,reaction,value) 2429 2430 Change a stoichiometric coefficient's value in the N matrix. Only a coefficients magnitude may be set, in other words a 2431 a coefficient's value must remain negative, positive or zero. After changing a coefficient 2432 it is necessary to Reanalyse the stoichiometry. 2433 2434 Arguments: 2435 2436 species: species name (s0) 2437 reaction: reaction name (R4) 2438 value: new coefficient value 2439 2440 """ 2441 index = self.__Stoich_nmatrix_FindIndex__(species, reaction) 2442 if self.__Stoich_nmatrix_CheckValue__(index, value): 2443 self.__Stoich_nmatrix_UpdateValue__(index, value) 2444 self.__StoichOK = 0 2445 2446 def __Stoich_nmatrix_FindIndex__(self, species, reaction): 2447 """ 2448 __Stoich_nmatrix_FindIndex__(species,reaction) 2449 2450 Return the N matrix co-ordinates of the coefficient referenced by species and reaction name. 2451 2452 Arguments: 2453 2454 species: species name 2455 reaction: reaction name 2456 2457 """ 2458 rval = None 2459 cval = None 2460 for x in enumerate(self.species): 2461 if species == x[1]: 2462 rval = x[0] 2463 for y in enumerate(self.reactions): 2464 if reaction == y[1]: 2465 cval = y[0] 2466 return (rval, cval) 2467 2468 def __Stoich_nmatrix_UpdateValue__(self, xxx_todo_changeme, val): 2469 """ 2470 __Stoich_nmatrix_UpdateValue__((x,y), val) 2471 2472 Update N matrix co-ordinates (x,y) with val 2473 2474 Arguments: 2475 (x,y): row, column coordinates 2476 val: value 2477 2478 """ 2479 (x, y) = xxx_todo_changeme 2480 self.nmatrix[x, y] = val 2481 self.__nmatrix__[x, y] = val 2482 self.Nmatrix.array[x, y] = val 2483 2484 def __Stoich_nmatrix_CheckValue__(self, xxx_todo_changeme1, val): 2485 """ 2486 __Stoich_nmatrix_CheckValue__((x,y),val) 2487 2488 Check validity of new coefficient value against existing N matrix coefficient (x,y) for existance and/or sign. 2489 Returns 1 (true) or 0. 2490 2491 Arguments: 2492 (x,y): N matrix coordinates 2493 val: new value 2494 2495 """ 2496 (x, y) = xxx_todo_changeme1 2497 go = 1 2498 if x == None or y == None: 2499 print('\nSpecies (nmatrix) index =', x) 2500 print('Reaction (nmatrix) index =', y) 2501 print( 2502 '\nI\'m confused, perhaps you entered an incorrect species or reaction name' 2503 ) 2504 print('or they are in the wrong order?') 2505 go = 0 2506 elif abs(self.__nmatrix__[x, y]) == 0.0: 2507 if val != 0.0: 2508 go = 0 2509 print('\nZero coefficient violation') 2510 print( 2511 ' nmatrix[' 2512 + repr(x) 2513 + ',' 2514 + repr(y) 2515 + '] can only be = 0.0 (input ' 2516 + str(val) 2517 + ')' 2518 ) 2519 elif self.__nmatrix__[x, y] > 0.0: 2520 if val <= 0.0: 2521 go = 0 2522 print('\nPositive coefficient violation') 2523 print( 2524 ' nmatrix[' 2525 + repr(x) 2526 + ',' 2527 + repr(y) 2528 + '] can only be > 0.0 (input ' 2529 + str(val) 2530 + ')' 2531 ) 2532 elif self.__nmatrix__[x, y] < 0.0: 2533 if val >= 0.0: 2534 go = 0 2535 print('\nNegative coefficient violation') 2536 print( 2537 ' nmatrix[' 2538 + repr(x) 2539 + ',' 2540 + repr(y) 2541 + '] can only be < 0.0 (input ' 2542 + str(val) 2543 + ')' 2544 ) 2545 if go: 2546 return 1 2547 else: 2548 return 0 2549 2550 def SerialEncode(self, data, filename): 2551 """ 2552 SerialEncode(data,filename) 2553 2554 Serialise and save a Python object using a binary pickle to file. The serialised object 2555 is saved as <filename>.pscdat in the directory defined by mod.model_serial. 2556 2557 Arguments: 2558 2559 data: pickleable Python object 2560 filename: the ouput filename 2561 2562 """ 2563 filename = str(filename) + '.pscdat' 2564 if os.path.exists(self.__settings__['serial_dir']): 2565 pass 2566 else: 2567 os.mkdir(self.__settings__['serial_dir']) 2568 print('\ncPickle data stored in: ' + self.__settings__['serial_dir']) 2569 2570 File = open(os.path.join(self.__settings__['serial_dir'], filename), 'wb') 2571 try: 2572 pickle.dump(data, File, -1) 2573 print('Serialization complete') 2574 except Exception as E: 2575 print('Serialize error:\n', E) 2576 File.flush() 2577 File.close() 2578 del data 2579 2580 def SerialDecode(self, filename): 2581 """ 2582 SerialDecode(filename) 2583 2584 Decode and return a serialised object saved with SerialEncode. 2585 2586 Arguments: 2587 2588 filename: the filename (.pscdat is assumed) 2589 2590 """ 2591 filename = str(filename) + '.pscdat' 2592 if not os.path.exists(os.path.join(self.__settings__['serial_dir'], filename)): 2593 raise RuntimeError( 2594 'Serialized data ' 2595 + os.path.join(self.__settings__['serial_dir'], filename) 2596 + ' does not exist' 2597 ) 2598 data = None 2599 try: 2600 File = open(os.path.join(self.__settings__['serial_dir'], filename), 'rb') 2601 data = pickle.load(File) 2602 File.close() 2603 print('Serial decoding complete') 2604 except Exception as E: 2605 print('Serial decode error:\n', E) 2606 2607 return data 2608 2609 def InitialiseCompartments(self): 2610 # the name should say it all 2611 self.__settings__['compartment_fudge_factor'] = 1.0 2612 startFudging = 1.0e-8 2613 I_AM_FUDGING = False 2614 if self.__HAS_COMPARTMENTS__: 2615 tmp = min( 2616 [ 2617 abs(self.__compartments__[c]['size']) 2618 for c in list(self.__compartments__.keys()) 2619 ] 2620 ) 2621 if tmp < startFudging: 2622 ## self.__settings__['compartment_fudge_factor'] = 10.0**round(numpy.log10(tmp)) 2623 self.__settings__[ 2624 'compartment_fudge_factor' 2625 ] = tmp # sneaky b*))_)$^&*@rds 2626 ## print 'compartment_fudge_factor', self.__settings__['compartment_fudge_factor'] 2627 for c in list(self.__compartments__.keys()): 2628 if self.__settings__['compartment_fudge_factor'] < startFudging: 2629 newsize = ( 2630 self.__compartments__[c]['size'] 2631 / self.__settings__['compartment_fudge_factor'] 2632 ) 2633 print( 2634 'INFO: Rescaling compartment with size {} to {}'.format( 2635 self.__compartments__[c]['size'], newsize 2636 ) 2637 ) 2638 self.__compartments__[c]['size'] = newsize 2639 self.__compartments__[c].update( 2640 {'scale': self.__settings__['compartment_fudge_factor']} 2641 ) 2642 I_AM_FUDGING = True 2643 setattr( 2644 self, 2645 self.__compartments__[c]['name'], 2646 self.__compartments__[c]['size'], 2647 ) 2648 setattr( 2649 self, 2650 '{}_init'.format(self.__compartments__[c]['name']), 2651 self.__compartments__[c]['size'], 2652 ) 2653 if self.__HAS_COMPARTMENTS__: 2654 for sp in list(self.__sDict__.keys()): 2655 if ( 2656 self.__sDict__[sp]['compartment'] == None 2657 and len(list(self.__compartments__.keys())) == 1 2658 ): 2659 self.__sDict__[sp]['compartment'] = self.__compartments__[ 2660 list(self.__compartments__.keys())[0] 2661 ]['name'] 2662 print( 2663 'COMPARTMENT WARNING: this model has a compartment defined {} but \"{}\" is not in it ... I\'ll try it for now'.format( 2664 [ 2665 self.__compartments__[c]['name'] 2666 for c in list(self.__compartments__.keys()) 2667 ], 2668 sp, 2669 ) 2670 ) 2671 ## print self.__sDict__[sp] 2672 elif ( 2673 self.__sDict__[sp]['compartment'] == None 2674 and len(list(self.__compartments__.keys())) > 1 2675 ): 2676 assert ( 2677 self.__sDict__[sp]['compartment'] != None 2678 ), '\nCOMPARTMENT ERROR: this model has multiple compartments defined {} but \"{}\" is not in one!'.format( 2679 [ 2680 self.__compartments__[c]['name'] 2681 for c in list(self.__compartments__.keys()) 2682 ], 2683 sp, 2684 ) 2685 # brett 2008 fudge this! 2686 if not self.__KeyWords__['Species_In_Conc'] and I_AM_FUDGING: 2687 self.__sDict__[sp]['initial'] = ( 2688 self.__sDict__[sp]['initial'] 2689 / self.__settings__['compartment_fudge_factor'] 2690 ) 2691 setattr(self, sp, self.__sDict__[sp]['initial']) 2692 setattr(self, '{}_init'.format(sp), self.__sDict__[sp]['initial']) 2693 print( 2694 'INFO: Rescaling species ({}) to size {}.'.format( 2695 sp, self.__sDict__[sp]['initial'] 2696 ) 2697 ) 2698 2699 self.__CsizeAllIdx__ = [] 2700 self.__null_compartment__ = 1.0 2701 uses_null_compartments = False 2702 for s in self.__species__: 2703 if self.__HAS_COMPARTMENTS__: 2704 self.__CsizeAllIdx__.append(self.__sDict__[s]['compartment']) 2705 else: 2706 self.__CsizeAllIdx__.append('__null_compartment__') 2707 uses_null_compartments = True 2708 if uses_null_compartments: 2709 setattr(self, '__null_compartment___init', 1.0) 2710 ## self.__CsizeFSIdx__ = [] 2711 ## for s in self.__fixed_species__: 2712 ## if self.__HAS_COMPARTMENTS__: 2713 ## self.__CsizeFSIdx__.append(self.__sDict__[s]['compartment']) 2714 ## else: 2715 ## self.__CsizeFSIdx__.append('__null_compartment__') 2716 2717 # Init compartment divisions vector - brett 2008 2718 ## self.__CsizeAll__ = numpy.ones(len(self.__CsizeAllIdx__), 'd') 2719 if self.__HAS_COMPARTMENTS__: 2720 self.__CsizeAll__ = numpy.array( 2721 [self.__compartments__[c]['size'] for c in self.__CsizeAllIdx__], 'd' 2722 ) 2723 else: 2724 self.__CsizeAll__ = numpy.ones(len(self.__CsizeAllIdx__), 'd') 2725 ## self.__CsizeFS__ = numpy.array([self.__compartments__[c]['size'] for c in self.__CsizeFSIdx__], 'd') 2726 2727 # add new function types to model 2728 def InitialiseFunctions(self): 2729 for func in self.__functions__: 2730 F = Function(func, self) 2731 for arg in self.__functions__[func]['args']: 2732 F.setArg(arg.strip()) 2733 F.addFormula(self.__functions__[func]['formula']) 2734 setattr(self, func, F) 2735 for func in self.__functions__: 2736 fobj = getattr(self, func) 2737 for f in fobj.functions: 2738 if f in self.__functions__: 2739 setattr(fobj, f, getattr(self, f)) 2740 2741 # add event types to model 2742 def InitialiseEvents(self): 2743 # return all event time points, even if not in self.sim_time 2744 self.__settings__['cvode_return_event_timepoints'] = True 2745 self.__events__ = [] 2746 # for each event 2747 for e in self.__eDict__: 2748 ev = Event(e, self) 2749 ev._time_symbol = self.__eDict__[e]['tsymb'] 2750 ev.setTrigger(self.__eDict__[e]['trigger'], self.__eDict__[e]['delay']) 2751 # for each assignment 2752 for ass in self.__eDict__[e]['assignments']: 2753 ev.setAssignment(ass, self.__eDict__[e]['assignments'][ass]) 2754 self.__events__.append(ev) 2755 setattr(self, ev.name, ev) 2756 if len(self.__events__) > 0: 2757 self.__HAS_EVENTS__ = True 2758 print('Event(s) detected.') 2759 else: 2760 self.__HAS_EVENTS__ = False 2761 2762 def InitialiseModel(self): 2763 """ 2764 InitialiseModel() 2765 2766 Initialise and set up dynamic model attributes and methods using the model defined in 2767 the associated PSC file 2768 2769 Arguments: 2770 None 2771 2772 """ 2773 # TODO killkillkill 2774 self.__inspec__ = numpy.zeros((len(self.__species__)), 'd') 2775 self._D_s_Order, DvarUpString = self.__initvar__() # Initialise s[] array 2776 2777 self.__CDvarUpString__ = compile(DvarUpString, 'DvarUpString', 'exec') 2778 2779 self.state_species = None 2780 self.state_flux = None 2781 ## self.sim_res = None 2782 self.elas_var_u = None 2783 self.elas_var = None 2784 self.__vvec__ = numpy.zeros((self.__Nshape__[1]), 'd') 2785 self.__tvec_a__ = None 2786 self.__tvec_c__ = None 2787 2788 self._CVODE_Vtemp = numpy.zeros((self.__Nshape__[1]), 'd') 2789 2790 # #debug 2791 # self.display_debugcount = 0 2792 # self.cntr = 50 2793 2794 par_map2store, par_remap = self.__initfixed__() # Initialise fixed species 2795 2796 # self.__par_map2storeC = par_map2store 2797 # self.__par_remapC = par_remap 2798 self.__par_map2storeC = compile(par_map2store, 'par_map2store', 'exec') 2799 self.__par_remapC = compile(par_remap, 'par_remap', 'exec') 2800 2801 ## self.__xMake = compile(xString,'xString','exec') 2802 ## exec(self.__xMake) 2803 2804 # initialise rate equations 2805 mapString, mapString_R, vString = self.__initREQ__() 2806 2807 self.__mapFunc__ = compile(mapString, 'mapString', 'exec') 2808 self.__mapFunc_R__ = compile(mapString_R, 'mapString_R', 'exec') 2809 self.__vFunc__ = compile(vString, 'vString', 'exec') 2810 ## exec(lambdaFuncsC) 2811 2812 # Machine specific values (for IEEE compliant FP machines this is around 2.e-16) 2813 # FutureNote: investigate arbitrary precision FP in Python, probably GNU-GMP based - brett 20040326 2814 self.__settings__['mach_floateps'] = mach_spec.eps 2815 2816 # PySCeS mode switches 2817 # this affects number output formatting in PySCeS) 2818 self.__settings__['mode_number_format'] = '%2.4e' 2819 # 0:initval, 1:zeroval, 2:lastss 3:sim_res[-1] 2820 self.__settings__['mode_sim_init'] = 0 2821 # maximum number of auto-stepsize adjustments 2822 self.__settings__['mode_sim_max_iter'] = 3 2823 2824 # TODO UPGRADE 2825 self.mode_state_init = 0 # 0:initval, 1:zeroval, 2:sim, 3:%state 2826 self.mode_solver = 'HYBRD' # 0:HYBRD, 1:FINTSLV, 2:NLEQ2 2827 self.mode_solver_fallback = 1 # 0:Solver failure fails, 2828 # 1:solver failure falls back to NLEQ2 if present then FINTSLV (see next) 2829 self.STATE_extra_output = [] # extra data added to data_sstate object 2830 # factor to multiply the previous ss used as initialiser 2831 self.__settings__['mode_state_init3_factor'] = 0.1 2832 # the simulation array used to initialise the steady state 2833 self.__mode_state_init2_array__ = numpy.logspace(0, 5, 18) 2834 self.__settings__['mode_state_mesg'] = 1 # happy exit message from State() 2835 # give last best solutions or NaN as solution 2836 self.__settings__['mode_state_nan_on_fail'] = False 2837 # the irritating switching to message 2838 self.__settings__['solver_switch_warning'] = True 2839 # 0:no fallback to forward integration 1:fallback to integration 2840 self.__settings__['mode_solver_fallback_integration'] = 1 2841 # this is now initialised in the model parsing sectiomn ParseModel 2842 # the order of ScipyDerivative - 3 seems good for most situations 2843 self.__settings__['mode_elas_deriv_order'] = 3 2844 # MCA scaling option 0:unscaled E+C+Eig 1:scaled E+C+Eig 2845 self.__settings__['mode_mca_scaled'] = 1 2846 # 0:normal, 1:extended eigen value + left/right vectors 2847 self.__settings__['mode_eigen_output'] = 0 2848 # under consideration 2849 # 0:warnings displayed, 1:warnings suppressed 2850 self.__settings__['mode_suppress_info'] = 0 2851 # new compartment stuff (using dictionary directly) - brett 2008 2852 2853 ## self.Species_In_Conc = False 2854 2855 # misc settings 2856 # write an id header before the array 2857 self.__settings__['write_array_header'] = 1 2858 # write a empty line before the array 2859 self.__settings__['write_array_spacer'] = 1 2860 self.__settings__['write_array_html_header'] = 1 # write html page header 2861 self.__settings__['write_array_html_footer'] = 1 # write html page footer 2862 self.__settings__['write_array_html_format'] = '%2.4f' # html number format 2863 # lines to write before flushing to disk 2864 self.__settings__['write_arr_lflush'] = 5 2865 2866 # Hybrd options (zero value means routine decides) 2867 self.__settings__['hybrd_xtol'] = 1.0e-12 # relative error tolerance 2868 # Maximum number of calls, zero means then 100*(len(species)+1) 2869 self.__settings__['hybrd_maxfev'] = 0 2870 # A suitable step length for the forward-difference approximation of the Jacobian 2871 self.__settings__['hybrd_epsfcn'] = copy.copy( 2872 self.__settings__['mach_floateps'] 2873 ) 2874 # A parameter determining the initial step bound in interval (0.1,100) 2875 self.__settings__['hybrd_factor'] = 100 2876 self.__settings__['hybrd_mesg'] = 1 # print the exit status message 2877 2878 # Finstslv options (forward integration solver) - brett (20030331) 2879 # max allowed deviation between max(sim_res) to be a steady state 2880 self.__settings__['fintslv_tol'] = 1.0e-3 2881 # threshold number of steps where deviation < atol to be declared a steady state 2882 self.__settings__['fintslv_step'] = 5 2883 # a "saturation" type range - should be ok for most systems 2884 self.__fintslv_range__ = numpy.array( 2885 [ 2886 1, 2887 10, 2888 100, 2889 1000, 2890 5000, 2891 10000, 2892 50000, 2893 50100, 2894 50200, 2895 50300, 2896 50400, 2897 50500, 2898 50600, 2899 50700, 2900 50800, 2901 50850, 2902 50900, 2903 50950, 2904 51000, 2905 ], 2906 'd', 2907 ) 2908 # self.__fintslv_range__ = numpy.array([1,10,100,1000,5000,10000,50000,100000,500000,1000000,1000100, 1000200,1000300,1000400,1000500,1000600,1000700,1000800],'d') 2909 self.__settings__['fintslv_rmult'] = 1.0 2910 2911 # NLEQ2 options (optional non-linear solver) - brett (20030331) 2912 # IOPT 2913 # use advanced NLEQ2 features ... may speedup some parameter scans 2914 self.__settings__['nleq2_advanced_mode'] = False 2915 # nitmax growth factor per nleq2 iteration 2916 self.__settings__['nleq2_growth_factor'] = 10 2917 # number of itertions to loop the solver through (2 should almost always be sufficient) 2918 self.__settings__['nleq2_iter'] = 3 2919 # maximum numbe rof iterations in advanced mode 2920 self.__settings__['nleq2_iter_max'] = 10 2921 2922 self.__settings__['nleq2_rtol'] = 1.0e-8 # automatically calculated by nleq12 2923 self.__settings__['nleq2_jacgen'] = 2 # 2:numdiff, 3:numdiff+feedback 2924 # 0:xscal lower thresholdof scaling vector, 1:always scaling vector 2925 self.__settings__['nleq2_iscaln'] = 0 2926 # Dangerous anything not zero seqfaults 2927 ## self.__settings__['nleq2_mprerr'] = 1 # 0:no output, 1:error, 2:+warning, 3:+info 2928 # 1:linear, 2:mildly non-lin, 3:highly non-lin, 4:extremely non-lin 2929 self.__settings__['nleq2_nonlin'] = 4 2930 # 0:no Broyden approx. rank-1 updates, 1:Broyden approx. rank-1 updates 2931 self.__settings__['nleq2_qrank1'] = 1 2932 # 0:Automatic row scaling is active, 1:inactive 2933 self.__settings__['nleq2_qnscal'] = 0 2934 # 0:auto damping strategy, 1:damping on, 2:damping off 2935 self.__settings__['nleq2_ibdamp'] = 0 2936 # on non-superlinear convergance nitmax *= this (ierr=5) 2937 self.__settings__['nleq2_nitmax_growth_factor'] = 4 2938 # 0:default(2) 1:convergance not checked, 2:+'weak stop', 3:+'hard stop' criterion 2939 self.__settings__['nleq2_iormon'] = 2 2940 self.__settings__['nleq2_mesg'] = 1 # print the exit status message 2941 # TODO: 2942 self.nleq2_nitmax = 50 # optimized and self adjusting :-) 2943 2944 # Other SteadyState options 2945 # the initial values of 1:zeroval smaller than 1.0e-6 sometimes give problems 2946 self.__settings__['small_concentration'] = 1.0e-6 2947 ## self.__state_set_conserve__ = 1 2948 self.__StateOK__ = True 2949 2950 self.data_sstate = None 2951 self._sim = None # new object for recarray with simulation results 2952 self.data_sim = None # the new integration results data object 2953 self.data_stochsim = None # the new stochastic integration results data object 2954 self.__scan2d_results__ = None # yaaaay gnuplot is back 2955 self.__scan2d_pars__ = None 2956 2957 # Simulate/lsoda options (zero value means routine decides) 2958 # The input parameters rtol and atol determine the error 2959 self.__settings__['lsoda_atol'] = 1.0e-12 2960 # control performed by the solver. -- johann 20050217 changed from 1e-5 2961 self.__settings__['lsoda_rtol'] = 1.0e-7 2962 # maximum number (internally defined) steps allowed per point. 0: x <= 500 2963 self.__settings__["lsoda_mxstep"] = 0 2964 # the step size to be attempted on the first step. 2965 self.__settings__["lsoda_h0"] = 0.0 2966 self.__settings__["lsoda_hmax"] = 0.0 # the maximum absolute step size allowed. 2967 self.__settings__["lsoda_hmin"] = 0.0 # the minimum absolute step size allowed. 2968 # maximum order to be allowed for the nonstiff (Adams) method. 2969 self.__settings__["lsoda_mxordn"] = 12 2970 # maximum order to be allowed for the stiff (BDF) method. 2971 self.__settings__["lsoda_mxords"] = 5 2972 self.__settings__["lsoda_mesg"] = 1 # print the exit status message 2973 2974 # try to select the best integration algorithm 2975 self.mode_integrator = 'LSODA' # LSODA/CVODE set the intgration algorithm 2976 if self.__HAS_EVENTS__: 2977 if _HAVE_ASSIMULO: 2978 print( 2979 '\nINFO: events detected and we have Assimulo installed,\n\ 2980switching to CVODE (mod.mode_integrator=\'CVODE\').' 2981 ) 2982 self.mode_integrator = 'CVODE' 2983 else: 2984 print( 2985 '\nWARNING: PySCeS needs Assimulo installed for event handling,\n\ 2986PySCeS will continue with LSODA (NOTE: ALL EVENTS WILL BE IGNORED!).' 2987 ) 2988 print( 2989 'Assimulo may be installed from conda-forge or compiled from source.\n\ 2990See: https://jmodelica.org/assimulo' 2991 ) 2992 self.__events__ = [] 2993 # if self.__HAS_RATE_RULES__ or self.__HAS_COMPARTMENTS__: 2994 if self.__HAS_RATE_RULES__: 2995 if _HAVE_ASSIMULO: 2996 print( 2997 'INFO: RateRules detected and Assimulo installed,\n\ 2998switching to CVODE (mod.mode_integrator=\'CVODE\').\n' 2999 ) 3000 self.mode_integrator = 'CVODE' 3001 else: 3002 print( 3003 '\nWARNING: RateRules detected! PySCeS prefers CVODE but will continue with LSODA\n\ 3004(NOTE: VARIABLE COMPARTMENTS ARE NOT SUPPORTED WITH LSODA!)' 3005 ) 3006 print( 3007 'Assimulo may be installed from conda-forge or compiled from source.\n\ 3008See: https://jmodelica.org/assimulo' 3009 ) 3010 3011 # CVode options 3012 self.mode_integrate_all_odes = False # only available with CVODE 3013 self.__settings__["cvode_abstol"] = 1.0e-15 # absolute tolerance 3014 # self.__settings__["cvode_abstol_max"] = 1.0e-3 # not used anymore 3015 # self.__settings__["cvode_abstol_factor"] = 1.0e-6 # not used in Assimulo 3016 self.__settings__["cvode_reltol"] = 1.0e-9 # relative tolerance 3017 # self.__settings__["cvode_auto_tol_adjust"] = True # not used in Assimulo 3018 self.__settings__["cvode_mxstep"] = 1000 # max step default 3019 # print some pretty stuff after a simulation 3020 self.__settings__["cvode_stats"] = False 3021 3022 if self.__HAS_PIECEWISE__ and self.__settings__["cvode_reltol"] <= 1.0e-9: 3023 self.__settings__["cvode_reltol"] = 1.0e-6 3024 print( 3025 'INFO: Piecewise functions detected increasing CVODE tolerance slightly\n\ 3026(mod.__settings__[\"cvode_reltol\"] = 1.0e-9 ).' 3027 ) 3028 3029 # Normal simulation options 3030 self.sim_start = 0.0 3031 self.sim_end = 10.0 3032 self.sim_points = 20.0 3033 sim_steps = (self.sim_end - self.sim_start) / self.sim_points 3034 self.sim_time = numpy.arange( 3035 self.sim_start, self.sim_end + sim_steps, sim_steps 3036 ) 3037 self.CVODE_extra_output = [] 3038 self.CVODE_xdata = None 3039 self.__SIMPLOT_OUT__ = [] 3040 3041 # elasticity options 3042 self.__settings__["elas_evar_upsymb"] = 1 # attach elasticities 3043 self.__settings__["elas_epar_upsymb"] = 1 # attach parameter elasticities 3044 # remap steady state values ... no if SimElas 3045 self.__settings__["elas_evar_remap"] = 1 3046 # used to determine a stepsize dx=So*factor 3047 self.__settings__['mode_elas_deriv_factor'] = 0.0001 3048 # minimum value dx is allowed to have 3049 self.__settings__['mode_elas_deriv_min'] = 1.0e-12 3050 # replaces zero fluxes with a very small number 3051 self.__settings__['elas_zero_flux_fix'] = False 3052 # replaces zero concentrations with a very small number 3053 self.__settings__['elas_zero_conc_fix'] = False 3054 # if Infinite values are created when scaling set to zero 3055 self.__settings__['elas_scaling_div0_fix'] = False 3056 3057 # MCA options 3058 self.__settings__["mca_ccj_upsymb"] = 1 # attach the flux control coefficients 3059 # attach the concentration control coefficients 3060 self.__settings__["mca_ccs_upsymb"] = 1 3061 self.__settings__["mca_ccall_fluxout"] = 1 # in .showCC() output flux cc's 3062 self.__settings__["mca_ccall_concout"] = 1 # in .showCC() output conc cc's 3063 # in .showCC() all CC's group by reaction 3064 self.__settings__["mca_ccall_altout"] = 0 3065 3066 # gone in 60 seconds brett2008 (Refactored to PyscesLink) 3067 ## # Elementary mode options 3068 ## self.emode_userout = 0 # write metatool output to file 0:no, 1:yes 3069 ## self.emode_intmode = 0 # 0:float metatool, 1:integer metatool 3070 ## self.emode_file = self.ModelFile[:-4] + '_emodes' 3071 3072 # pitcon (pitcon 6.1) continuation options - brett 20040429 3073 # my interface controls 3074 # in the REq evaluation values <1.e-15 = 1.e-15 3075 self.__settings__["pitcon_fix_small"] = 0 3076 # parameter space that pitcon must search in 3077 self.pitcon_par_space = numpy.logspace(-1, 3, 10) 3078 # number of iterations to search for every point in par_space 3079 self.pitcon_iter = 10 3080 # initialize with non steady-state values 0:no,1:yes 3081 self.__settings__["pitcon_allow_badstate"] = 0 3082 # generate fluxes as output 3083 self.__settings__["pitcon_flux_gen"] = 1 3084 # drop pitcon results containing negative concentrations 0:no,1:yes 3085 self.__settings__["pitcon_filter_neg"] = 1 3086 # drop output results containing negative concentrations 0:no,1:yes 3087 self.__settings__["pitcon_filter_neg_res"] = 0 3088 self.__settings__["pitcon_max_step"] = 30.0 # Maximum stepsize 3089 # TODO: 3090 self.pitcon_target_points = [] # list of calculated target points 3091 self.pitcon_limit_points = [] # list of calculated limit points 3092 self.pitcon_targ_val = 0.0 # Target value (Seek solution with iwork[4]=) 3093 3094 # moved to InitialiseConservationArrays 3095 # self.__settings__["pitcon_max_step"] = 10*(len(self.__SI__)+1) #max corrector steps 3096 3097 # pitcon integer options iwork in pitcon/dpcon61.f 3098 self.__settings__["pitcon_init_par"] = 1 # Use X(1) for initial parameter 3099 # Parameterization option 0:allows program 3100 self.__settings__["pitcon_par_opt"] = 0 3101 self.__settings__["pitcon_jac_upd"] = 0 # Update jacobian every newton step 3102 self.__settings__["pitcon_targ_val_idx"] = 0 # Seek target values for X(n) 3103 self.__settings__["pitcon_limit_point_idx"] = 0 # Seek limit points in X(n) 3104 self.__settings__["pitcon_output_lvl"] = 0 # Control amount of output. 3105 # Jacobian choice. 0:supply jacobian,1:use forward difference,2:central difference 3106 self.__settings__["pitcon_jac_opt"] = 1 3107 # pitcon float options rwork in pitcon/dpcon61.f 3108 self.__settings__["pitcon_abs_tol"] = 0.00001 # Absolute error tolerance 3109 self.__settings__["pitcon_rel_tol"] = 0.00001 # Relative error tolerance 3110 self.__settings__["pitcon_min_step"] = 0.01 # Minimum stepsize 3111 self.__settings__["pitcon_start_step"] = 0.3 # Starting stepsize 3112 self.__settings__["pitcon_start_dir"] = 1.0 # Starting direction +1.0/-1.0 3113 self.__settings__["pitcon_max_grow"] = 3.0 # maximum growth factor 3114 3115 # setup conservation matrices (L_switch is set by stoich to 1 if it "detects" conservation) 3116 self.InitialiseConservationArrays() 3117 3118 # scan option - removed from initsimscan 3119 self.scan_in = '' 3120 self.scan_out = [] 3121 self._scan = None 3122 self.scan_res = None 3123 # should scan1 run mca analysis 0:no,1:elas,2:cc 3124 self.__settings__["scan1_mca_mode"] = 0 3125 # should scan1 drop invalid steady states (rare)? 3126 self.__settings__["scan1_dropbad"] = 0 3127 # invalid steady states are returned as NaN 3128 self.__settings__["scan1_nan_on_bad"] = True 3129 # print out progress messages for large (>20) scans 3130 self.__settings__["scan1_mesg"] = True 3131 self.__scan_errors_par__ = None # collect errors, parameters values 3132 self.__scan_errors_idx__ = None # collect errors, indexes 3133 3134 def InitialiseConservationArrays(self): 3135 """ 3136 Initialise conservation related vectors/array was in InitialiseModel but has been 3137 moved out so is can be called by when the stoichiometry is reanalysed 3138 3139 """ 3140 # Init and Sx vectors # these vectors are multiplying all by themselves - brett 3141 self.__SI__ = numpy.zeros((self.__lzeromatrix__.shape[1]), 'd') 3142 self.__SALL__ = numpy.zeros((len(self.__species__)), 'd') 3143 self.__settings__["pitcon_max_step"] = 10 * ( 3144 len(self.__SI__) + 1 3145 ) # max corrector steps 3146 if self.__HAS_MOIETY_CONSERVATION__ == True: 3147 # reorder the conservation matrix so that it is compatible with L 3148 idx = [ 3149 list(self.conservation_matrix_col).index(n) 3150 for n in range(len(self.conservation_matrix_col)) 3151 ] 3152 self.__reordered_lcons = self.conservation_matrix.take(idx, 1) 3153 if self.__HAS_COMPARTMENTS__ and self.__KeyWords__['Species_In_Conc']: 3154 self.__Build_Tvec__(amounts=False) 3155 else: 3156 self.__Build_Tvec__(amounts=True) 3157 self.showConserved(screenwrite=0) 3158 3159 def InitialiseOldFunctions(self): 3160 """ 3161 InitialiseOldFunctions() 3162 3163 Parse and initialise user defined functions specified by !T !U in the PSC input file 3164 3165 Arguments: 3166 None 3167 3168 """ 3169 self.__CODE_user = compile(self._Function_user, '_Function_user', 'exec') 3170 try: 3171 exec(self.__CODE_user) 3172 # print 'done.' 3173 except Exception as e: 3174 print('WARNING: User function error\n', e) 3175 print('This might be due to non-instantiated (e.g. MCA/state) attributes') 3176 print('Make sure the attributes that are used in your function exist ...') 3177 print('and manually load using the self.ReloadUserFunc() method') 3178 3179 # print '\nInitializing init function ...' 3180 # print self._Function_init 3181 3182 try: 3183 self.ReloadInitFunc() 3184 except Exception as e: 3185 print('WARNING: Init function error\n', e) 3186 print( 3187 'This function is meant to be used exclusively for the initialization' 3188 ) 3189 print( 3190 'of PySCeS properties and expressions based on model attributes defined in the input file.' 3191 ) 3192 print('Reinitialize with ReloadInitFunc()') 3193 3194 def ReloadUserFunc(self): 3195 """ 3196 ReloadUserFunc() 3197 3198 Recompile and execute the user function (!U) from the input file. 3199 3200 Arguments: 3201 None 3202 3203 """ 3204 ## print "User functions disabled ..." 3205 self.__CODE_user = compile(self._Function_user, '_Function_user', 'exec') 3206 try: 3207 exec(self.__CODE_user) 3208 except Exception as e: 3209 print('WARNING: User function load error\n', e) 3210 3211 def ReloadInitFunc(self): 3212 """ 3213 ReloadInitFunc() 3214 3215 Recompile and execute the user initialisations (!I) as defined in the PSC input file. 3216 and in mod.__InitFuncs__. 3217 3218 UPDATE 2015: can now be used to define InitialAssignments (no need for self.* prefix in input file) 3219 3220 Arguments: 3221 None 3222 3223 """ 3224 3225 def topolgical_sort(graph_unsorted): 3226 """ 3227 Repeatedly go through all of the nodes in the graph, moving each of 3228 the nodes that has all its edges resolved, onto a sequence that 3229 forms our sorted graph. A node has all of its edges resolved and 3230 can be moved once all the nodes its edges point to, have been moved 3231 from the unsorted graph onto the sorted one. 3232 """ 3233 3234 # This is the list we'll return, that stores each node/edges pair 3235 # in topological order. 3236 graph_sorted = [] 3237 3238 # Convert the unsorted graph into a hash table. This gives us 3239 # constant-time lookup for checking if edges are unresolved, and 3240 # for removing nodes from the unsorted graph. 3241 graph_unsorted = dict(graph_unsorted) 3242 3243 # Run until the unsorted graph is empty. 3244 while graph_unsorted: 3245 3246 # Go through each of the node/edges pairs in the unsorted 3247 # graph. If a set of edges doesn't contain any nodes that 3248 # haven't been resolved, that is, that are still in the 3249 # unsorted graph, remove the pair from the unsorted graph, 3250 # and append it to the sorted graph. Note here that by using 3251 # using the items() method for iterating, a copy of the 3252 # unsorted graph is used, allowing us to modify the unsorted 3253 # graph as we move through it. We also keep a flag for 3254 # checking that that graph is acyclic, which is true if any 3255 # nodes are resolved during each pass through the graph. If 3256 # not, we need to bail out as the graph therefore can't be 3257 # sorted. 3258 acyclic = False 3259 for node, edges in list(graph_unsorted.items()): 3260 for edge in edges: 3261 if edge in graph_unsorted: 3262 break 3263 else: 3264 acyclic = True 3265 del graph_unsorted[node] 3266 graph_sorted.append((node, edges)) 3267 3268 if not acyclic: 3269 # Uh oh, we've passed through all the unsorted nodes and 3270 # weren't able to resolve any of them, which means there 3271 # are nodes with cyclic edges that will never be resolved, 3272 # so we bail out with an error. 3273 raise RuntimeError("A cyclic dependency occurred") 3274 3275 return graph_sorted 3276 3277 simpleAss = [] 3278 exprsAss = [] 3279 symbolsX = [] 3280 for init in self.__InitFuncs__: 3281 if hasattr(self, init): 3282 # TODO: to be continued by brett 3283 # print(init, self.__InitFuncs__[init]) 3284 if type(self.__InitFuncs__[init]) == str: 3285 if self.__InitFuncs__[init].isdigit(): 3286 # self.__InitFuncs__[init] = eval(self.__InitFuncs__[init]) 3287 # setattr(self, init, self.__InitFuncs__[init]) 3288 # self.__InitFuncs__[init] = compile(self.__InitFuncs__[init], '_InitFunc_', 'single') 3289 simpleAss.append((init, eval(self.__InitFuncs__[init]))) 3290 # setattr(self, init, eval(self.__InitFuncs__[init])) 3291 else: 3292 InfixParser.setNameStr('self.', '') 3293 # InfixParser.SymbolReplacements = {'_TIME_':'mod._TIME_'} 3294 InfixParser.parse(str(self.__InitFuncs__[init])) 3295 piecewises = InfixParser.piecewises 3296 symbols = InfixParser.names 3297 for s_ in symbols: 3298 if s_ not in symbolsX: 3299 symbolsX.append(s_) 3300 if init not in symbolsX: 3301 symbolsX.append(init) 3302 functions = InfixParser.functions 3303 code_string = 'self.{} = {}'.format(init, InfixParser.output) 3304 xcode = compile(code_string, '_InitAss_', 'exec') 3305 exprsAss.append((init, xcode, symbols)) 3306 # setattr(self, init, eval(xcode)) 3307 # else: 3308 ##setattr(self, init, self.__InitFuncs__[init]) 3309 # self.__InitFuncs__[init] = compile(self.__InitFuncs__[init], '_InitFunc_', 'single') 3310 # setattr(self, init, eval(self.__InitFuncs__[init])) 3311 else: 3312 setattr(self, init, self.__InitFuncs__[init]) 3313 else: 3314 assert hasattr(self, init), '\nModelInit error' 3315 # TODO: to be continued by brett 3316 # print('symbolsX', symbolsX) 3317 for init, val in simpleAss: 3318 # print('s', init,val) 3319 setattr(self, init, val) 3320 execOrder = [] 3321 for init, xcode, symbols2 in exprsAss: 3322 symbols2 = [symbolsX.index(s_) for s_ in symbols2] 3323 execOrder.append((symbolsX.index(init), symbols2)) 3324 # TODO: to be continued by brett 3325 # print('x', symbols2) 3326 # print('e', init,code_string) 3327 eval(xcode) 3328 # TODO: to be continued by brett 3329 # print(execOrder) 3330 # print(topolgical_sort(execOrder)) 3331 # print(symbolsX) 3332 3333 def __initREQ__(self): 3334 """ 3335 __initREQ__() 3336 3337 Compile and initialise the model rate equations and associated arrays. Rate equations are 3338 generated as lambda functions callable as mod.R1(mod) 3339 3340 Arguments: 3341 None 3342 3343 """ 3344 # create work arrays for Reactions and variable species 3345 # s = numpy.zeros(len(self.__species__),'d') 3346 # self.Vtemp = numpy.zeros(len(self.__reactions__),'d') 3347 # This appears to be redundant leftover code - brett 20031215 3348 3349 if self.__settings__['display_debug'] == 1: 3350 pass 3351 # print 'Vtemp' 3352 3353 # create the map string by taking the VarReagent[] and assigning it to an s[index] 3354 # a reverse map function mapping self.sXi back to self.init[x] for simulation initiation 3355 mapString = '' 3356 mapString_R = '' 3357 for x in range(0, len(self.__species__)): 3358 mapString += 'self.{} = s[{}]\n'.format(self.__species__[x], x) 3359 mapString_R += 'self.__inspec__[{}] = self.{}_init\n'.format( 3360 x, self.__species__[x], 3361 ) 3362 ## print mapString_R 3363 # this creates the mapping string for the derivative functions 3364 3365 if self.__settings__['display_debug'] == 1: 3366 print('mapString') 3367 print(mapString) 3368 print('mapString_R') 3369 print(mapString_R) 3370 3371 # create the REq string in ReactionIDs order as a single multiline definition 3372 # using indexes: vString (Vtemp[x] =) 3373 # or a list of individual compile definitions: vArray (v + ReactionID = ) 3374 vString = '' 3375 ## vString2 = '' 3376 ## DvOrder = [] 3377 EStat = [] 3378 ## dispREq = '' 3379 3380 symbR = {} 3381 if ( 3382 len(list(self.__rules__.keys())) > 0 3383 and self.__settings__['mode_substitute_assignment_rules'] 3384 ): 3385 # create substitution dictionary 3386 for ass in self.__rules__: 3387 symbR.update( 3388 {self.__rules__[ass]['name']: self.__rules__[ass]['code_string']} 3389 ) 3390 3391 for x in range(0, len(self.__reactions__)): 3392 req1 = self.__nDict__[self.__reactions__[x]]['RateEq'] 3393 ## DvOrder.append(self.__reactions__[x]) 3394 vString += 'Vtemp[{}] = self.{}()\n'.format(x, self.__reactions__[x]) 3395 3396 # Core update inspired by Core2, lambda functions replaced by Reaction instances 3397 3398 if ( 3399 len(list(self.__rules__.keys())) > 0 3400 and self.__settings__['mode_substitute_assignment_rules'] 3401 ): 3402 # substitute assignment rules 3403 InfixParser.setNameStr('self.', '') 3404 InfixParser.FunctionReplacements = symbR 3405 InfixParser.parse(req1.replace('self.', '')) 3406 req2 = InfixParser.output 3407 ## req1_names = InfixParser.names 3408 rObj = ReactionObj(self, self.__reactions__[x], req2, 'self.') 3409 else: 3410 rObj = ReactionObj(self, self.__reactions__[x], req1, 'self.') 3411 self.__ParsePiecewiseFunctions__(rObj.piecewises) 3412 rObj.compartment = self.__nDict__[rObj.name]['compartment'] 3413 3414 setattr(self, self.__reactions__[x], rObj) 3415 3416 # this is to check that if a model defines compartments then REq's MUST be in amounts brett2008 3417 # brett2008 3418 if self.__HAS_COMPARTMENTS__: 3419 cnames = [self.__compartments__[c]['name'] for c in self.__compartments__] 3420 warnings = '' 3421 for rr in self.__reactions__: 3422 rrobj = getattr(self, rr) 3423 warn = False 3424 if ( 3425 rrobj.compartment == None 3426 and self.__settings__['display_compartment_warnings'] 3427 ): 3428 warnings += "# {} is not located in a compartment.\n".format( 3429 rrobj.name 3430 ) 3431 else: 3432 for comp in cnames: 3433 if comp in rrobj.symbols: 3434 warn = False 3435 break 3436 else: 3437 warn = True 3438 if warn: 3439 warnings += "# {}: {}\n# assuming kinetic constants are flow constants.\n".format( 3440 rr, rrobj.formula 3441 ) 3442 if warnings != '' and self.__settings__['display_compartment_warnings']: 3443 print('\n# -- COMPARTMENT WARNINGS --') 3444 print(warnings) 3445 if self.__settings__['display_debug'] == 1: 3446 print('vString') 3447 print(vString) 3448 ## print 'vString2' 3449 ## print vString2 3450 ## print 'DvOrder' 3451 ## print DvOrder 3452 return mapString, mapString_R, vString 3453 3454 def __initmodel__(self): 3455 """ 3456 __initmodel__() 3457 3458 Generate the stoichiometric matrix N from the parsed model description. 3459 Returns a stoichiometric matrix (N) 3460 3461 Arguments: 3462 None 3463 3464 """ 3465 VarReagents = ['self.' + s for s in self.__species__] 3466 StoicMatrix = numpy.zeros((len(VarReagents), len(self.__reactions__)), 'd') 3467 for reag in VarReagents: 3468 for id in self.__reactions__: 3469 if reag in list(self.__nDict__[id]['Reagents'].keys()): 3470 StoicMatrix[VarReagents.index(reag)][ 3471 self.__reactions__.index(id) 3472 ] = self.__nDict__[id]['Reagents'][reag] 3473 return StoicMatrix 3474 3475 def __initvar__(self): 3476 """ 3477 __initvar__() 3478 3479 Compile and initialise the model variable species and derivatives and associated mapping arrays. 3480 3481 Arguments: 3482 None 3483 3484 """ 3485 mvarString = '' 3486 sOrder = [] 3487 self.__remaps = '' 3488 DvarUpString = '' 3489 3490 # self.__inspec__ = numpy.zeros((len(self.__species__)),'d') moved to ParseModel 3491 for x in range(0, len(self.__species__)): 3492 key = self.__species__[x] 3493 ## self.__inspec__[x] = eval(key) 3494 self.__inspec__[x] = getattr(self, key) 3495 mvarString += 'self.__inspec__[{}] = float(self.{})\n'.format(x, key) 3496 self.__remaps += 'self.{} = self.{}_ss\n'.format(key, key) 3497 sOrder.append('self.' + key) 3498 DvarUpString += 'self.{} = input[{}]\n'.format(self.__species__[x], x) 3499 3500 if self.__settings__['display_debug'] == 1: 3501 print('s_initDeriv') 3502 print(s_initDeriv) 3503 print('\n__remaps') 3504 print(self.__remaps) 3505 print('\nDvarUpString') 3506 print(DvarUpString) 3507 3508 mvarFunc = compile(mvarString, 'mvarString', 'exec') 3509 return sOrder, DvarUpString 3510 3511 def __initfixed__(self): 3512 """ 3513 __initfixed__() 3514 3515 Compile and initialise the fixed species and associated mapping arrays 3516 3517 Arguments: 3518 None 3519 3520 """ 3521 runmapString = '' 3522 if self.__settings__['display_debug'] == 1: 3523 print('InitParams2') 3524 print(self.__parameters__) 3525 print('InitStrings2') 3526 ## print self.__InitStrings 3527 3528 # Initialise parameter elasticities 3529 par_map2store = '' 3530 par_remap = '' 3531 for x in range(len(self.__parameters__)): 3532 par_map2store += 'parVal_hold[{}] = self.{}\n'.format( 3533 x, self.__parameters__[x] 3534 ) 3535 par_remap += 'self.{} = parVal_hold[{}]\n'.format(self.__parameters__[x], x) 3536 3537 if self.__settings__['display_debug'] == 1: 3538 print('par_map2store') 3539 print(par_map2store) 3540 print('par_remap') 3541 print(par_remap) 3542 return (par_map2store, par_remap) 3543 3544 # pysces core - the steady-state solver and integration routines 3545 def _SpeciesAmountToConc(self, s): 3546 """takes and returns s""" 3547 for x in range(len(self.__CsizeAllIdx__)): 3548 self.__CsizeAll__[x] = getattr(self, self.__CsizeAllIdx__[x]) 3549 ## print '__CALL__', self.__CsizeAll__ 3550 return s / self.__CsizeAll__ 3551 3552 def _SpeciesConcToAmount(self, s): 3553 """takes and returns s""" 3554 for x in range(len(self.__CsizeAllIdx__)): 3555 self.__CsizeAll__[x] = getattr(self, self.__CsizeAllIdx__[x]) 3556 ## print '__CALL__', self.__CsizeAll__ 3557 return s * self.__CsizeAll__ 3558 3559 def _FixedSpeciesAmountToConc(self): 3560 for x in range(len(self.__fixed_species__)): 3561 am = getattr(self, self.__fixed_species__[x]) 3562 self.__CsizeFS__[x] = getattr(self, self.__CsizeFSIdx__[x]) 3563 setattr(self, self.__fixed_species__[x], am / self.__CsizeFS__[x]) 3564 3565 def _FixedSpeciesConcToAmount(self): 3566 for x in range(len(self.__fixed_species__)): 3567 cn = getattr(self, self.__fixed_species__[x]) 3568 self.__CsizeFS__[x] = getattr(self, self.__CsizeFSIdx__[x]) 3569 setattr(self, self.__fixed_species__[x], cn * self.__CsizeFS__[x]) 3570 3571 # extract SI and "correct" SD in s solve for v 3572 # Vtemp is passed through the function to avoid an irritating bug 3573 # that appears if it isn't (ie defined as a global) - brett 3574 def _EvalREq2_alt(self, s, Vtemp): 3575 """ 3576 _EvalREq2_alt(s,Vtemp) 3577 3578 Evaluate the rate equations correcting for mass conservation. 3579 Takes full [s] returns full [s] --> moiety aware 3580 3581 Arguments: 3582 3583 s: a vector of species cosnservations 3584 Vtemp: the rate vector 3585 3586 """ 3587 # form an SI vector 3588 for x in range(0, len(self.lzeromatrix_col)): 3589 self.__SI__[x] = s[self.lzeromatrix_col[x]] 3590 3591 # replace SD with SI calculated values 3592 for x in range(0, len(self.lzeromatrix_row)): 3593 s[self.lzeromatrix_row[x]] = self.__tvec_a__[x] + numpy.add.reduce( 3594 self.__lzeromatrix__[x, :] * self.__SI__ 3595 ) 3596 return self._EvalREq(s, Vtemp) 3597 3598 def _EvalREq(self, s, Vtemp): 3599 if self.__HAS_RATE_RULES__: 3600 exec(self.__CODE_raterule_map) 3601 if self.__HAS_COMPARTMENTS__ and self.__KeyWords__['Species_In_Conc']: 3602 s = self._SpeciesAmountToConc(s) 3603 exec(self.__mapFunc__) 3604 try: 3605 self.Forcing_Function() 3606 except Exception as de: 3607 print('INFO: forcing function failure', de) 3608 try: 3609 exec(self.__vFunc__) 3610 except ( 3611 ArithmeticError, 3612 AttributeError, 3613 NameError, 3614 ZeroDivisionError, 3615 ValueError, 3616 ) as detail: 3617 print('INFO: REq evaluation failure:', detail) 3618 Vtemp[:] = self.__settings__['mach_floateps'] 3619 if self.__HAS_RATE_RULES__: 3620 try: 3621 exec(self.__CODE_raterule) 3622 except ( 3623 ArithmeticError, 3624 AttributeError, 3625 NameError, 3626 ZeroDivisionError, 3627 ValueError, 3628 ) as detail: 3629 print('INFO: RateRule evaluation failure:', detail) 3630 return Vtemp 3631 3632 def _EvalREq2(self, s, Vtemp): 3633 """ 3634 _EvalREq2(s,Vtemp) 3635 3636 Evaluate the rate equations, as PySCeS uses the reduced set of ODE's for its core operations, this method 3637 takes mass conservation into account and regenerates the full species vector 3638 takes [si] returns full [s] 3639 3640 Arguments: 3641 3642 s: species vector 3643 Vtemp: rate vector 3644 3645 """ 3646 # stick SI into s using Nrrow order 3647 for x in range(len(s)): 3648 self.__SALL__[self.nrmatrix_row[x]] = s[x] 3649 # stick SD into s using lzerorow order 3650 for x in range(len(self.lzeromatrix_row)): 3651 self.__SALL__[self.lzeromatrix_row[x]] = self.__tvec_a__[ 3652 x 3653 ] + numpy.add.reduce(self.__lzeromatrix__[x, :] * s) 3654 return self._EvalREq(self.__SALL__, Vtemp) 3655 3656 def _EvalODE(self, s, Vtemp): 3657 """ 3658 _EvalODE(s,Vtemp) 3659 3660 Core ODE evaluation routine evaluating the reduced set of ODE's. Depending on whether mass conservation is present or not either N*v (_EvalREq) or Nr*v (_EvalREq2) is used to automatically generate the ODE's. 3661 Returns sdot. 3662 3663 Arguments: 3664 3665 s: species vector 3666 Vtemp: rate vector 3667 3668 """ 3669 if self.__HAS_RATE_RULES__: 3670 s, self.__rrule__ = numpy.split(s, [self.Nrmatrix.shape[0]]) 3671 if self.__HAS_MOIETY_CONSERVATION__ == True: 3672 for x in range(len(s)): 3673 self.__SALL__[self.nrmatrix_row[x]] = s[x] 3674 for x in range(len(self.lzeromatrix_row)): 3675 self.__SALL__[self.lzeromatrix_row[x]] = self.__tvec_a__[ 3676 x 3677 ] + numpy.add.reduce(self.__lzeromatrix__[x, :] * s) 3678 self.__vvec__ = self._EvalREq(self.__SALL__, Vtemp) 3679 s = numpy.add.reduce(self.__nrmatrix__ * self.__vvec__, 1) 3680 else: 3681 self.__vvec__ = self._EvalREq(s, Vtemp) 3682 s = numpy.add.reduce(self.__nmatrix__ * self.__vvec__, 1) 3683 if self.__HAS_RATE_RULES__: 3684 s = numpy.concatenate([s, self.__rrule__]).copy() 3685 return s 3686 3687 # the following are user functions defined in the input file or assigned after instantiated 3688 def _EvalODE_CVODE(self, s, Vtemp): 3689 """ 3690 _EvalODE_CVODE(s,Vtemp) 3691 3692 Evaluate the full set of ODE's. Depending on whether mass conservation is present or not 3693 either N*v (_EvalREq) or Nr*v (_EvalREq2_alt) is used. 3694 3695 Arguments: 3696 3697 s: species vector 3698 Vtemp: rate vector 3699 3700 """ 3701 if self.__HAS_RATE_RULES__: 3702 s, self.__rrule__ = numpy.split(s, [self.Nmatrix.shape[0]]) 3703 self.__vvec__ = self._EvalREq(s, Vtemp) 3704 s = numpy.add.reduce(self.__nmatrix__ * self.__vvec__, 1) 3705 if self.__HAS_RATE_RULES__: 3706 s = numpy.concatenate([s, self.__rrule__]).copy() 3707 return s 3708 3709 def Forcing_Function(self): 3710 """ 3711 Forcing_Function() 3712 3713 User defined forcing function either defined in the PSC input file as !F or by overwriting this method. 3714 This method is evaluated prior to every rate equation evaluation. 3715 3716 Arguments: 3717 None 3718 3719 """ 3720 # initialized in __initFunction__() - brett 20050621 3721 exec(self.__CODE_forced) 3722 3723 def User_Function(self): 3724 """ 3725 **Deprecated** 3726 """ 3727 exec(self.__CODE_user) 3728 3729 # pysundials 3730 3731 def _EvalExtraData(self, xdata): 3732 """ 3733 Takes a list of model attributes and returns an array of values 3734 """ 3735 return numpy.array([getattr(self, d) for d in xdata]) 3736 3737 __CVODE_initialise__ = True 3738 CVODE_continuous_result = None 3739 __CVODE_initial_num__ = None 3740 3741 def CVODE_continue(self, tvec): 3742 """ 3743 **Experimental:** continues a simulation over a new time vector, the 3744 CVODE memobj is reused and not reinitialised and model parameters can be 3745 changed between calls to this method. The mod.data_sim objects from 3746 the initial simulation and all calls to this method are stored in the list 3747 *mod.CVODE_continuous_result*. 3748 3749 - *tvec* a numpy array of time points 3750 3751 """ 3752 assert ( 3753 self.mode_integrator == 'CVODE' 3754 ), "\nFor what should be rather obvious reasons, this method requires CVODE to be used as the default integration algorithm.\n" 3755 if self.CVODE_continuous_result == None: 3756 self.CVODE_continuous_result = [self.data_sim] 3757 3758 self.__CVODE_initialise__ = False 3759 self.sim_time = tvec 3760 3761 Tsim0 = time.time() 3762 sim_res, rates, simOK = self.CVODE(None) 3763 Tsim1 = time.time() 3764 print( 3765 "{} time for {} points: {}".format( 3766 self.mode_integrator, len(self.sim_time), Tsim1 - Tsim0 3767 ) 3768 ) 3769 3770 if self.__HAS_RATE_RULES__: 3771 sim_res, rrules = numpy.split(sim_res, [len(self.__species__)], axis=1) 3772 print('RateRules evaluated and added to mod.data_sim.') 3773 3774 # TODO: split this off into a method shared by this and Simulate() 3775 self.data_sim = IntegrationDataObj() 3776 self.IS_VALID = simOK 3777 self.data_sim.setTime(self.sim_time) 3778 self.data_sim.setSpecies(sim_res, self.__species__) 3779 self.data_sim.setRates(rates, self.__reactions__) 3780 if self.__HAS_RATE_RULES__: 3781 self.data_sim.setRules(rrules, self.__rate_rules__) 3782 if len(self.CVODE_extra_output) > 0: 3783 self.data_sim.setXData(self.CVODE_xdata, lbls=self.CVODE_extra_output) 3784 self.CVODE_xdata = None 3785 if not simOK: 3786 print('Simulation failure') 3787 del sim_res 3788 3789 self.CVODE_continuous_result.append(self.data_sim) 3790 self.__CVODE_initialise__ = True 3791 3792 # def CVODE(self, initial): 3793 # """ 3794 # CVODE(initial) 3795 # 3796 # PySCeS interface to the CVODE integration algorithm. 3797 # 3798 # Arguments: 3799 # initial: vector containing initial species concentrations 3800 # 3801 # """ 3802 # assert ( 3803 # _HAVE_ASSIMULO 3804 # ), '\nPySundials is not installed or did not import correctly\n{}'.format( 3805 # _ASSIMULO_LOAD_ERROR 3806 # ) 3807 # Vtemp = numpy.zeros((self.__Nshape__[1])) 3808 # 3809 # def findi(t, y, ydot, f_data): 3810 # self._TIME_ = t 3811 # ydota = self._EvalODE(numpy.array(y), Vtemp) 3812 # ydot[:] = ydota[:] 3813 # return 0 # non-zero return indicates error state 3814 # 3815 # def ffull(t, y, ydot, f_data): 3816 # self._TIME_ = t 3817 # ## ya = numpy.array(y) 3818 # ydota = self._EvalODE_CVODE(numpy.array(y), Vtemp) # unreduced ODE's 3819 # ydot[:] = ydota[:] 3820 # return 0 3821 # 3822 # func = None 3823 # if self.mode_integrate_all_odes: 3824 # func = ffull 3825 # else: 3826 # func = findi 3827 # 3828 # if self.__CVODE_initialise__: 3829 # tZero = initial.copy() 3830 # if self.__HAS_RATE_RULES__: 3831 # initial, rrules = numpy.split(initial, [self.Nrmatrix.shape[0]]) 3832 # tZero = initial.copy() 3833 # if self.__HAS_MOIETY_CONSERVATION__ and self.mode_integrate_all_odes: 3834 # initial = self.Fix_S_indinput(initial, amounts=True) 3835 # tZero = initial.copy() 3836 # elif self.__HAS_MOIETY_CONSERVATION__: 3837 # tZero = self.Fix_S_indinput(tZero, amounts=True) 3838 # if self.__HAS_RATE_RULES__: 3839 # initial = numpy.concatenate([initial, rrules]) 3840 # tZero = numpy.concatenate([tZero, rrules]) 3841 # else: 3842 # tZero = None 3843 # 3844 # # the following block initialises the cvode integrator, and sets various options 3845 # if self.__CVODE_initialise__: 3846 # self.__CVODE_y__ = cvode.NVector(initial.tolist()) 3847 # self.__CVODE_mem__ = cvode.CVodeCreate( 3848 # cvode.CV_BDF, cvode.CV_NEWTON 3849 # ) # initialisation with basic options, newtonian solver etc... 3850 # self.__CVODE_initial_num__ = len(initial) 3851 # del initial 3852 # t = cvode.realtype(0.0) 3853 # rates = numpy.zeros((len(self.sim_time), len(self.__reactions__))) 3854 # output = None 3855 # if self.__HAS_RATE_RULES__: 3856 # output = numpy.zeros( 3857 # (len(self.sim_time), len(self.__rrule__) + len(self.__species__)) 3858 # ) 3859 # else: 3860 # output = numpy.zeros((len(self.sim_time), len(self.__species__))) 3861 # 3862 # CVODE_XOUT = False 3863 # if len(self.CVODE_extra_output) > 0: 3864 # out = [] 3865 # for d in self.CVODE_extra_output: 3866 # if ( 3867 # hasattr(self, d) 3868 # and d 3869 # not in self.__species__ + self.__reactions__ + self.__rate_rules__ 3870 # ): 3871 # out.append(d) 3872 # else: 3873 # print( 3874 # '\nWarning: CVODE is ignoring extra data ({}), it either doesn\'t exist or it\'s a species or rate.\n'.format( 3875 # d 3876 # ) 3877 # ) 3878 # if len(out) > 0: 3879 # self.CVODE_extra_output = out 3880 # CVODE_XOUT = True 3881 # del out 3882 # 3883 # if CVODE_XOUT: 3884 # self.CVODE_xdata = numpy.zeros( 3885 # (len(self.sim_time), len(self.CVODE_extra_output)) 3886 # ) 3887 # 3888 # sim_st = 0 3889 # if self.sim_time[0] == 0.0: 3890 # 3891 # if self.__CVODE_initialise__: 3892 # out0 = tZero[:].copy() 3893 # else: 3894 # out0 = numpy.array(self.__CVODE_y__[:]) 3895 # output[0, :] = out0 3896 # 3897 # if not self.mode_integrate_all_odes: 3898 # self._EvalODE(out0.copy(), self._CVODE_Vtemp) 3899 # else: 3900 # self._EvalODE_CVODE(out0.copy(), self._CVODE_Vtemp) 3901 # rates[0] = self.__vvec__ 3902 # 3903 # if CVODE_XOUT: 3904 # self.CVODE_xdata[0, :] = self._EvalExtraData(self.CVODE_extra_output) 3905 # sim_st = 1 3906 # del tZero 3907 # 3908 # var_store = {} 3909 # if self.__HAS_EVENTS__: 3910 # for ev in self.__events__: 3911 # ev.reset() 3912 # for ass in ev.assignments: 3913 # var_store.update({ass.variable: getattr(self, ass.variable)}) 3914 # TOL_ADJUSTER = 0 3915 # MAX_TOL_CNT = 5 3916 # MAX_REL_TOLERANCE = 1.0e-3 3917 # RELTOL_ADJUST_FACTOR = 1.0e3 3918 # MIN_ABS_TOL = self.__settings__["cvode_abstol"] # 1.0e-15 3919 # ## MAX_ABS_TOL = self.__settings__["cvode_abstol_max"] #1.0e-3 not used anymore 3920 # ABSTOL_ADJUST_FACTOR = self.__settings__["cvode_abstol_factor"] # 1.0e-6 3921 # cvode_sim_range = list(range(sim_st, len(self.sim_time))) 3922 # cvode_scale_range = list( 3923 # range( 3924 # sim_st, 3925 # len(self.sim_time), 3926 # len(self.sim_time) // 4 or len(self.sim_time), 3927 # ) 3928 # ) 3929 # cvode_scale_range = cvode_scale_range[1:] 3930 # reltol = cvode.realtype( 3931 # self.__settings__["cvode_reltol"] 3932 # ) # relative tolerance must be a realtype 3933 # abstol = cvode.NVector( 3934 # self.__CVODE_initial_num__ * [self.__settings__["cvode_abstol"]] 3935 # ) 3936 # for s in range(len(self.__CVODE_y__)): 3937 # newVal = abs(self.__CVODE_y__[s]) * ABSTOL_ADJUST_FACTOR 3938 # if newVal < MIN_ABS_TOL: 3939 # abstol[s] = MIN_ABS_TOL 3940 # else: 3941 # abstol[s] = newVal 3942 # if self.__CVODE_initialise__: 3943 # cvode.CVodeMalloc( 3944 # self.__CVODE_mem__, 3945 # func, 3946 # 0.0, 3947 # self.__CVODE_y__, 3948 # cvode.CV_SV, 3949 # reltol, 3950 # abstol, 3951 # ) # set tolerances, specify vector of initial conditions, set integrtion function etc... 3952 # cvode.CVDense( 3953 # self.__CVODE_mem__, self.__CVODE_initial_num__ 3954 # ) # set dense option, specify dimension of problem (3) 3955 # if self.__HAS_EVENTS__: 3956 # cvode.CVodeRootInit( 3957 # self.__CVODE_mem__, len(self.__events__), self.CVODE_EVENTS, None 3958 # ) # specify to 'root' conditions, and function that calculates them 3959 # 3960 # for st in cvode_sim_range: 3961 # tout = self.sim_time[st] 3962 # errcount = 0 3963 # ## print TOL_ADJUSTER, MAX_TOL_CNT, abstol, MAX_REL_TOLERANCE 3964 # if ( 3965 # self.__settings__["cvode_auto_tol_adjust"] 3966 # and TOL_ADJUSTER >= MAX_TOL_CNT 3967 # and reltol.value < MAX_REL_TOLERANCE 3968 # ): 3969 # reltol.value = reltol.value * RELTOL_ADJUST_FACTOR 3970 # cvode.CVodeSetTolerances( 3971 # self.__CVODE_mem__, cvode.CV_SV, reltol, abstol 3972 # ) 3973 # ## print '\nCVODE: new tolerance set:\nreltol={}'.format(reltol.value) 3974 # ## print '\nAbs tolerance:\n{}'.format(abstol) 3975 # TOL_ADJUSTER = 0 3976 # if (st in cvode_scale_range) and self.__settings__["cvode_auto_tol_adjust"]: 3977 # for s in range(len(self.__CVODE_y__)): 3978 # newVal = abs(self.__CVODE_y__[s]) * ABSTOL_ADJUST_FACTOR 3979 # if newVal < MIN_ABS_TOL: 3980 # abstol[s] = MIN_ABS_TOL 3981 # else: 3982 # abstol[s] = newVal 3983 # ## print '\nCVODE: new tolerance set, abstol:\n{}'.format(abstol) 3984 # cvode.CVodeSetTolerances( 3985 # self.__CVODE_mem__, cvode.CV_SV, reltol, abstol 3986 # ) 3987 # ## cvode.CVodeReInit(self.__CVODE_mem__, func, self.sim_time[st-1], self.__CVODE_y__, cvode.CV_SV, reltol, abstol) 3988 # while True: 3989 # try: 3990 # flag = cvode.CVode( 3991 # self.__CVODE_mem__, 3992 # tout, 3993 # self.__CVODE_y__, 3994 # cvode.ctypes.byref(t), 3995 # cvode.CV_NORMAL, 3996 # ) 3997 # except AssertionError as ex: 3998 # print('cvode error1', ex) 3999 # flag = None 4000 # self._TIME_ = tout 4001 # if ( 4002 # flag == cvode.CV_ROOT_RETURN 4003 # ): # if a root was found before desired time point, output it 4004 # ya = numpy.array(self.__CVODE_y__) 4005 # rootsfound = cvode.CVodeGetRootInfo( 4006 # self.__CVODE_mem__, len(self.__events__) 4007 # ) 4008 # reInit = False 4009 # for ev in range(len(self.__events__)): 4010 # if rootsfound[ev] == 1: 4011 # for ass in self.__events__[ev].assignments: 4012 # # only can assign to independent species vector 4013 # if ass.variable in self.L0matrix.getLabels()[1] or ( 4014 # self.mode_integrate_all_odes 4015 # and ass.variable in self.__species__ 4016 # ): 4017 # assVal = ass.getValue() 4018 # assIdx = self.__species__.index(ass.variable) 4019 # if self.__KeyWords__['Species_In_Conc']: 4020 # ## print self.__CVODE_y__ 4021 # self.__CVODE_y__[assIdx] = assVal * getattr( 4022 # self, self.__CsizeAllIdx__[assIdx] 4023 # ) 4024 # ## raw_input(self.__CVODE_y__) 4025 # else: 4026 # self.__CVODE_y__[assIdx] = assVal 4027 # reInit = True 4028 # elif ( 4029 # not self.mode_integrate_all_odes 4030 # and ass.variable in self.L0matrix.getLabels()[0] 4031 # ): 4032 # print( 4033 # 'Event assignment to dependent species consider setting \"mod.mode_integrate_all_odes = True\"' 4034 # ) 4035 # elif ( 4036 # self.__HAS_RATE_RULES__ 4037 # and ass.variable in self.__rate_rules__ 4038 # ): 4039 # ## print 'Event is assigning to rate rule' 4040 # assVal = ass.getValue() 4041 # rrIdx = self.__rate_rules__.index(ass.variable) 4042 # ## print ass.variable, assVal 4043 # self.__rrule__[rrIdx] = assVal 4044 # ## print self.L0matrix.shape[1], rrIdx, len(self.__CVODE_y__) 4045 # self.__CVODE_y__[ 4046 # self.L0matrix.shape[1] + rrIdx 4047 # ] = assVal 4048 # setattr(self, ass.variable, assVal) 4049 # reInit = True 4050 # else: 4051 # try: 4052 # setattr(self, ass.variable, ass.getValue()) 4053 # reInit = True 4054 # except: 4055 # print( 4056 # 'ERROR: Updating model attribute from event: ', 4057 # ass.variable, 4058 # ) 4059 # 4060 # if reInit: 4061 # cvode.CVodeReInit( 4062 # self.__CVODE_mem__, 4063 # func, 4064 # tout, 4065 # self.__CVODE_y__, 4066 # cvode.CV_SV, 4067 # reltol, 4068 # abstol, 4069 # ) 4070 # 4071 # # this gets everything into the current tout state 4072 # tmp = None 4073 # if not self.mode_integrate_all_odes: 4074 # tmp = self._EvalODE(ya.copy(), self._CVODE_Vtemp) 4075 # else: 4076 # tmp = self._EvalODE_CVODE(ya.copy(), self._CVODE_Vtemp) 4077 # del tmp 4078 # 4079 # # here we regenerate Sd's and fix concentrations 4080 # rrules = None 4081 # if ( 4082 # self.__HAS_MOIETY_CONSERVATION__ 4083 # and not self.mode_integrate_all_odes 4084 # ): 4085 # if self.__HAS_RATE_RULES__: 4086 # ya, rrules = numpy.split(ya, [self.Nrmatrix.shape[0]]) 4087 # ya = self.Fix_S_indinput(ya, amounts=True) 4088 # # convert to concentrations 4089 # if ( 4090 # self.__HAS_COMPARTMENTS__ 4091 # and self.__KeyWords__['Output_In_Conc'] 4092 # ): 4093 # ya = self._SpeciesAmountToConc(ya) 4094 # if self.__HAS_RATE_RULES__: 4095 # ya = numpy.concatenate([ya, rrules]) 4096 # else: 4097 # if self.__HAS_RATE_RULES__: 4098 # ya, rrules = numpy.split(ya, [self.Nmatrix.shape[0]]) 4099 # if ( 4100 # self.__HAS_COMPARTMENTS__ 4101 # and self.__KeyWords__['Output_In_Conc'] 4102 # ): 4103 # ya = self._SpeciesAmountToConc(ya) 4104 # if self.__HAS_RATE_RULES__: 4105 # ya = numpy.concatenate([ya, rrules]) 4106 # 4107 # output[st] = ya 4108 # # set with self._EvalODE above 4109 # rates[st] = self.__vvec__ 4110 # 4111 # if CVODE_XOUT: 4112 # self.CVODE_xdata[st, :] = self._EvalExtraData( 4113 # self.CVODE_extra_output 4114 # ) 4115 # # this should adjust the expected time to the new output time time 4116 # self.sim_time[st] = float(tout) 4117 # # dont need anymore i think 4118 # if _HAVE_VPYTHON: 4119 # self.CVODE_VPYTHON(ya) 4120 # del ya, rrules 4121 # break 4122 # if flag == cvode.CV_SUCCESS: 4123 # ya = numpy.array(self.__CVODE_y__) 4124 # # this gets everything into the current tout state 4125 # tmp = None 4126 # if not self.mode_integrate_all_odes: 4127 # tmp = self._EvalODE(ya.copy(), self._CVODE_Vtemp) 4128 # else: 4129 # tmp = self._EvalODE_CVODE(ya.copy(), self._CVODE_Vtemp) 4130 # del tmp 4131 # # here we regenerate Sd's and fix concentrations 4132 # rrules = None 4133 # if ( 4134 # self.__HAS_MOIETY_CONSERVATION__ 4135 # and not self.mode_integrate_all_odes 4136 # ): 4137 # if self.__HAS_RATE_RULES__: 4138 # ya, rrules = numpy.split(ya, [self.Nrmatrix.shape[0]]) 4139 # ya = self.Fix_S_indinput(ya, amounts=True) 4140 # # convert to concentrations 4141 # if ( 4142 # self.__HAS_COMPARTMENTS__ 4143 # and self.__KeyWords__['Output_In_Conc'] 4144 # ): 4145 # ya = self._SpeciesAmountToConc(ya) 4146 # if self.__HAS_RATE_RULES__: 4147 # ya = numpy.concatenate([ya, rrules]) 4148 # else: 4149 # if self.__HAS_RATE_RULES__: 4150 # ya, rrules = numpy.split(ya, [self.Nmatrix.shape[0]]) 4151 # if ( 4152 # self.__HAS_COMPARTMENTS__ 4153 # and self.__KeyWords__['Output_In_Conc'] 4154 # ): 4155 # ya = self._SpeciesAmountToConc(ya) 4156 # if self.__HAS_RATE_RULES__: 4157 # ya = numpy.concatenate([ya, rrules]) 4158 # 4159 # output[st] = ya 4160 # # set with self._EvalODE above 4161 # rates[st] = self.__vvec__ 4162 # 4163 # if CVODE_XOUT: 4164 # self.CVODE_xdata[st, :] = self._EvalExtraData( 4165 # self.CVODE_extra_output 4166 # ) 4167 # if _HAVE_VPYTHON: 4168 # self.CVODE_VPYTHON(ya) 4169 # del ya, rrules 4170 # break 4171 # elif flag == -1: 4172 # if self.__settings__["cvode_mxstep"] == 1000: 4173 # self.__settings__["cvode_mxstep"] = 3000 4174 # TOL_ADJUSTER += 1 4175 # elif self.__settings__["cvode_mxstep"] == 3000: 4176 # self.__settings__["cvode_mxstep"] = 10000 4177 # TOL_ADJUSTER += 2 4178 # elif self.__settings__["cvode_mxstep"] == 10000: 4179 # ## TOL_ADJUSTER += 1 4180 # output[st] = numpy.NaN 4181 # break 4182 # print( 4183 # 'mxstep warning ({}) mxstep set to {}'.format( 4184 # flag, self.__settings__["cvode_mxstep"] 4185 # ) 4186 # ) 4187 # cvode.CVodeSetMaxNumSteps( 4188 # self.__CVODE_mem__, self.__settings__["cvode_mxstep"] 4189 # ) 4190 # elif flag < -3: 4191 # print('CVODE error:', flag) 4192 # print('At ', tout) 4193 # output[st] = numpy.NaN 4194 # rates[st] = numpy.NaN 4195 # if CVODE_XOUT: 4196 # self.CVODE_xdata[st] = numpy.NaN 4197 # break 4198 # self.__settings__["cvode_mxstep"] = 1000 4199 # cvode.CVodeSetMaxNumSteps( 4200 # self.__CVODE_mem__, self.__settings__["cvode_mxstep"] 4201 # ) 4202 # 4203 # if self.__HAS_EVENTS__: 4204 # for ass in list(var_store.keys()): 4205 # # print 'old value', ass, getattr(self, ass) 4206 # setattr(self, ass, var_store[ass]) 4207 # # print 'new value', getattr(self, ass) 4208 # 4209 # if self.__settings__["cvode_stats"]: 4210 # # print some stats from the intgrator 4211 # nst = cvode.CVodeGetNumSteps(self.__CVODE_mem__) 4212 # nfe = cvode.CVodeGetNumRhsEvals(self.__CVODE_mem__) 4213 # nsetups = cvode.CVodeGetNumLinSolvSetups(self.__CVODE_mem__) 4214 # netf = cvode.CVodeGetNumErrTestFails(self.__CVODE_mem__) 4215 # nni = cvode.CVodeGetNumNonlinSolvIters(self.__CVODE_mem__) 4216 # ncfn = cvode.CVodeGetNumNonlinSolvConvFails(self.__CVODE_mem__) 4217 # nje = cvode.CVDenseGetNumJacEvals(self.__CVODE_mem__) 4218 # nfeLS = cvode.CVDenseGetNumRhsEvals(self.__CVODE_mem__) 4219 # nge = cvode.CVodeGetNumGEvals(self.__CVODE_mem__) 4220 # 4221 # print("\nFinal Statistics:") 4222 # print( 4223 # "nst = {} nfe = {} nsetups = {} nfeLS = {} nje = {}".format( 4224 # nst, nfe, nsetups, nfeLS, nje 4225 # ) 4226 # ) 4227 # print( 4228 # "nni = {} ncfn = {} netf = {} nge = {}\n ".format(nni, ncfn, netf, nge) 4229 # ) 4230 # print('reltol = {}'.format(reltol)) 4231 # print('abstol:\n{}'.format(abstol)) 4232 # 4233 # if cvode.CV_SUCCESS >= 0: 4234 # return output, rates, True 4235 # else: 4236 # return output, rates, False 4237 4238 def CVODE(self, initial): 4239 """ 4240 CVODE(initial) 4241 4242 PySCeS interface to the CVode integration algorithm. Given a set of initial 4243 conditions. 4244 4245 Arguments: 4246 4247 initial: vector containing initial species concentrations 4248 4249 """ 4250 assert ( 4251 _HAVE_ASSIMULO 4252 ), '\nAssimulo is not installed or did not import correctly\n{}'.format( 4253 _ASSIMULO_LOAD_ERROR 4254 ) 4255 Vtemp = numpy.zeros((self.__Nshape__[1]), 'd') 4256 4257 def findi(t, s): 4258 self._TIME_ = t 4259 return self._EvalODE(s, Vtemp) 4260 4261 def ffull(t, s): 4262 self._TIME_ = t 4263 return self._EvalODE_CVODE(s, Vtemp) # unreduced ODE's 4264 4265 if self.mode_integrate_all_odes: 4266 rhs = ffull 4267 else: 4268 rhs = findi 4269 4270 if self.__CVODE_initialise__: 4271 if self.__HAS_RATE_RULES__: 4272 initial, rrules = numpy.split(initial, [self.Nrmatrix.shape[0]]) 4273 if self.__HAS_MOIETY_CONSERVATION__ and self.mode_integrate_all_odes: 4274 initial = self.Fix_S_indinput(initial, amounts=True) 4275 if self.__HAS_RATE_RULES__: 4276 initial = numpy.concatenate([initial, rrules]) 4277 4278 problem = EventsProblem(self, rhs=rhs, y0=initial) 4279 # for direct access to the problem class 4280 self._problem = problem 4281 sim = CVode(problem) 4282 # for direct access to the solver class 4283 self._solver = sim 4284 if self.__settings__["cvode_stats"]: 4285 sim.verbosity = 10 4286 else: 4287 sim.verbosity = 40 4288 sim.atol = self.__settings__["cvode_abstol"] 4289 sim.rtol = self.__settings__["cvode_reltol"] 4290 t, sim_res = sim.simulate(self.sim_end, ncp=0, ncp_list=self.sim_time) 4291 # needed because CVode adds extra time points around discontinuity 4292 t = numpy.array(t) 4293 # divide m.sim_time into segments between event firings 4294 idx = [0] + [numpy.max(numpy.where(t == i)) for i in 4295 problem.event_times] + [len(t)] 4296 4297 # initialise rates array 4298 rates = numpy.zeros((sim_res.shape[0], len(self.__reactions__))) 4299 4300 if ( 4301 self.__HAS_MOIETY_CONSERVATION__ 4302 and not self.mode_integrate_all_odes 4303 ): 4304 # calculate rates from independent species 4305 # re-assign parameters after every event in case they changed 4306 for i in range(len(idx) - 1): 4307 for j in range(len(self.parameters)): 4308 setattr(self, self.parameters[j], problem.parvals[i][j]) 4309 for r in range(idx[i], idx[i + 1]): 4310 self._EvalODE(sim_res[r].copy(), self._CVODE_Vtemp) 4311 rates[r] = self.__vvec__ 4312 if self.__HAS_RATE_RULES__: 4313 sim_res, rrules = numpy.split(sim_res, [self.Nmatrix.shape[0]], axis=1) 4314 # regenerate dependent variables 4315 res = numpy.zeros((sim_res.shape[0], len(self.__species__))) 4316 for x in range(sim_res.shape[0]): 4317 res[x, :] = self.Fix_S_indinput(sim_res[x, :], amounts=True) 4318 sim_res = res 4319 del res 4320 # convert to concentrations 4321 if ( 4322 self.__HAS_COMPARTMENTS__ 4323 and self.__KeyWords__['Output_In_Conc'] 4324 ): 4325 for x in range(0, sim_res.shape[0]): 4326 sim_res[x] = self._SpeciesAmountToConc(sim_res[x]) 4327 if self.__HAS_RATE_RULES__: 4328 sim_res = numpy.concatenate([sim_res, rrules], axis=1) 4329 4330 else: 4331 # calculate rates from all species 4332 # re-assign parameters after every event in case they changed 4333 for i in range(len(idx) - 1): 4334 for j in range(len(self.parameters)): 4335 setattr(self, self.parameters[j], problem.parvals[i][j]) 4336 for r in range(idx[i], idx[i + 1]): 4337 self._EvalODE_CVODE(sim_res[r].copy(), self._CVODE_Vtemp) 4338 rates[r] = self.__vvec__ 4339 if self.__HAS_RATE_RULES__: 4340 sim_res, rrules = numpy.split(sim_res, [self.Nmatrix.shape[0]], axis=1) 4341 if ( 4342 self.__HAS_COMPARTMENTS__ 4343 and self.__KeyWords__['Output_In_Conc'] 4344 ): 4345 for x in range(sim_res.shape[0]): 4346 sim_res[x] = self._SpeciesAmountToConc(sim_res[x]) 4347 if self.__HAS_RATE_RULES__: 4348 sim_res = numpy.concatenate([sim_res, rrules], axis=1) 4349 4350 if self.__settings__['cvode_return_event_timepoints']: 4351 self.sim_time = t 4352 else: 4353 tidx = [numpy.where(t==i)[0][0] for i in self.sim_time] 4354 sim_res = sim_res[tidx] 4355 rates = rates[tidx] 4356 4357 return sim_res, rates, True 4358 4359 def CVODE_VPYTHON(self, s): 4360 """Future VPython hook for CVODE""" 4361 pass 4362 4363 def LSODA(self, initial): 4364 """ 4365 LSODA(initial) 4366 4367 PySCeS interface to the LSODA integration algorithm. Given a set of initial 4368 conditions LSODA returns an array of species concentrations and a status flag. 4369 LSODA controls are accessible as mod.lsoda_<control> 4370 4371 Arguments: 4372 4373 initial: vector containing initial species concentrations 4374 4375 """ 4376 Vtemp = numpy.zeros((self.__Nshape__[1]), 'd') 4377 4378 def function_sim(s, t): 4379 self._TIME_ = t 4380 return self._EvalODE(s, Vtemp) 4381 4382 iter = 0 4383 go = True 4384 4385 status = 0 4386 while go: 4387 sim_res, infodict = scipy.integrate.odeint( 4388 function_sim, 4389 initial, 4390 self.sim_time, 4391 atol=self.__settings__['lsoda_atol'], 4392 rtol=self.__settings__['lsoda_rtol'], 4393 mxstep=self.__settings__["lsoda_mxstep"], 4394 h0=self.__settings__["lsoda_h0"], 4395 hmax=self.__settings__["lsoda_hmax"], 4396 hmin=self.__settings__["lsoda_hmin"], 4397 mxordn=self.__settings__["lsoda_mxordn"], 4398 mxords=self.__settings__["lsoda_mxords"], 4399 printmessg=self.__settings__["lsoda_mesg"], 4400 full_output=1, 4401 ) 4402 if infodict['message'] == 'Integration successful.': 4403 status = 0 4404 else: 4405 status = 1 4406 if status > 0 and iter < self.__settings__['mode_sim_max_iter']: 4407 if self.__settings__["lsoda_mxstep"] == 0: 4408 print( 4409 '\nIntegration error\n\nSetting self.__settings__["lsoda_mxstep"] = 1000 and reSimulating ...' 4410 ) 4411 self.__settings__["lsoda_mxstep"] = 1000 4412 else: 4413 print( 4414 'Integration error\n\nSetting self.__settings__["lsoda_mxstep"] = ' 4415 + repr(self.__settings__["lsoda_mxstep"] * 3) 4416 + ' and reSimulating ...' 4417 ) 4418 self.__settings__["lsoda_mxstep"] = ( 4419 self.__settings__["lsoda_mxstep"] * 3 4420 ) 4421 iter += 1 4422 elif status > 0 and iter == self.__settings__['mode_sim_max_iter']: 4423 print( 4424 '\nThis simulation is going nowhere fast\nConsider trying CVODE (mod.mode_integrator = \'CVODE\')\n' 4425 ) 4426 print( 4427 'self.__settings__["lsoda_mxstep"] = ' 4428 + repr(self.__settings__["lsoda_mxstep"]) 4429 ) 4430 print('__settings__[\'mode_sim_max_iter\'] = ' + repr(iter)) 4431 go = False 4432 else: 4433 go = False 4434 self.__settings__["lsoda_mxstep"] = 0 4435 4436 rates = numpy.zeros((sim_res.shape[0], len(self.__reactions__))) 4437 if status == 0: 4438 4439 tmp = None 4440 for r in range(sim_res.shape[0]): 4441 tmp = self._EvalODE(sim_res[r].copy(), self._CVODE_Vtemp) 4442 # set with self._EvalODE above 4443 rates[r] = self.__vvec__ 4444 del tmp 4445 4446 # regenerate dependent variables 4447 if self.__HAS_RATE_RULES__: 4448 sim_res, rrules = numpy.split(sim_res, [self.Nrmatrix.shape[0]], axis=1) 4449 if self.__HAS_MOIETY_CONSERVATION__ == True: 4450 res = numpy.zeros((sim_res.shape[0], len(self.__species__))) 4451 for x in range(0, sim_res.shape[0]): 4452 res[x, :] = self.Fix_S_indinput(sim_res[x, :], amounts=True) 4453 sim_res = res 4454 del res 4455 if self.__HAS_COMPARTMENTS__ and self.__KeyWords__['Output_In_Conc']: 4456 for x in range(0, sim_res.shape[0]): 4457 sim_res[x] = self._SpeciesAmountToConc(sim_res[x]) 4458 4459 if self.__HAS_RATE_RULES__: 4460 sim_res = numpy.concatenate([sim_res, rrules], axis=1) 4461 return sim_res, rates, True 4462 else: 4463 if self.__HAS_MOIETY_CONSERVATION__ == True and self.__HAS_RATE_RULES__: 4464 sim_res = numpy.zeros( 4465 (sim_res.shape[0], len(self.__species__) + len(self.__rrule__)), 'd' 4466 ) 4467 elif self.__HAS_MOIETY_CONSERVATION__ == True: 4468 sim_res = numpy.zeros((sim_res.shape[0], len(self.__species__)), 'd') 4469 sim_res[:] = scipy.NaN 4470 return sim_res, rates, False 4471 4472 def HYBRD(self, initial): 4473 """ 4474 HYBRD(initial) 4475 4476 PySCeS interface to the HYBRD solver. Returns a steady-state solution and 4477 error flag. Good general purpose solver. 4478 Algorithm controls are available as mod.hybrd_<control> 4479 4480 Arguments: 4481 4482 initial: vector of initial species concentrations 4483 4484 """ 4485 Vtemp = numpy.zeros((self.__Nshape__[1]), 'd') 4486 4487 def function_state(s): 4488 return self._EvalODE(s, Vtemp) 4489 4490 state_out = scipy.optimize.fsolve( 4491 function_state, 4492 initial, 4493 args=(), 4494 xtol=self.__settings__['hybrd_xtol'], 4495 maxfev=self.__settings__['hybrd_maxfev'], 4496 epsfcn=self.__settings__['hybrd_epsfcn'], 4497 factor=self.__settings__['hybrd_factor'], 4498 col_deriv=0, 4499 full_output=1, 4500 ) 4501 if state_out[2] == 1: 4502 if self.__settings__['hybrd_mesg'] == 1: 4503 print('(hybrd)', state_out[3]) 4504 return state_out[0], True 4505 else: 4506 if self.__settings__['hybrd_mesg']: 4507 print('INFO: (hybrd) Invalid steady state:') 4508 if self.__settings__['hybrd_mesg'] == 1: 4509 print('(hybrd)', state_out[3]) 4510 return state_out[0], False 4511 4512 def FINTSLV(self, initial): 4513 """ 4514 FINTSLV(initial) 4515 4516 Forward integration steady-state solver. Finds a steady state when the 4517 maximum change in species concentration falls within a specified tolerance. 4518 Returns the steady-state solution and a error flag. 4519 Algorithm controls are available as mod.fintslv_<control> 4520 4521 Arguments: 4522 4523 initial: vector of initial concentrations 4524 4525 """ 4526 sim_time = self.__fintslv_range__ * self.__settings__['fintslv_rmult'] 4527 # print sim_time 4528 Vtemp = numpy.zeros((self.__Nshape__[1]), 'd') 4529 4530 def function_sim(s, t): 4531 return self._EvalODE(s, Vtemp) 4532 4533 res, infodict = scipy.integrate.odeint( 4534 function_sim, 4535 initial.copy(), 4536 sim_time, 4537 atol=self.__settings__['lsoda_atol'], 4538 rtol=self.__settings__[ 4539 'lsoda_rtol' 4540 ], ## mxstep=self.__settings__["lsoda_mxstep"],\ 4541 mxstep=10000, 4542 h0=self.__settings__["lsoda_h0"], 4543 hmax=self.__settings__["lsoda_hmax"], 4544 hmin=self.__settings__["lsoda_hmin"], 4545 mxordn=self.__settings__["lsoda_mxordn"], 4546 mxords=self.__settings__["lsoda_mxords"], 4547 printmessg=self.__settings__["lsoda_mesg"], 4548 full_output=1, 4549 ) 4550 if infodict['message'] == 'Integration successful.': 4551 status = True 4552 else: 4553 status = False 4554 4555 # run through results if max(abs([x]-[x-1])) < self.__settings__['fintslv_tol'] score +1 4556 # if you get 5 points by seq end ... happiness 4557 OK = 0 4558 if status: 4559 for x in range(len(res)): 4560 if OK >= self.__settings__['fintslv_step']: 4561 break 4562 if x > 0 and OK < self.__settings__['fintslv_step']: 4563 dif = abs(res[x] - res[x - 1]) 4564 if max(dif) < self.__settings__['fintslv_tol']: 4565 OK += 1 4566 else: 4567 pass 4568 if OK >= 5: 4569 return res[-1], True 4570 else: 4571 return res[-1], False 4572 4573 def NLEQ2(self, initial): 4574 """ 4575 NLEQ2(initial) 4576 4577 PySCeS interface to the (optional) NLEQ2 algorithm. This is a powerful steady-state 4578 solver that can usually find a solution for when HYBRD() fails. Algorithm 4579 controls are available as: mod.nleq2_<control> 4580 Returns as steady-state solution and error flag. 4581 4582 Arguments: 4583 4584 initial: vector of initial species concentrations 4585 4586 """ 4587 Vtemp = numpy.zeros((self.__Nshape__[1]), 'd') 4588 initial0 = initial.copy() 4589 s_scale = initial.copy() 4590 4591 N = len(initial) 4592 iwk = numpy.zeros((N + 52), 'i') 4593 rwk = numpy.zeros(((N + max(N, 10) + 15) * N + 61), 'd') 4594 iopt = numpy.zeros((50), 'i') 4595 4596 if self.__settings__['nleq2_jacgen'] == 1: 4597 print( 4598 '(nleq2)User supplied Jacobian not supported yet ... setting __settings__[\'nleq2_jacgen\'] = 0' 4599 ) 4600 self.__settings__['nleq2_jacgen'] = 0 4601 4602 rtol = mach_spec.eps * 10.0 * N 4603 iopt[2] = self.__settings__['nleq2_jacgen'] # 2 4604 iopt[8] = self.__settings__['nleq2_iscaln'] # 0 4605 iopt[10] = 0 4606 iopt[11] = 6 4607 iopt[12] = 0 4608 iopt[13] = 6 4609 iopt[14] = 0 4610 iopt[15] = 6 4611 iopt[30] = self.__settings__['nleq2_nonlin'] # 4 4612 iopt[31] = self.__settings__['nleq2_qrank1'] # 1 4613 iopt[34] = self.__settings__['nleq2_qnscal'] # 0 4614 iopt[37] = self.__settings__['nleq2_ibdamp'] # 0 4615 iopt[38] = self.__settings__['nleq2_iormon'] # 2 4616 4617 iwk[30] = self.nleq2_nitmax 4618 4619 def func(s, ifail): 4620 s = self._EvalODE(s, Vtemp) 4621 if numpy.isnan(s).any(): 4622 ifail = -1 4623 elif (numpy.abs(s) > 1.0e150).any(): 4624 ifail = -1 4625 return s, ifail 4626 4627 def jacfunc(s): 4628 return s 4629 4630 ierr = 0 4631 4632 BRETT_DEBUG_MODE = False 4633 ADVANCED_MODE = self.__settings__['nleq2_advanced_mode'] 4634 if ADVANCED_MODE: 4635 max_iter = self.__settings__['nleq2_iter'] # 3 4636 max_iter_ceiling = self.__settings__['nleq2_iter_max'] # 10 4637 else: 4638 max_iter_ceiling = self.__settings__['nleq2_iter'] 4639 max_iter = self.__settings__['nleq2_iter'] 4640 4641 iter = 1 4642 GO = True 4643 while GO: 4644 if BRETT_DEBUG_MODE: 4645 print('s_scale', s_scale) 4646 print('ierr({}) = {}'.format(iter, ierr)) 4647 print('rtol({}) = {}'.format(iter, rtol)) 4648 print('nitmax({}) = {}'.format(iter, iwk[30])) 4649 print('s_scale({}) = {}'.format(iter, s_scale)) 4650 print('max_iter({}) = {}'.format(iter, max_iter)) 4651 4652 res, s_scale, rtol, iopt, ierr = nleq2.nleq2( 4653 func, jacfunc, initial, s_scale, rtol, iopt, iwk, rwk 4654 ) 4655 4656 if ierr == 0: 4657 # success 4658 GO = False 4659 elif ierr == 21: 4660 # negative rtol 4661 GO = False 4662 ## rtol = abs(rtol) 4663 ## ierr = 0 4664 elif ierr == 2: 4665 # nitmax reached 4666 iwk[30] = iwk[30] * self.__settings__['nleq2_growth_factor'] # 10 4667 4668 if iter >= max_iter and iter >= max_iter_ceiling: 4669 GO = False 4670 iter += 1 4671 4672 if BRETT_DEBUG_MODE and ierr > 0: 4673 print('ierr = {}'.format(ierr)) 4674 print(res) 4675 time.sleep(5) 4676 4677 if ierr > 0: 4678 if self.__settings__['nleq2_mesg']: 4679 print('(nleq2) exits with ierr = {}'.format(ierr)) 4680 else: 4681 if self.__settings__['nleq2_mesg']: 4682 print('(nleq2) The solution converged.') 4683 if ierr > 0: 4684 return res, False 4685 else: 4686 return res, True 4687 4688 def PITCON(self, scanpar, scanpar3d=None): 4689 """ 4690 PITCON(scanpar,scanpar3d=None) 4691 4692 PySCeS interface to the PITCON continuation algorithm. Single parameter 4693 continuation has been implemented as a "scan" with the continuation 4694 being initialised in mod.pitcon_par_space. The second argument does not 4695 affect the continuation but can be used to insert a third axis parameter into 4696 the results. Returns an array containing the results. 4697 Algorithm controls are available as mod.pitcon_<control> 4698 4699 Arguments: 4700 4701 scanpar: the model parameter to scan (x5) 4702 scanpar3d [default=None]: additional output parameter for 3D plots 4703 4704 """ 4705 if self.__HAS_RATE_RULES__: 4706 raise NotImplementedError( 4707 '\nBifurcation analysis not currently available for models containing RateRules' 4708 ) 4709 4710 assert ( 4711 type(scanpar) == str 4712 ), '\nscanpar must be a <string> representing a model parameter' 4713 modpar = list(self.__parameters__) 4714 try: 4715 a = modpar.index(scanpar) 4716 except: 4717 raise NameError(repr(scanpar) + ' is not a parameter of this model') 4718 if scanpar3d != None: 4719 if type(scanpar3d) == str: 4720 scanpar3d = float(scanpar3d) 4721 assert type(scanpar3d) == float, 'scanpar3d must be a <float>' 4722 4723 par_hold = getattr(self, scanpar) 4724 4725 if self.__settings__["pitcon_jac_opt"] < 1: 4726 self.__settings__["pitcon_jac_opt"] = 1 4727 print( 4728 '\nINFO: .__settings__["pitcon_jac_opt"] set to 1 - user defined jacobian function not yet supported' 4729 ) 4730 4731 # DONE! 4732 def fx(s): 4733 setattr(self, scanpar, s[-1]) 4734 try: 4735 sdot[:-1] = self._EvalODE(s[:-1], Vtemp) 4736 except Exception as ex: 4737 print('PITCON EXCEPTION 1', ex) 4738 sdot[:-1] = 0.0 4739 sdot[-1] = s[-1] 4740 return sdot 4741 4742 parm = len(self.__SI__) + 1 4743 Vtemp = numpy.zeros((self.__Nshape__[1]), 'd') 4744 xr2 = numpy.zeros((parm), 'd') 4745 sdot = numpy.zeros((parm), 'd') 4746 iwork = numpy.zeros((30 + parm), 'i') 4747 rwork = numpy.zeros((30 + (6 * (parm)) * (parm)), 'd') 4748 ipar = numpy.zeros((parm), 'i') 4749 fpar = numpy.zeros((parm), 'd') 4750 4751 res = [] 4752 for xscan in self.pitcon_par_space: 4753 Vtemp[:] = 0.0 4754 xr2[:] = 0.0 4755 sdot[:] = 0.0 4756 ipar[:] = 0 4757 fpar[:] = 0.0 4758 4759 iwork[0] = 0 # This is a startup 4760 iwork[1] = self.__settings__[ 4761 "pitcon_init_par" 4762 ] # Use X(1) for initial parameter 4763 iwork[2] = self.__settings__[ 4764 "pitcon_par_opt" 4765 ] # Parameterization option 0:allows program 4766 iwork[3] = self.__settings__[ 4767 "pitcon_jac_upd" 4768 ] # Update jacobian every newton step 4769 iwork[4] = self.__settings__[ 4770 "pitcon_targ_val_idx" 4771 ] # Seek target values for X(n) 4772 iwork[5] = self.__settings__[ 4773 "pitcon_limit_point_idx" 4774 ] # Seek limit points in X(n) 4775 iwork[6] = self.__settings__[ 4776 "pitcon_output_lvl" 4777 ] # Control amount of output. 4778 iwork[7] = 6 # Output unit 6=PC 4779 iwork[8] = self.__settings__[ 4780 "pitcon_jac_opt" 4781 ] # Jacobian choice. 0:supply jacobian,1:use forward difference,2:central difference 4782 iwork[9] = 0 4783 iwork[10] = 0 4784 iwork[11] = 0 4785 iwork[12] = 30 4786 iwork[13] = len(iwork) 4787 iwork[14] = 30 + (4 * parm) 4788 iwork[15] = len(rwork) 4789 iwork[16] = self.__settings__["pitcon_max_step"] # max corrector steps 4790 iwork[17] = 0 4791 iwork[18] = 0 4792 iwork[19] = 0 4793 iwork[20] = 0 4794 iwork[21] = 0 4795 iwork[22] = 0 4796 iwork[23] = 0 4797 iwork[24] = 0 4798 iwork[25] = 0 4799 iwork[26] = 0 4800 iwork[27] = 0 4801 4802 rwork[0] = self.__settings__["pitcon_abs_tol"] # Absolute error tolerance 4803 rwork[1] = self.__settings__["pitcon_rel_tol"] # Relative error tolerance 4804 rwork[2] = self.__settings__["pitcon_min_step"] # Minimum stepsize 4805 rwork[3] = self.__settings__["pitcon_max_step"] # Maximum stepsize 4806 rwork[4] = self.__settings__["pitcon_start_step"] # Starting stepsize 4807 rwork[5] = self.__settings__[ 4808 "pitcon_start_dir" 4809 ] # Starting direction +1.0/-1.0 4810 rwork[ 4811 6 4812 ] = self.pitcon_targ_val # Target value (Seek solution with iwork[4]=) 4813 rwork[7] = 0.0 4814 rwork[8] = 0.0 4815 rwork[9] = 0.0 4816 rwork[10] = 0.0 4817 rwork[11] = 0.0 4818 rwork[12] = 0.0 4819 rwork[13] = 0.0 4820 rwork[14] = 0.0 4821 rwork[15] = 0.0 4822 rwork[16] = 0.0 4823 rwork[17] = 0.0 4824 rwork[18] = 0.0 4825 rwork[19] = self.__settings__["pitcon_max_grow"] # maximum growth factor 4826 rwork[20] = 0.0 4827 rwork[21] = 0.0 4828 rwork[22] = 0.0 4829 rwork[23] = 0.0 4830 rwork[24] = 0.0 4831 rwork[25] = 0.0 4832 rwork[26] = 0.0 4833 rwork[27] = 0.0 4834 rwork[28] = 0.0 4835 4836 setattr(self, scanpar, xscan) 4837 self.State() 4838 4839 if self.__HAS_COMPARTMENTS__ and self.__KeyWords__['Output_In_Conc']: 4840 ## if self.__KeyWords__['Species_In_Conc']: 4841 self.__inspec__ = copy.copy( 4842 self._SpeciesConcToAmount(self.state_species) 4843 ) 4844 else: 4845 self.__inspec__ = copy.copy(self.state_species) 4846 4847 go = False 4848 if self.__StateOK__: 4849 go = True 4850 elif not self.__StateOK__ and self.__settings__["pitcon_allow_badstate"]: 4851 go = True 4852 else: 4853 go = False 4854 4855 if go: 4856 if self.__HAS_MOIETY_CONSERVATION__ == True: 4857 temp = numpy.zeros((len(self.__SI__)), 'd') 4858 # sI0_sim_init 4859 for x in range(0, len(self.lzeromatrix_col)): 4860 xr2[x] = self.__inspec__[self.nrmatrix_row[x]] 4861 else: 4862 xr2[:-1] = copy.copy(self.state_species[:]) 4863 xr2[-1] = copy.copy(xscan) 4864 for x in range(int(self.pitcon_iter)): 4865 ierror, iwork, rwork, xr2 = pitcon.pitcon1( 4866 fx, fpar, fx, ipar, iwork, rwork, xr2 4867 ) 4868 if iwork[0] == 2: 4869 if self.__settings__["pitcon_flux_gen"]: 4870 if ( 4871 min(xr2[:-1]) < 0.0 4872 and self.__settings__["pitcon_filter_neg"] 4873 ): 4874 pass 4875 else: 4876 xout = xr2.tolist() 4877 a = xout.pop(-1) 4878 if self.__HAS_MOIETY_CONSERVATION__ == True: 4879 xout = self.Fix_S_indinput(xout, amounts=True) 4880 else: 4881 xout = numpy.array(xout) 4882 if ( 4883 self.__HAS_COMPARTMENTS__ 4884 and self.__KeyWords__['Output_In_Conc'] 4885 ): 4886 xout = self._SpeciesAmountToConc(xout) 4887 xout2 = (self.__FluxGen__(xout)).tolist() 4888 xout = xout.tolist() 4889 xout.insert(0, a) 4890 if scanpar3d != None: 4891 xout.insert(0, scanpar3d) 4892 xout2 = xout + xout2 4893 res.append(xout2) 4894 else: 4895 if ( 4896 min(xr2[:-1]) < 0.0 4897 and self.__settings__["pitcon_filter_neg"] 4898 ): 4899 pass 4900 else: 4901 xout = xr2.tolist() 4902 a = xout.pop(-1) 4903 if self.__HAS_MOIETY_CONSERVATION__ == True: 4904 xout = self.Fix_S_indinput(xout, amounts=True) 4905 else: 4906 xout = numpy.array(xout) 4907 if ( 4908 self.__HAS_COMPARTMENTS__ 4909 and self.__KeyWords__['Output_In_Conc'] 4910 ): 4911 xout = self._SpeciesAmountToConc(xout) 4912 xout = xout.tolist() 4913 xout.insert(0, a) 4914 if scanpar3d != None: 4915 xout.insert(0, scanpar3d) 4916 res.append(xout) 4917 elif iwork[0] == 3: 4918 print('\nTarget point:') 4919 xout = xr2.tolist() 4920 a = xout.pop(-1) 4921 4922 if self.__HAS_MOIETY_CONSERVATION__ == True: 4923 xout = self.Fix_S_indinput(xout, amounts=True) 4924 else: 4925 xout = numpy.array(xout) 4926 if ( 4927 self.__HAS_COMPARTMENTS__ 4928 and self.__KeyWords__['Output_In_Conc'] 4929 ): 4930 xout = self._SpeciesAmountToConc(xout) 4931 xout = xout.tolist() 4932 xout.insert(0, a) 4933 4934 print(xout) 4935 self.pitcon_target_points.append(xout) 4936 elif iwork[0] == 4: 4937 print('\nLimit point') 4938 xout = xr2.tolist() 4939 a = xout.pop(-1) 4940 4941 if self.__HAS_MOIETY_CONSERVATION__ == True: 4942 xout = self.Fix_S_indinput(xout, amounts=True) 4943 else: 4944 xout = numpy.array(xout) 4945 if ( 4946 self.__HAS_COMPARTMENTS__ 4947 and self.__KeyWords__['Output_In_Conc'] 4948 ): 4949 xout = self._SpeciesAmountToConc(xout) 4950 xout = xout.tolist() 4951 xout.insert(0, a) 4952 4953 print(xout) 4954 self.pitcon_limit_points.append(xout) 4955 elif iwork[0] == 1: 4956 pass 4957 else: 4958 print(iwork[0]) 4959 # raw_input() 4960 else: 4961 print('\nInvalid steady state, skipping ...') 4962 4963 if self.__settings__["pitcon_filter_neg_res"]: 4964 for result in range(len(res) - 1, -1, -1): 4965 if min(res[result][: len(self.species) + 1]) < 0.0: 4966 res.pop(result) 4967 setattr(self, scanpar, par_hold) 4968 return numpy.array(res) 4969 4970 def Simulate(self, userinit=0): 4971 """ 4972 PySCeS integration driver routine that evolves the system over the time. 4973 Resulting array of species concentrations is stored in the **mod.data_sim** object 4974 Initial concentrations can be selected using *mod.__settings__['mode_sim_init']* 4975 (default=0): 4976 4977 - 0 initialise with intial concentrations 4978 - 1 initialise with a very small (close to zero) value 4979 - 2 initialise with results of previously calculated steady state 4980 - 3 initialise with final point of previous simulation 4981 4982 *userinit* values can be (default=0): 4983 4984 - 0: initial species concentrations intitialised from (mod.S_init), 4985 time array calculated from sim_start/sim_end/sim_points 4986 - 1: intial species concentrations intitialised from (mod.S_init) existing 4987 "mod.sim_time" used directly 4988 - 2: initial species concentrations read from "mod.__inspec__", 4989 "mod.sim_time" used directly 4990 """ 4991 # check if stoichiometry has been adjusted using Stoich_nmatrix_SetValue 4992 # - brett 20050719 4993 if not self.__StoichOK: 4994 self.Stoichiometry_ReAnalyse() 4995 4996 # check for zero first point in user-supplied mod.sim_time, add if needed 4997 self._sim_time_bak = None 4998 if userinit != 0: 4999 if self.sim_time[0] != 0: 5000 self._sim_time_bak = copy.copy(self.sim_time) 5001 self.sim_time = [0.0] + list(self._sim_time_bak) 5002 5003 # initialises self.__inspec__[x] with self.sXi 5004 if userinit == 1: 5005 eval(self.__mapFunc_R__) 5006 elif userinit == 2: 5007 try: 5008 assert len(self.__inspec__) == len(self.__species__) 5009 except: 5010 print( 5011 '\nINFO: mod.__inspec__ is the incorrect length, initialising with .sX_init' 5012 ) 5013 self.__inspec__ = numpy.zeros(len(self.__species__)) 5014 eval(self.__mapFunc_R__) 5015 else: 5016 self.sim_start = float(self.sim_start) 5017 self.sim_end = float(self.sim_end) 5018 self.sim_points = int(self.sim_points) 5019 if self.sim_points == 1.0: 5020 print( 5021 '*****\nWARNING: simulations require a minimum of 2 points,\ 5022setting sim_points = 2.0\n*****' 5023 ) 5024 self.sim_points = 2.0 5025 self.sim_time = numpy.linspace( 5026 self.sim_start, self.sim_end, self.sim_points, endpoint=True, retstep=False 5027 ) 5028 eval(self.__mapFunc_R__) 5029 5030 # initialise __rrule__ to mod.<rule>_init 5031 if self.__HAS_RATE_RULES__: 5032 for r in range(len(self.__rate_rules__)): 5033 self.__rrule__[r] = getattr( 5034 self, '{}_init'.format(self.__rate_rules__[r]) 5035 ) 5036 for c in range(len(self.__CsizeAllIdx__)): 5037 cval = getattr(self, '{}_init'.format(self.__CsizeAllIdx__[c])) 5038 setattr(self, self.__CsizeAllIdx__[c], cval) 5039 self.__CsizeAll__[c] = cval 5040 # set initialisation array to amounts 5041 if self.__HAS_COMPARTMENTS__ and self.__KeyWords__['Species_In_Conc']: 5042 self.__inspec__ = self._SpeciesConcToAmount(self.__inspec__) 5043 5044 # set mod.<species> to corrected mod.<species>_init value 5045 for s in range(len(self.__species__)): 5046 setattr(self, self.__species__[s], self.__inspec__[s]) 5047 5048 # This should work ok __Build_Tvec__ uses .self.__inspec__ to create Tvec 5049 if self.__HAS_MOIETY_CONSERVATION__ == True: 5050 self.__Build_Tvec__(amounts=True) 5051 self.showConserved(screenwrite=0) 5052 if self.__settings__['display_debug'] == 1: 5053 print(self.conserved_sums) 5054 5055 # Initialise the simulation ... 5056 if self.__settings__['mode_sim_init'] == 0: 5057 # Start with s[x] at their initial values 5058 s0_sim_init = copy.copy(self.__inspec__) 5059 elif self.__settings__['mode_sim_init'] == 1: 5060 # Start with s[x] at zero ... well close to it anyway 5061 s0_sim_init = copy.copy(self.__inspec__) 5062 s0_sim_init[:] = self.__settings__['small_concentration'] 5063 elif self.__settings__['mode_sim_init'] == 2: 5064 # Start with s[x] at the previous steady state if it exists and check if s[x] < 0.0 5065 # if so set that value to 1.0e-10 5066 if self.state_species != None: 5067 s0_sim_init = copy.copy(self.state_species) * 1.0 5068 for x in range(len(s0_sim_init)): 5069 if s0_sim_init[x] < 0.0: 5070 s0_sim_init[x] = self.__settings__['small_concentration'] 5071 print( 5072 'Negative concentration detected in SimInit: s[' 5073 + repr(x) 5074 + '] set to ' 5075 + repr(self.__settings__['small_concentration']) 5076 ) 5077 else: 5078 s0_sim_init = copy.copy(self.__inspec__) 5079 elif self.__settings__['mode_sim_init'] == 3: 5080 # Start with s[x] at the final point of the previous simulation if exists -- johann 20050220 5081 try: 5082 s0_sim_init = copy.copy(self.data_sim.species[-1]) 5083 except: 5084 s0_sim_init = copy.copy(self.__inspec__) 5085 else: 5086 s0_sim_init = copy.copy(self.__inspec__) 5087 5088 # print 'Sim debug' 5089 # print s0_sim_init 5090 if self.__HAS_MOIETY_CONSERVATION__ == True: 5091 temp = numpy.zeros((len(self.__SI__)), 'd') 5092 for x in range(0, len(self.lzeromatrix_col)): 5093 temp[x] = s0_sim_init[self.nrmatrix_row[x]] 5094 s0_sim_init = temp 5095 del temp 5096 # print s0_sim_init 5097 5098 # ok so now we add RateRules to the initialisation_vec and see what happens 5099 if self.__HAS_RATE_RULES__: 5100 s0_sim_init = numpy.concatenate([s0_sim_init, self.__rrule__]) 5101 5102 # re-set self._sim recarray (otherwise self.sim is not updated) 5103 self._sim = None 5104 5105 # real pluggable integration routines - brett 2007 5106 # the array copy is important ... brett leave it alone! 5107 Tsim0 = time.time() 5108 if self.mode_integrator == 'LSODA': 5109 sim_res, rates, simOK = self.LSODA(copy.copy(s0_sim_init)) 5110 elif self.mode_integrator == 'CVODE': 5111 sim_res, rates, simOK = self.CVODE(copy.copy(s0_sim_init)) 5112 # remove zero point from reported simulation data if necessary 5113 if self._sim_time_bak is not None: 5114 self.sim_time = copy.copy(self._sim_time_bak) 5115 sim_res = sim_res[1:] 5116 rates = rates[1:] 5117 Tsim1 = time.time() 5118 if self.__settings__['lsoda_mesg']: 5119 print( 5120 "{} time for {} points: {}".format( 5121 self.mode_integrator, len(self.sim_time), Tsim1 - Tsim0 5122 ) 5123 ) 5124 5125 if self.__HAS_RATE_RULES__: 5126 sim_res, rrules = numpy.split(sim_res, [len(self.__species__)], axis=1) 5127 print('RateRules evaluated and added to mod.data_sim.') 5128 5129 self.data_sim = IntegrationDataObj() 5130 self.IS_VALID = simOK 5131 self.data_sim.setTime(self.sim_time) 5132 self.data_sim.setSpecies(sim_res, self.__species__) 5133 self.data_sim.setRates(rates, self.__reactions__) 5134 if self.__HAS_RATE_RULES__: 5135 self.data_sim.setRules(rrules, self.__rate_rules__) 5136 if len(self.CVODE_extra_output) > 0: 5137 self.data_sim.setXData(self.CVODE_xdata, lbls=self.CVODE_extra_output) 5138 self.CVODE_xdata = None 5139 if not simOK: 5140 print('Simulation failure') 5141 del sim_res 5142 5143 @property 5144 def sim(self): 5145 if self._sim is None and self.data_sim is not None: 5146 data = self.data_sim.getAllSimData(lbls=True) 5147 self._sim = numpy.rec.fromrecords(data[0], names=data[1]) 5148 return self._sim 5149 5150 def State(self): 5151 """ 5152 State() 5153 5154 PySCeS non-linear solver driver routine. Solve for a steady state using HYBRD/NLEQ2/FINTSLV 5155 algorithms. Results are stored in mod.state_species and mod.state_flux. The results 5156 of a steady-state analysis can be viewed with the mod.showState() method. 5157 5158 The solver can be initialised in 3 ways using the mode_state_init switch. 5159 mod.mode_state_init = 0 initialize with species initial values 5160 mod.mode_state_init = 1 initialize with small values 5161 mod.mode_state_init = 2 initialize with the final value of a 10-logstep simulation numpy.logspace(0,5,18) 5162 5163 Arguments: 5164 None 5165 5166 """ 5167 # check if the stoichiometry has been adjusted using Stoich_nmatrix_SetValue - brett 20050719 5168 if not self.__StoichOK: 5169 self.Stoichiometry_ReAnalyse() 5170 5171 self.__StateOK__ = True 5172 5173 # function to feed the simulation routine if used to initialise the solver 5174 Vtemp = numpy.zeros((self.__Nshape__[1]), 'd') 5175 5176 def function_sim(s, t): 5177 return self._EvalODE(s, Vtemp) 5178 5179 # set self.__inspec__ to current Xi values 5180 eval(self.__mapFunc_R__) 5181 5182 # initialise __rrule__ to mod.<rule>_init 5183 if self.__HAS_RATE_RULES__: 5184 for r in range(len(self.__rate_rules__)): 5185 self.__rrule__[r] = getattr( 5186 self, '{}_init'.format(self.__rate_rules__[r]) 5187 ) 5188 # set compartment values to initial values 5189 for c in range(len(self.__CsizeAllIdx__)): 5190 cval = getattr(self, '{}_init'.format(self.__CsizeAllIdx__[c])) 5191 setattr(self, self.__CsizeAllIdx__[c], cval) 5192 self.__CsizeAll__[c] = cval 5193 # set initialisation array to amounts 5194 if self.__HAS_COMPARTMENTS__ and self.__KeyWords__['Species_In_Conc']: 5195 self.__inspec__ = self._SpeciesConcToAmount(self.__inspec__) 5196 # set mod.<species> to corrected mod.<species>_init value 5197 for s in range(len(self.__species__)): 5198 setattr(self, self.__species__[s], self.__inspec__[s]) 5199 # This should work ok __Build_Tvec__ uses .self.__inspec__ to create Tvec 5200 if self.__HAS_MOIETY_CONSERVATION__ == True: 5201 self.__Build_Tvec__(amounts=True) 5202 self.showConserved(screenwrite=0) 5203 if self.__settings__['display_debug'] == 1: 5204 print(self.conserved_sums) 5205 5206 # clear the solver initialisation array 5207 s0_ss_init = None 5208 # save the __settings__['hybrd_factor'] 5209 hybrd_factor_temp = copy.copy(self.__settings__['hybrd_factor']) 5210 5211 # use StateInit to initialise the solver with either 0:initval 1:zeroval 2:sim 3:1%state 5212 # in all cases fallback to init 5213 # initialising to previous ss seems problematic so I use |10%| of previous - brett 5214 if self.mode_state_init == 0: 5215 # Use initial S values 5216 s0_ss_init = self.__inspec__.copy() 5217 elif self.mode_state_init == 1: 5218 # Use almost zero values 1.0e-8 any smaller seems to cause a problem 5219 # this is user defineable option --> model.ZeroVal - brett 20040121 5220 s0_ss_init = self.__inspec__.copy() 5221 s0_ss_init[:] = self.__settings__['small_concentration'] 5222 elif self.mode_state_init == 2: 5223 print('This initialisation mode has been disabled, using initial values.') 5224 s0_ss_init = self.__inspec__.copy() 5225 ## # Perform a 10-logstep simulation numpy.logspace(0,5,18) and initialise solver with final value 5226 ## # This is guesstimate ... if anyone has a better idea please let me know 5227 ## # it should and be a user defineable array (min/max/len)- brett 20031215 5228 ## self.__settings__['hybrd_factor'] = 10 5229 ## try: 5230 ## s0_ss_init = scipy.integrate.odeint(function_sim,self.__inspec__.tolist(),self.__mode_state_init2_array__,10000,full_output=0) 5231 ## if self.__HAS_MOIETY_CONSERVATION__ == True: 5232 ## s0_ss_init = self.Fix_S_fullinput(s0_ss_init[-1], amounts=True) 5233 ## else: 5234 ## s0_ss_init = s0_ss_init[-1] 5235 ## except: 5236 ## s0_ss_init = copy.copy(self.__inspec__) 5237 elif self.mode_state_init == 3: 5238 # Use |10%| of previous steady-state - seems to be safe, needs more testing and probably professional help 5239 # My rationale for this is "a set of approximate values where all variables are relatively close" 5240 # without having the overhead of a simulation ... 5241 # will eventually be a user option so that it can be +/- x% previous steadystate - brett 20031215 5242 # Note: hybrid doesn't seem to like to start too close to its solution more than 10% is dodgy 5243 if ( 5244 self.state_species != None 5245 and self.__HAS_COMPARTMENTS__ 5246 and self.__KeyWords__['Output_In_Conc'] 5247 ): 5248 s0_ss_init = self._SpeciesConcToAmount( 5249 abs(copy.copy(self.state_species)) 5250 * float(self.__settings__['mode_state_init3_factor']) 5251 ) 5252 else: 5253 s0_ss_init = copy.copy(self.__inspec__) 5254 else: 5255 s0_ss_init = copy.copy(self.__inspec__) 5256 # reset __settings__['hybrd_factor'] 5257 self.__settings__['hybrd_factor'] = hybrd_factor_temp 5258 5259 # print 'State debug' 5260 # print s0_ss_init 5261 # form an initialisation vector of independent species 5262 if self.__HAS_MOIETY_CONSERVATION__ == True: 5263 temp = numpy.zeros((len(self.__SI__)), 'd') 5264 for x in range(0, len(self.lzeromatrix_col)): 5265 temp[x] = s0_ss_init[self.nrmatrix_row[x]] 5266 # add RateRules to the initialisation_vec and see what happens 5267 if self.__HAS_RATE_RULES__: 5268 temp = numpy.concatenate([temp, self.__rrule__]) 5269 s0_ss_init = temp 5270 del temp 5271 # print s0_ss_init 5272 5273 available_solvers = ['HYBRD'] 5274 5275 # set mode_solver to new syntax for backwards compatibility only 5276 if self.mode_solver == 0: 5277 self.mode_solver = 'HYBRD' 5278 elif self.mode_solver == 1: 5279 self.mode_solver = 'FINTSLV' 5280 elif self.mode_solver == 2: 5281 self.mode_solver = 'NLEQ2' 5282 5283 # check for nleq2 and add if available this should be first 5284 if nleq2_switch == 1: 5285 available_solvers.append('NLEQ2') 5286 else: 5287 if self.mode_solver == 'NLEQ2': 5288 self.mode_solver = 'HYBRD' 5289 print( 5290 'INFO: switching to HYBRD.\nNleq2 solver not available see /nleq/readme.txt for details' 5291 ) 5292 5293 # ******* OTHER SOLVERS GO IN HERE ******* 5294 5295 # ******* OTHER SOLVERS GO IN HERE ******* 5296 5297 # shall we add HYBRD to fallback? this should be last 5298 if self.__settings__['mode_solver_fallback_integration']: 5299 available_solvers.append('FINTSLV') 5300 5301 if not self.mode_solver_fallback == 1: 5302 assert ( 5303 self.mode_solver in available_solvers 5304 ), '\nERROR: {} is not a valid ({}) solver!'.format( 5305 solver, str(available_solvers) 5306 ) 5307 available_solvers = [self.mode_solver] 5308 5309 # if solver other than HYBRD is selected move it to front of list so it is run first 5310 if self.mode_solver != 'HYBRD': 5311 available_solvers.insert( 5312 0, available_solvers.pop(available_solvers.index(self.mode_solver)) 5313 ) 5314 5315 STATE_XOUT = False 5316 STATE_xdata = None 5317 if len(self.STATE_extra_output) > 0: 5318 out = [] 5319 for d in self.STATE_extra_output: 5320 if ( 5321 hasattr(self, d) 5322 and d 5323 not in self.__species__ + self.__reactions__ + self.__rate_rules__ 5324 ): 5325 out.append(d) 5326 else: 5327 print( 5328 '\nWARNING: STATE is ignoring extra data ({}), it either doesn\'t exist or it\'s a species, rate or rule.\n'.format( 5329 d 5330 ) 5331 ) 5332 if len(out) > 0: 5333 self.STATE_extra_output = out 5334 STATE_XOUT = True 5335 del out 5336 5337 self.__StateOK__ = True 5338 state_species = None 5339 rrules = None 5340 state_flux = None 5341 for solver in available_solvers: 5342 if solver == 'HYBRD': 5343 state_species, self.__StateOK__ = self.HYBRD(s0_ss_init.copy()) 5344 elif solver == 'NLEQ2': 5345 state_species, self.__StateOK__ = self.NLEQ2(s0_ss_init.copy()) 5346 elif solver == 'FINTSLV': 5347 state_species, self.__StateOK__ = self.FINTSLV(s0_ss_init.copy()) 5348 # In case of a number (scalar) output from the solver 5349 if numpy.isscalar(state_species): 5350 state_species = numpy.array([state_species], 'd') 5351 5352 # this gets everything into the current tout state 5353 tmp = self._EvalODE(state_species.copy(), Vtemp) 5354 state_flux = self.__vvec__ 5355 5356 if self.__HAS_RATE_RULES__: 5357 state_species, rrules = numpy.split( 5358 state_species, [self.Nrmatrix.shape[0]] 5359 ) 5360 # test for negative concentrations 5361 if (state_species < 0.0).any(): 5362 self.__StateOK__ = False 5363 if self.__settings__['mode_state_mesg']: 5364 print('WARNING!! Negative concentrations detected.') 5365 if self.__StateOK__: 5366 break 5367 else: 5368 if ( 5369 self.mode_solver_fallback 5370 and self.__settings__['solver_switch_warning'] 5371 ): 5372 slv_idx = available_solvers.index(solver) 5373 if slv_idx != len(available_solvers) - 1: 5374 print( 5375 'INFO: STATE is switching to {} solver.'.format( 5376 available_solvers[slv_idx + 1] 5377 ) 5378 ) 5379 else: 5380 print('INFO: STATE calculation failed!') 5381 if STATE_XOUT: 5382 STATE_xdata = self._EvalExtraData(self.STATE_extra_output) 5383 5384 # the current status quo is all state algorithms will use and produce results 5385 # in amounts and autoconversion to and from species takes place in the calling 5386 # algorithms -- brett2008 5387 # THIS IS OPPOSITE TO THE SIMULATE METHOD brett - again 5388 5389 if self.__HAS_MOIETY_CONSERVATION__ == True: 5390 state_species = self.Fix_S_indinput(state_species, amounts=True) 5391 if self.__HAS_COMPARTMENTS__ and self.__KeyWords__['Output_In_Conc']: 5392 self.state_species = self._SpeciesAmountToConc(state_species.copy()) 5393 else: 5394 self.state_species = state_species 5395 5396 self.state_flux = state_flux 5397 5398 del state_species, state_flux 5399 5400 # final check for a bad state set check if fluxes are == mach_eps 5401 # this is almost never going to be true 5402 if (self.state_flux == self.__settings__['mach_floateps']).any(): 5403 print('\nWARNING: extremely small flux detected! proceed with caution:') 5404 print(self.state_flux) 5405 ## self.__StateOK__ = False 5406 5407 if not self.__StateOK__: 5408 print( 5409 '\n***\nWARNING: invalid steady state solution (species concentrations and fluxes)\n***\n' 5410 ) 5411 if self.__settings__['mode_state_nan_on_fail']: 5412 self.state_species[:] = numpy.NaN 5413 self.state_flux[:] = numpy.NaN 5414 5415 # set the instance steady state flux and species attributes 5416 self.SetStateSymb(self.state_flux, self.state_species) 5417 5418 # coming soon to a terminal near you 5419 self.data_sstate = StateDataObj() 5420 self.data_sstate.setSpecies(self.state_species, self.__species__) 5421 self.data_sstate.setFluxes(self.state_flux, self.__reactions__) 5422 if self.__HAS_RATE_RULES__: 5423 self.data_sstate.setRules(rrules, self.__rate_rules__) 5424 if STATE_XOUT: 5425 self.data_sstate.setXData(STATE_xdata, lbls=self.STATE_extra_output) 5426 del STATE_xdata 5427 5428 self.data_sstate.IS_VALID = self.__StateOK__ 5429 5430 if self.__settings__['display_debug'] == 1: 5431 print('self.state_species') 5432 print(self.state_species) 5433 print('self.state_flux') 5434 print(self.state_flux) 5435 5436 # driver routines that support the core routines 5437 # core support 5438 5439 # takes the flux and species arrays and 5440 # assigns them as instances variable self.Jx and self.Xss 5441 # I'm not sure if anyone wants to use this in real life so it might be hidden at some point - brett 20040122 5442 # gone - brett 20040506 ... back brett 20040720 5443 def SetStateSymb(self, flux, metab): 5444 """ 5445 SetStateSymb(flux,metab) 5446 5447 Sets the individual steady-state flux and concentration attributes as 5448 mod.J_<reaction> and mod.<species>_ss 5449 5450 Arguments: 5451 5452 flux: the steady-state flux array 5453 metab: the steady-state concentration array 5454 5455 """ 5456 for x in range(0, len(self.state_species)): 5457 setattr(self, self.__species__[x] + '_ss', metab[x]) 5458 5459 for x in range(0, len(self.state_flux)): 5460 setattr(self, 'J_' + self.__reactions__[x], flux[x]) 5461 5462 # uses __inspec__ to build Tvec 5463 def __Build_Tvec__(self, amounts=True): 5464 """ 5465 __Build_Tvec_(concs=False) 5466 5467 Creates vectors of conserved moiety totals from __inspec__ 5468 self.__tvec_a__ = amounts 5469 self.__tvec_c__ = concentrations 5470 5471 Arguments: 5472 None 5473 5474 """ 5475 if self.__HAS_MOIETY_CONSERVATION__ == True: 5476 if amounts: 5477 self.__tvec_a__ = numpy.add.reduce( 5478 copy.copy(self.__reordered_lcons) * self.__inspec__, 1 5479 ) 5480 self.__tvec_c__ = numpy.add.reduce( 5481 copy.copy(self.__reordered_lcons) 5482 * self._SpeciesAmountToConc(self.__inspec__), 5483 1, 5484 ) 5485 else: 5486 self.__tvec_c__ = numpy.add.reduce( 5487 copy.copy(self.__reordered_lcons) * self.__inspec__, 1 5488 ) 5489 self.__tvec_a__ = numpy.add.reduce( 5490 copy.copy(self.__reordered_lcons) 5491 * self._SpeciesConcToAmount(self.__inspec__), 5492 1, 5493 ) 5494 5495 def showConserved(self, File=None, screenwrite=1, fmt='%2.3f'): 5496 """ 5497 showConserved(File=None,screenwrite=1,fmt='%2.3f') 5498 5499 Print the moiety conserved cycles present in the system. 5500 5501 Arguments: 5502 5503 File [default=None]: an open writable Python file object 5504 screenwrite [default=1]: write results to console (0 means no reponse) 5505 fmt [default='%2.3f']: the output number format string 5506 5507 """ 5508 if self.__HAS_MOIETY_CONSERVATION__ == True: 5509 Tlist = list(range(0, len(self.__tvec_a__))) 5510 if Tlist != []: 5511 ConSumPstr = '' 5512 for x in range(0, len(Tlist)): 5513 for y in range(0, len(self.conservation_matrix_col)): 5514 if self.conservation_matrix[Tlist[x], y] > 0.0: 5515 # coeff = self.__settings__['mode_number_format'] % (s_init[y]*abs(conservation_matrix[Tlist[x],y])) 5516 coeff = fmt % abs(self.conservation_matrix[Tlist[x], y]) 5517 met = self._D_s_Order[self.conservation_matrix_col[y]] 5518 ConSumPstr += ( 5519 ' + {' + coeff + '}' + met.replace('self.', '') 5520 ) 5521 elif self.conservation_matrix[Tlist[x], y] < 0.0: 5522 # coeff = self.__settings__['mode_number_format'] % (s_init[y]*abs(conservation_matrix[Tlist[x],y])) 5523 coeff = fmt % abs(self.conservation_matrix[Tlist[x], y]) 5524 met = self._D_s_Order[self.conservation_matrix_col[y]] 5525 ConSumPstr += ( 5526 ' - {' + coeff + '}' + met.replace('self.', '') 5527 ) 5528 if ( 5529 self.__HAS_COMPARTMENTS__ 5530 and self.__KeyWords__['Output_In_Conc'] 5531 ): 5532 ConSumPstr += ( 5533 ' = ' 5534 + self.__settings__['mode_number_format'] 5535 % self.__tvec_c__[Tlist[x]] 5536 + '\n' 5537 ) 5538 else: 5539 ConSumPstr += ( 5540 ' = ' 5541 + self.__settings__['mode_number_format'] 5542 % self.__tvec_a__[Tlist[x]] 5543 + '\n' 5544 ) 5545 self.conserved_sums = ConSumPstr 5546 else: 5547 self.conserved_sums = 'No moiety conservation' 5548 5549 if File != None: 5550 print('\nConserved relationships') 5551 # assert type(File) == file, 'showConserved() needs an open file object' 5552 File.write('\n## Conserved relationships\n') 5553 File.write(self.conserved_sums) 5554 elif screenwrite: 5555 print('\nConserved relationships') 5556 print(self.conserved_sums) 5557 5558 def showFluxRelationships(self, File=None): 5559 """ 5560 showConserved(File=None) 5561 5562 Print the flux relationships present in the system. 5563 5564 Arguments: 5565 5566 File [default=None]: an open writable Python file object 5567 5568 """ 5569 Ostr = '' 5570 for row in range(self.__kzeromatrix__.shape[0]): 5571 Ostr += "{} =".format(self.reactions[self.kzeromatrix_row[row]]) 5572 for col in range(self.__kzeromatrix__.shape[1]): 5573 if self.__kzeromatrix__[row, col] != 0.0: 5574 if self.__kzeromatrix__[row, col] > 0.0: 5575 Ostr += " + {%2.2f}%s" % ( 5576 abs(self.__kzeromatrix__[row, col]), 5577 self.reactions[self.kzeromatrix_col[col]], 5578 ) 5579 else: 5580 Ostr += " - {%2.2f}%s" % ( 5581 abs(self.__kzeromatrix__[row, col]), 5582 self.reactions[self.kzeromatrix_col[col]], 5583 ) 5584 Ostr += '\n' 5585 5586 if File != None: 5587 print('\nFlux relationships') 5588 # assert type(File) == file, 'showConserved() needs an open file object' 5589 File.write('\n## Flux relationships\n') 5590 File.write(Ostr) 5591 else: 5592 print('\nFlux relationships') 5593 print(Ostr) 5594 5595 # Calculate dependant variables done directly in EvalREq2 this is a utility version 5596 def Fix_S_fullinput(self, s_vec, amounts=True): 5597 """ 5598 Fix_S_fullinput(s_vec) 5599 5600 Using the full concentration vector evaluate the dependent species 5601 5602 Arguments: 5603 5604 s_vec: a full length concentration vector 5605 5606 """ 5607 # s_vec = copy.copy(s) 5608 for x in range(0, len(self.lzeromatrix_col)): 5609 self.__SI__[x] = s_vec[self.lzeromatrix_col[x]] 5610 5611 for x in range(0, len(self.lzeromatrix_row)): 5612 if amounts: 5613 s_vec[self.lzeromatrix_row[x]] = self.__tvec_a__[x] + numpy.add.reduce( 5614 self.__lzeromatrix__[x, :] * self.__SI__ 5615 ) # there might be a way to reduce the 2 for loops 5616 else: 5617 s_vec[self.lzeromatrix_row[x]] = self.__tvec_c__[x] + numpy.add.reduce( 5618 self.__lzeromatrix__[x, :] * self.__SI__ 5619 ) # there might be a way to reduce the 2 for loops 5620 5621 return s_vec 5622 5623 # Calculate dependant variables done directly in EvalREq2B this is a utility version 5624 def Fix_S_indinput(self, s_vec, amounts=True): 5625 """ 5626 Fix_S_indinput(s_vec, amounts=True) 5627 whether to use self.__tvec_a__ (default) 5628 or self.__tvec_c__ 5629 5630 Given a vector of independent species evaluate and return a full concentration vector. 5631 5632 Arguments: 5633 5634 s_vec: vector of independent species 5635 5636 """ 5637 # stick SI into s using Nrrow order 5638 for x in range(len(s_vec)): 5639 self.__SALL__[self.nrmatrix_row[x]] = s_vec[x] 5640 # stick SD into s using lzerorow order 5641 for x in range(len(self.lzeromatrix_row)): 5642 if amounts: 5643 self.__SALL__[self.lzeromatrix_row[x]] = self.__tvec_a__[ 5644 x 5645 ] + numpy.add.reduce(self.__lzeromatrix__[x, :] * s_vec) 5646 else: 5647 self.__SALL__[self.lzeromatrix_row[x]] = self.__tvec_c__[ 5648 x 5649 ] + numpy.add.reduce(self.__lzeromatrix__[x, :] * s_vec) 5650 return copy.copy(self.__SALL__) 5651 5652 # output support routines 5653 5654 # Quick and dirty flux regeneration uses the Rx form of the RE's to cater for potential conservation 5655 # caters for both vectors and arrays and is conservation aware 5656 def __FluxGen__(self, s): 5657 """ 5658 **Deprecating** only used by **PITCON** 5659 5660 s: species vector/array 5661 """ 5662 Vtemp = numpy.zeros((self.__Nshape__[1]), 'd') 5663 try: 5664 s.shape[0] 5665 Vout = numpy.zeros((len(s), len(self.__vvec__)), 'd') 5666 for x in range(len(s)): 5667 if ( 5668 self.__HAS_MOIETY_CONSERVATION__ == True 5669 ): # depending if there is conservation -- N.L_switch is: 0 for none or 1 for present 5670 Vout[x, :] = self._EvalREq2_alt(s[x, :], Vtemp) 5671 elif self.__HAS_MOIETY_CONSERVATION__ == False: 5672 Vout[x, :] = self._EvalREq(s[x, :], Vtemp) 5673 except: 5674 if ( 5675 self.__HAS_MOIETY_CONSERVATION__ == True 5676 ): # depending if there is conservation -- N.L_switch is: 0 for none or 1 for present 5677 Vout = self._EvalREq2_alt(s, Vtemp) 5678 elif self.__HAS_MOIETY_CONSERVATION__ == False: 5679 Vout = self._EvalREq(s, Vtemp) 5680 return Vout 5681 5682 def FluxGenSim(self, s): 5683 """ 5684 **Deprecated** 5685 """ 5686 pass 5687 5688 def ParGenSim(self): 5689 """ 5690 **Deprecated** 5691 """ 5692 pass 5693 5694 def Fix_Sim(self, metab, flux=0, par=0): 5695 """ 5696 **Deprecated** 5697 """ 5698 pass 5699 5700 # MCA routines 5701 # elasticity routines 5702 5703 def EvalEvar(self, input=None, input2=None): 5704 """ 5705 EvalEvar(input=None,input2=None) 5706 5707 Calculate reaction elasticities towards the variable species. 5708 5709 Both inputs (input1=species,input2=rates) should be valid (steady state for MCA) solutions and given in the correct order for them to be used. If either or both are missing the last state values are used automatically. 5710 Elasticities are scaled using input 1 and 2. 5711 5712 Arguments:: 5713 5714 - input [default=None]: species concentration vector 5715 - input2 [default=None]: reaction rate vector 5716 5717 Settings, set in mod.__settings__:: 5718 5719 - elas_evar_upsymb [default = 1] attach individual elasticity symbols to model instance 5720 - elas_zero_conc_fix [default=False] if zero concentrations are detected in a steady-state solution make it a very small number 5721 - elas_zero_flux_fix [default=False] if zero fluxes are detected in a steady-state solution make it a very small number 5722 - elas_scaling_div0_fix [default=False] if INf's are detected after scaling set to zero 5723 5724 """ 5725 if input == None or input2 == None: 5726 input = self.state_species 5727 input2 = self.state_flux 5728 # print 'INFO: Using state_species and state_flux as input' 5729 else: 5730 assert len(input) == len(self.species), ( 5731 'length error this array must have ' 5732 + str(len(self.species)) 5733 + ' elements' 5734 ) 5735 assert len(input2) == len(self.reactions), ( 5736 'length error this array must have ' 5737 + str(len(self.reactions)) 5738 + ' elements' 5739 ) 5740 # not really necessary but in here just in case - brett 20040930 5741 # map input back to self.Sx 5742 exec(self.__CDvarUpString__) 5743 5744 if self.__settings__['elas_zero_flux_fix']: 5745 val = self.__settings__['mach_floateps'] ** 2 5746 for x in range(len(input2)): 5747 if abs(input2[x]) <= val: 5748 print( 5749 'Info: zero flux detected: J_{} set to {}'.format( 5750 self.reactions[x], val 5751 ) 5752 ) 5753 input2[x] = val 5754 if self.__settings__['elas_zero_conc_fix']: 5755 val = self.__settings__['mach_floateps'] ** 2 5756 for x in range(len(input)): 5757 if abs(input[x]) <= val: 5758 print( 5759 'Info: zero concentration detected: {} set to {}'.format( 5760 self.species[x], val 5761 ) 5762 ) 5763 input[x] = val 5764 5765 if self.__settings__['display_debug'] == 1: 5766 print('\nVarinput = ' + repr(input) + '\n') 5767 5768 self.__evmatrix__ = numpy.zeros( 5769 (len(self.__reactions__), len(self.__species__)), 'd' 5770 ) 5771 5772 # scale KL (future refactoring allow user input) 5773 self.ScaleKL(input, input2) 5774 5775 # create tuples of the rows and columns for the Ev matrix 5776 self.elas_var_row = tuple(copy.copy(self.__reactions__)) 5777 col = copy.copy(self._D_s_Order) 5778 for x in range(len(self._D_s_Order)): 5779 col[x] = col[x].replace('self.', '') 5780 self.elas_var_col = tuple(col) 5781 del col 5782 5783 # attempt to evaluate every variable against every flux: E's > mach_eps are assumed to exist 5784 for react in range(len(self.__reactions__)): 5785 if self.__settings__['display_debug'] == 1: 5786 print('\nReaction: ' + self.__reactions__[react]) 5787 for met in range(len(self.__species__)): 5788 countV = 0 5789 countMet = 0 5790 # this modification should make this independant of a steady state - finally implimented july2004 5791 # eval('self.'+ self.__species__[met] + '_ss'),order=self.__settings__['mode_elas_deriv_order'],dx=hstep,n=1,\ 5792 try: 5793 """ 5794 I borrowed the idea of scaling the stepsize to So from Herbert Sauro's Jarnac TModel.uEEOp function 5795 brett - 2004-08-18 5796 """ 5797 5798 hstep = ( 5799 input[met] * self.__settings__['mode_elas_deriv_factor'] 5800 ) # self.__settings__['mode_elas_deriv_factor'] = 0.0001 5801 if ( 5802 abs(hstep) < self.__settings__['mode_elas_deriv_min'] 5803 ): # self.__settings__['mode_elas_deriv_min'] = 1.0e-12 5804 hstep = self.__settings__['mode_elas_deriv_min'] 5805 5806 a = ScipyDerivative( 5807 self.__num_deriv_function__, 5808 input[met], 5809 order=self.__settings__['mode_elas_deriv_order'], 5810 dx=hstep, 5811 n=1, 5812 args=(self.__reactions__[react], self.__species__[met]), 5813 ) 5814 except Exception as ex: 5815 print(ex) 5816 print( 5817 '\nINFO: Elasticity evaluation failure in ', 5818 self.__reactions__[react], 5819 self.__species__[met], 5820 ) 5821 print('Elasticity has been set to zero') 5822 print( 5823 'A stepsize that is too large might cause this ... try decreasing the factor and or min stepsize' 5824 ) 5825 print( 5826 'Keep in mind machine precision, is', 5827 self.__settings__['mach_floateps'], 5828 ' and if min stepsize', 5829 ) 5830 print('becomes too small numeric error can become significant') 5831 a = 0.0 5832 if abs(a) >= self.__settings__['mach_floateps']: 5833 if self.__settings__['display_debug'] == 1: 5834 print('species: ' + self.__species__[met]) 5835 print( 5836 '--> d(' 5837 + self.__reactions__[react] 5838 + ')d(' 5839 + self.__species__[met] 5840 + ') = ' 5841 + str(a) 5842 ) 5843 self.__evmatrix__[react, met] = a 5844 5845 # restore variables to ss values only if steady state used 5846 if self.__settings__['elas_evar_remap']: 5847 eval(compile(self.__remaps, '__remaps', 'exec')) 5848 # print "\nREMAPPING\n" 5849 5850 self.elas_var_u = self.__evmatrix__ 5851 5852 # If scaled mca is requested 5853 if self.__settings__['mode_mca_scaled'] == 1: 5854 Ds = numpy.zeros((len(input), len(input)), 'd') 5855 Dj = numpy.zeros((len(input2), len(input2)), 'd') 5856 5857 for x in range(0, len(input)): 5858 Ds[x, x] = input[x] 5859 # print Ds 5860 5861 for x in range(0, len(input2)): 5862 Dj[x, x] = 1.0 / input2[x] 5863 if self.__settings__['elas_scaling_div0_fix'] and numpy.isinf(Dj[x, x]): 5864 print( 5865 'Infinite elasticity detected during scaling setting to zero ({})'.format( 5866 self.reactions[x] 5867 ) 5868 ) 5869 Dj[x, x] = 1.0e-16 5870 # print Dj 5871 5872 Dj_e = numpy.dot(Dj, self.__evmatrix__) 5873 self.__evmatrix__ = numpy.dot(Dj_e, Ds) 5874 self.elas_var = self.__evmatrix__ 5875 else: 5876 self.elas_var = None 5877 5878 if self.__settings__["elas_evar_upsymb"] == 1: 5879 # Upload elasticity symbols into namespace 5880 r, c = self.__evmatrix__.shape 5881 output2 = '' 5882 for x in range(0, r): 5883 react = self.__reactions__[x] 5884 for y in range(0, c): 5885 met = self._D_s_Order[y] 5886 if self.__settings__['mode_mca_scaled']: 5887 setattr( 5888 self, 5889 'ec' + react + '_' + met.replace('self.', ''), 5890 self.__evmatrix__[x, y], 5891 ) 5892 else: 5893 setattr( 5894 self, 5895 'uec' + react + '_' + met.replace('self.', ''), 5896 self.__evmatrix__[x, y], 5897 ) 5898 5899 # the new way of doing things (test phase) brett - 2007 5900 self.ec = BagOfStuff( 5901 self.__evmatrix__, self.elas_var_row, self.elas_var_col 5902 ) 5903 self.ec.load() 5904 if self.__settings__['mode_mca_scaled'] == 1: 5905 self.ec.scaled = True 5906 else: 5907 self.ec.scaled = False 5908 5909 if self.__settings__['display_debug'] == 1: 5910 print('\n\n********************************\n') 5911 print(output2) 5912 print('\n********************************\n') 5913 else: 5914 pass 5915 # print 'INFO: variable elasticity symbols not attached - .__settings__["elas_evar_upsymb"] = ' + `self.__settings__["elas_evar_upsymb"]` 5916 5917 if self.__settings__['display_debug'] == 1: 5918 print('\ne_vmatrix') 5919 print(repr(self._D_s_Order).replace('self.', '')) 5920 print(self.__reactions__) 5921 print(self.__evmatrix__) 5922 5923 def CleanNaNsFromArray(self, arr, replace_val=0.0): 5924 """ 5925 Scan a matrix for NaN's and replace with zeros: 5926 5927 - *arr* the array to be cleaned 5928 5929 """ 5930 nantest = numpy.isnan(arr) 5931 if nantest.any(): 5932 for r in range(nantest.shape[0]): 5933 if nantest[r].any(): 5934 for c in range(nantest.shape[1]): 5935 if numpy.isnan(arr[r, c]): 5936 arr[r, c] = replace_val 5937 5938 def EvalEpar(self, input=None, input2=None): 5939 """ 5940 EvalEpar(input=None,input2=None) 5941 5942 Calculate reaction elasticities towards the parameters. 5943 5944 Both inputs (input1=species,input2=rates) should be valid (steady state for MCA) solutions and given in the correct order for them to be used. If either or both are missing the last state values are used automatically. Elasticities are scaled using input 1 and 2. 5945 5946 Arguments: 5947 5948 - input [default=None]: species concentration vector 5949 - input2 [default=None]: reaction rate vector 5950 5951 Settings, set in mod.__settings__:: 5952 5953 - elas_epar_upsymb [default = 1] attach individual elasticity symbols to model instance 5954 - elas_scaling_div0_fix [default=False] if NaN's are detected in the variable and parameter elasticity matrix replace with zero 5955 5956 """ 5957 if input == None or input2 == None: 5958 input = self.state_species 5959 input2 = self.state_flux 5960 else: 5961 assert len(input) == len(self.species), ( 5962 'length error this array must have ' 5963 + str(len(self.species)) 5964 + ' elements' 5965 ) 5966 assert len(input2) == len(self.reactions), ( 5967 'length error this array must have ' 5968 + str(len(self.reactions)) 5969 + ' elements' 5970 ) 5971 # put in to fix the juicy bug - brett 20040930 5972 # iow automatic derivatives use the current values of self.Sx to operate 5973 exec(self.__CDvarUpString__) 5974 5975 # create parameter holding array 5976 parVal_hold = numpy.zeros((len(self.__parameters__)), 'd') 5977 if self.__settings__['display_debug'] == 1: 5978 print('\nParinput = ' + repr(input) + '\n') 5979 print('\nparVal_hold1') 5980 print(parVal_hold) 5981 5982 # Store parameter values into the storage array and copy them to the working array (parVal2) 5983 exec(self.__par_map2storeC) 5984 parVal2 = copy.copy(parVal_hold) 5985 5986 # create the matrix of parameter elasticities 5987 self.__epmatrix__ = numpy.zeros( 5988 (len(self.__reactions__), len(self.__parameters__)), 'd' 5989 ) 5990 5991 # create tuples of the rows and columns for the Ep matrix 5992 self.elas_par_row = tuple(copy.copy(self.__reactions__)) 5993 self.elas_par_col = tuple(self.__parameters__) 5994 5995 for react in range(len(self.__reactions__)): 5996 if self.__settings__['display_debug'] == 1: 5997 print('\nReaction: ' + self.__reactions__[react]) 5998 for par in range(len(self.__parameters__)): 5999 countV = 0 6000 countPar = 0 6001 try: 6002 """ I got the idea of scaling the stepsize to So from Herbert Sauro's Jarnac TModel.uEEOp function 6003 6004 brett - 20040818""" 6005 6006 hstep = ( 6007 getattr(self, self.__parameters__[par]) 6008 * self.__settings__['mode_elas_deriv_factor'] 6009 ) # self.__settings__['mode_elas_deriv_factor'] = 0.0001 6010 if ( 6011 abs(hstep) < self.__settings__['mode_elas_deriv_min'] 6012 ): # self.__settings__['mode_elas_deriv_min'] = 1.0e-12 6013 hstep = self.__settings__['mode_elas_deriv_min'] 6014 6015 a = ScipyDerivative( 6016 self.__num_deriv_function__, 6017 getattr(self, self.__parameters__[par]), 6018 order=self.__settings__['mode_elas_deriv_order'], 6019 dx=hstep, 6020 n=1, 6021 args=(self.__reactions__[react], self.__parameters__[par]), 6022 ) 6023 except Exception as ex: 6024 print( 6025 '\nNumeric derivative evaluation failure in ', 6026 self.__reactions__[react], 6027 self.__parameters__[par], 6028 ) 6029 print('Elasticity has been set to NaN') 6030 print( 6031 'A stepsize that is too large might cause this ... try decreasing the factor and or min stepsize' 6032 ) 6033 print( 6034 'Keep in mind machine precision, is', 6035 self.__settings__['mach_floateps'], 6036 ' and if min stepsize', 6037 ) 6038 print('becomes too small numeric error can become significant') 6039 print(ex) 6040 a = numpy.NaN 6041 6042 if numpy.isnan(a) or abs(a) > self.__settings__['mach_floateps']: 6043 if self.__settings__['display_debug'] == 1: 6044 print('parameter: ' + self.__parameters__[par]) 6045 print( 6046 '--> d(' 6047 + self.__reactions__[react] 6048 + ')d(' 6049 + self.__parameters__[par] 6050 + ') = ' 6051 + repr(a) 6052 ) 6053 self.__epmatrix__[react, par] = a 6054 6055 self.elas_par_u = self.__epmatrix__ 6056 6057 # Retrieve parameter values from the storage array 6058 exec(self.__par_remapC) 6059 6060 if self.__settings__['display_debug'] == 1: 6061 print('\nparVal_hold2') 6062 print(parVal_hold) 6063 6064 # Parameters are scaled by [1/J]*[Ep]*[P] 6065 # If scaled mca is requested scale self.__epmatrix__ 6066 if self.__settings__['mode_mca_scaled'] == 1: 6067 Dp = numpy.zeros((len(self.__parameters__), len(self.__parameters__)), 'd') 6068 Dj = numpy.zeros((len(input2), len(input2)), 'd') 6069 6070 for x in range(0, len(self.__parameters__)): 6071 Dp[x, x] = parVal_hold[x] 6072 # print Dp 6073 6074 for x in range(0, len(input2)): 6075 Dj[x, x] = 1.0 / input2[x] 6076 if self.__settings__['elas_scaling_div0_fix'] and numpy.isinf(Dj[x, x]): 6077 print( 6078 'Infinite elasticity detected during scaling setting to zero ({})'.format( 6079 self.reactions[x] 6080 ) 6081 ) 6082 Dj[x, x] = 1.0e-16 6083 # print Dj 6084 6085 Dj_e = numpy.dot(Dj, self.__epmatrix__) 6086 self.__epmatrix__ = numpy.dot(Dj_e, Dp) 6087 self.elas_par = self.__epmatrix__ 6088 else: 6089 self.elas_par = None 6090 6091 del parVal_hold 6092 6093 if self.__settings__["elas_epar_upsymb"] == 1: 6094 # Upload elasticity symbols into namespace 6095 r, c = self.__epmatrix__.shape 6096 output2 = '' 6097 for x in range(0, r): 6098 react = self.__reactions__[x] 6099 for y in range(0, c): 6100 met = self.__parameters__[y] 6101 if self.__settings__['mode_mca_scaled']: 6102 setattr( 6103 self, 6104 'ec' + react + '_' + met.replace('self.', ''), 6105 self.__epmatrix__[x, y], 6106 ) 6107 else: 6108 setattr( 6109 self, 6110 'uec' + react + '_' + met.replace('self.', ''), 6111 self.__epmatrix__[x, y], 6112 ) 6113 6114 # the new way of doing things (test phase) brett - 2007 6115 self.ecp = BagOfStuff( 6116 self.__epmatrix__, self.elas_par_row, self.elas_par_col 6117 ) 6118 self.ecp.load() 6119 if self.__settings__['mode_mca_scaled'] == 1: 6120 self.ecp.scaled = True 6121 else: 6122 self.ecp.scaled = False 6123 6124 if self.__settings__['display_debug'] == 1: 6125 print('\n\n********************************\n') 6126 print(output2) 6127 print('\n********************************\n') 6128 6129 if self.__settings__['display_debug'] == 1: 6130 print('\ne_pmatrix') 6131 print(self.__parameters__) 6132 print(self.__reactions__) 6133 print(self.__epmatrix__) 6134 6135 def showEvar(self, File=None): 6136 """ 6137 showEvar(File=None) 6138 6139 Write out all variable elasticities as \'LaTeX\' formatted strings, alternatively write results to a file. 6140 6141 Arguments: 6142 6143 File [default=None]: an open writable Python file object 6144 6145 """ 6146 # The Variable elasticities 6147 try: 6148 r, c = self.__evmatrix__.shape 6149 evar_output = '' 6150 for x in range(0, r): 6151 react = self.__reactions__[x] 6152 evar_output += '\n' + repr(self.__reactions__[x]) + '\n' 6153 for y in range(0, c): 6154 rtemp = ( 6155 self.__settings__['mode_number_format'] 6156 % self.__evmatrix__[x, y] 6157 ) 6158 met = self._D_s_Order[y] 6159 if self.__evmatrix__[x, y] != 0.0: 6160 if self.__settings__['mode_mca_scaled']: 6161 elas = '\\ec{' + react + '}{' + met + '} = ' + rtemp 6162 else: 6163 elas = '\\uec{' + react + '}{' + met + '} = ' + rtemp 6164 evar_output += elas + '\n' 6165 evar_output = evar_output.replace('self.', '') 6166 except: 6167 evar_output = 'No variable elasticities - run EvalEvar() to calculate' 6168 6169 if File != None: 6170 print('\nspecies elasticities') 6171 File.write('\n## species elasticities\n') 6172 File.write(evar_output) 6173 else: 6174 print('\nspecies elasticities') 6175 print(evar_output) 6176 6177 def showEpar(self, File=None): 6178 """ 6179 showEpar(File=None) 6180 6181 Write out all nonzero parameter elasticities as \'LaTeX\' formatted strings, alternatively write to file. 6182 6183 Arguments: 6184 6185 File [default=None]: an open writable Python file object 6186 6187 """ 6188 # The parameter elasticities 6189 try: 6190 r, c = self.__epmatrix__.shape 6191 epar_output = '' 6192 for x in range(0, r): 6193 react = self.__reactions__[x] 6194 epar_output += '\n' + repr(self.__reactions__[x]) + '\n' 6195 for y in range(0, c): 6196 rtemp = ( 6197 self.__settings__['mode_number_format'] 6198 % self.__epmatrix__[x, y] 6199 ) 6200 met = self.__parameters__[y] 6201 # brett (paranoia removal) October 2004 6202 # if abs(self.__epmatrix__[x,y]) >= 1.e-15: 6203 if self.__settings__['mode_mca_scaled']: 6204 elas = '\\ec{' + react + '}{' + met + '} = ' + rtemp 6205 else: 6206 elas = '\\uec{' + react + '}{' + met + '} = ' + rtemp 6207 # print elas 6208 epar_output += elas + '\n' 6209 epar_output = epar_output.replace('self.', '') 6210 except: 6211 epar_output = 'No parameter elasticities - run EvalEpar() to calculate' 6212 6213 if File != None: 6214 print('\nParameter elasticities') 6215 File.write('\n## Parameter elasticities\n') 6216 File.write(epar_output) 6217 else: 6218 print('\nParameter elasticities') 6219 print(epar_output) 6220 6221 def showElas(self, File=None): 6222 """ 6223 showElas(File=None) 6224 6225 Print all elasticities to screen or file as \'LaTeX\' compatible strings. 6226 Calls showEvar() and showEpar() 6227 6228 Arguments: 6229 6230 File [default=None]: an open writable Python file object 6231 6232 """ 6233 if File != None: 6234 self.showEvar(File) 6235 self.showEpar(File) 6236 else: 6237 self.showEvar() 6238 self.showEpar() 6239 6240 def ScaleKL(self, input, input2): 6241 """ 6242 ScaleKL(input,input2) 6243 6244 Scale the K and L matrices with current steady state (if either input1 or 2 == None) or user input. 6245 6246 Arguments: 6247 6248 input: vector of species concentrations 6249 input2: vector of reaction rates 6250 6251 """ 6252 # called by EvalEvar 6253 # Scale L matrix 6254 lmat = copy.copy(self.__lmatrix__) 6255 6256 if type(input) == type(None) or type(input2) == type(None): 6257 input = self.state_species 6258 input2 = self.state_flux 6259 # print 'INFO: Using state_species and state_flux as input' 6260 else: 6261 assert len(input) == len(self.species), ( 6262 'length error this array must have ' 6263 + str(len(self.species)) 6264 + ' elements' 6265 ) 6266 assert len(input2) == len(self.reactions), ( 6267 'length error this array must have ' 6268 + str(len(self.reactions)) 6269 + ' elements' 6270 ) 6271 6272 s_1_scale = numpy.zeros((len(input), len(input)), 'd') 6273 6274 for x in range(0, len(input)): 6275 try: 6276 s_1_scale[x, x] = ( 6277 1.0 / input[self.lmatrix_row[x]] 6278 ) # create 1/D Using the steady-state met's ordered to the rows 6279 except: 6280 print( 6281 'Zero species detected: ' 6282 + self.__species__[self.lmatrix_row[x]] 6283 + '_ss = ' 6284 + repr(input[self.lmatrix_row[x]]) 6285 ) 6286 s_1_scale[x, x] = 0.0 6287 6288 Si_order = numpy.zeros(len(self.lmatrix_col)) # check 6289 Si_scale = numpy.zeros((len(self.lmatrix_col), len(self.lmatrix_col)), 'd') 6290 6291 for x in range(0, len(self.lmatrix_col)): 6292 Si_order[x] = self.lmatrix_col[x] # check 6293 Si_scale[x, x] = input[self.lmatrix_col[x]] 6294 6295 L_Si = numpy.dot(lmat, Si_scale) 6296 L_scaled = numpy.dot(s_1_scale, L_Si) 6297 6298 self.lmatrix_scaled = L_scaled 6299 6300 # Scale K matrix 6301 kmat = copy.copy(self.__kmatrix__) 6302 6303 j_1_scale = numpy.zeros((len(input2), len(input2)), 'd') 6304 6305 for x in range(0, len(input2)): 6306 try: 6307 j_1_scale[x, x] = 1.0 / input2[self.kmatrix_row[x]] 6308 except: 6309 print( 6310 '\nNull flux detected: ' 6311 + self.__reactions__[self.kmatrix_row[x]] 6312 + '_ss = ' 6313 + repr(input2[self.kmatrix_row[x]]) 6314 ) 6315 j_1_scale[x, x] = 0.0 6316 6317 Ji_order = numpy.zeros(len(self.kmatrix_col)) # check 6318 Ji_scale = numpy.zeros((len(self.kmatrix_col), len(self.kmatrix_col)), 'd') 6319 6320 for x in range(0, len(self.kmatrix_col)): 6321 Ji_order[x] = self.kmatrix_col[x] # check 6322 Ji_scale[x, x] = input2[self.kmatrix_col[x]] 6323 6324 K_Ji = numpy.dot(kmat, Ji_scale) 6325 K_scaled = numpy.dot(j_1_scale, K_Ji) 6326 6327 self.kmatrix_scaled = K_scaled 6328 6329 def __num_deriv_function__(self, x, react, met): 6330 """ 6331 __num_deriv_function__(x,react,met) 6332 6333 System function that evaluates the rate equation, used by numeric perturbation methods to derive 6334 elasticities. It uses a specific format assigning x to met and evaluating react for v and is tailored for the ScipyDerivative() function using precompiled function strings. 6335 6336 Arguments: 6337 6338 x: value to assign to met 6339 react: reaction 6340 met: species 6341 6342 """ 6343 # exec(met) 6344 # self.Forcing_Function() 6345 # exec(react) 6346 # return v 6347 bk = getattr(self, met) # backup current metabolite value 6348 setattr(self, met, x) 6349 self.Forcing_Function() 6350 v = getattr(self, react) 6351 vout = v() 6352 setattr(self, met, bk) # reset metabolite value 6353 return vout 6354 6355 # Control coefficient routines 6356 def EvalCC(self): 6357 """ 6358 EvalCC() 6359 6360 Calculate the MCA control coefficients using the current steady-state solution. 6361 6362 mod.__settings__["mca_ccj_upsymb"] = 1 attach the flux control coefficients to the model instance 6363 mod.__settings__["mca_ccs_upsymb"] = 1 attach the concentration control coefficients to the model instance 6364 6365 Arguments: 6366 None 6367 6368 """ 6369 # sort E to match K and L 6370 e2 = numpy.zeros((len(self.kmatrix_row), len(self.lmatrix_row)), 'd') 6371 # print e2 6372 6373 for x in range(0, len(self.kmatrix_row)): 6374 for y in range(0, len(self.lmatrix_row)): 6375 e2[x, y] = self.elas_var_u[self.kmatrix_row[x], self.lmatrix_row[y]] 6376 6377 if self.__settings__['display_debug'] == 1: 6378 print(self.lmatrix_row) 6379 print(self.kmatrix_row) 6380 print('---') 6381 print(self.__nmatrix__.shape) 6382 print(self.nmatrix_row) 6383 print(self.nmatrix_col) 6384 print(self.nmatrix) 6385 print('---') 6386 print(self.__lmatrix__.shape) 6387 print(self.__lmatrix__) 6388 print('---') 6389 print(self.__kmatrix__.shape) 6390 print(self.__kmatrix__) 6391 print('---') 6392 print(e2.shape) 6393 print(e2) 6394 6395 EL = -1.0 * numpy.dot(e2, self.__lmatrix__) 6396 KEL = numpy.concatenate((self.__kmatrix__, EL), 1) 6397 self.kel_unscaled = KEL 6398 6399 go = 0 6400 try: 6401 Ci_u = scipy.linalg.inv(KEL) 6402 # fortran has a very fp idea of zero I use abs(val)<1.0e-15 6403 self.__structural__.MatrixFloatFix(Ci_u, val=1.0e-15) 6404 go = 1 6405 except Exception as ex: 6406 print(ex) 6407 print( 6408 '\nINFO: K-EL matrix inversion failed this is possibly due to NaN values in the Elasticity matrix' 6409 ) 6410 print( 6411 'NaN elasticities can be caused by zero fluxes or concentrations look at the settings (in mod.__settings__)' 6412 ) 6413 print('\'elas_scaling_div0_fix\' and \'elas_zero_flux_fix\'') 6414 go = 0 6415 6416 if go == 1 and not self.__settings__['mode_mca_scaled']: 6417 self.mca_ci = Ci_u 6418 self.__mca_CCi_unscaled = Ci_u 6419 del Ci_u 6420 elif go == 1 and self.__settings__['mode_mca_scaled']: 6421 Vi_l = self.__kmatrix__.shape[1] # Ind fluxes 6422 Vd_l = abs( 6423 self.__kmatrix__.shape[0] - self.__kmatrix__.shape[1] 6424 ) # Can never be the case but b.paranoid 6425 Si_l = self.__lmatrix__.shape[1] # Ind species 6426 Sd_l = abs( 6427 self.__lmatrix__.shape[0] - self.__lmatrix__.shape[1] 6428 ) # Can never be the case but b.paranoid 6429 6430 scale1_l = Vi_l + Si_l 6431 scale1 = numpy.zeros((scale1_l, scale1_l), 'd') 6432 6433 for x in range(0, Vi_l): 6434 scale1[x, x] = 1.0 / self.state_flux[self.kmatrix_row[x]] 6435 for x in range(Vi_l, scale1_l): 6436 scale1[x, x] = 1.0 / self.state_species[self.lmatrix_row[x - Vi_l]] 6437 6438 scale2 = numpy.zeros((Vi_l + Vd_l, Vi_l + Vd_l), 'd') 6439 6440 for x in range(0, len(self.state_flux)): 6441 scale2[x, x] = self.state_flux[self.kmatrix_row[x]] 6442 6443 left = numpy.dot(scale1, Ci_u) 6444 Ci = numpy.dot(left, scale2) 6445 6446 self.mca_ci = Ci 6447 del scale1_l, scale1, scale2, left, Ci, Ci_u 6448 6449 Cirow = [] 6450 Cicol = [] 6451 6452 # the wierdness continues 6453 for x in range(0, len(self.kmatrix_row)): 6454 Cicol.append(self.__reactions__[self.kmatrix_row[x]]) 6455 6456 for x in range(0, len(self.kmatrix_col)): 6457 Cirow.append(self.__reactions__[self.kmatrix_col[x]]) 6458 6459 for x in range(0, len(self.lmatrix_col)): 6460 Cirow.append(self._D_s_Order[self.lmatrix_col[x]].replace('self.', '')) 6461 6462 self.mca_ci_row = Cirow 6463 self.mca_ci_col = Cicol 6464 6465 del e2, EL, KEL 6466 6467 if self.__settings__['display_debug'] == 1: 6468 print('print self.mca_ci_row') 6469 print(self.mca_ci_row) 6470 print('print self.mca_ci_col') 6471 print(self.mca_ci_col) 6472 print('print self.mca_ci') 6473 print(self.mca_ci) 6474 6475 CJi = numpy.zeros((len(self.kmatrix_col), self.mca_ci.shape[1]), 'd') 6476 CSi = numpy.zeros( 6477 (self.mca_ci.shape[0] - len(self.kmatrix_col), self.mca_ci.shape[1]), 'd' 6478 ) 6479 6480 for x in range(0, self.mca_ci.shape[0]): 6481 for y in range(0, self.mca_ci.shape[1]): 6482 if x < len(self.kmatrix_col): 6483 CJi[x, y] = self.mca_ci[x, y] 6484 else: 6485 CSi[x - len(self.kmatrix_col), y] = self.mca_ci[x, y] 6486 6487 Ko_row = [] 6488 Ko_col = [] 6489 sKo = numpy.zeros(self.__kzeromatrix__.shape, 'd') 6490 xFactor = self.__kmatrix__.shape[0] - self.__kzeromatrix__.shape[0] 6491 6492 # we need the scaled k/l matrices to calculate the dependent cc's 6493 # self.ScaleKL() 6494 6495 for x in range(self.__kzeromatrix__.shape[0] - 1, -1, -1): 6496 Ko_row.append(self.__reactions__[self.kzeromatrix_row[x]]) 6497 for y in range( 6498 self.__kzeromatrix__.shape[1] - 1, -1, -1 6499 ): # this can be replaced by a row slice operation 6500 sKo[x, y] = self.kmatrix_scaled[x + xFactor, y] 6501 if x == 0: 6502 Ko_col.append(self.__reactions__[self.kzeromatrix_col[y]]) 6503 6504 Ko_row.reverse() 6505 Ko_col.reverse() 6506 6507 if self.__settings__['display_debug'] == 1: 6508 print('CJi') 6509 print(CJi) 6510 print('sKo') 6511 print(sKo) 6512 print('self.kzeromatrix') 6513 print(self.__kzeromatrix__) 6514 6515 # new 6516 if self.__settings__['mode_mca_scaled']: 6517 self.mca_cjd = numpy.dot(sKo, CJi) 6518 else: 6519 self.mca_cjd = numpy.dot(self.__kzeromatrix__, CJi) 6520 6521 self.mca_cjd_row = Ko_row 6522 self.mca_cjd_col = copy.copy(self.mca_ci_col) 6523 6524 del sKo, CJi 6525 6526 if self.__settings__['display_debug'] == 1: 6527 print('self.mca_cjd_row') 6528 print(self.mca_cjd_row) 6529 print('self.mca_cjd_col') 6530 print(self.mca_cjd_col) 6531 print('self.mca_cjd') 6532 print(self.mca_cjd) 6533 6534 Lo_row = [] 6535 Lo_col = [] 6536 sLo = numpy.zeros(self.__lzeromatrix__.shape, 'd') 6537 xFactor = self.__lmatrix__.shape[0] - self.__lzeromatrix__.shape[0] 6538 for x in range(self.__lzeromatrix__.shape[0] - 1, -1, -1): 6539 Lo_row.append( 6540 self._D_s_Order[self.lzeromatrix_row[x]].replace('self.', '') 6541 ) 6542 for y in range( 6543 self.__lzeromatrix__.shape[1] - 1, -1, -1 6544 ): # this can be replaced by a row slice operation 6545 sLo[x, y] = self.lmatrix_scaled[x + xFactor, y] 6546 if x == 0: 6547 Lo_col.append(self._D_s_Order[self.lzeromatrix_col[y]]) 6548 6549 Lo_row.reverse() 6550 Lo_col.reverse() 6551 6552 if ( 6553 self.__HAS_MOIETY_CONSERVATION__ == True 6554 ): # Only do this if there is S dependency 6555 if self.__settings__['mode_mca_scaled']: 6556 self.mca_csd = numpy.dot(sLo, CSi) 6557 else: 6558 self.mca_csd = numpy.dot(self.__lzeromatrix__, CSi) 6559 self.mca_csd_row = Lo_row 6560 self.mca_csd_col = copy.copy(self.mca_ci_col) 6561 else: 6562 self.mca_csd = None 6563 self.mca_csd_row = None 6564 self.mca_csd_col = None 6565 6566 del CSi, sLo 6567 6568 if self.__settings__['display_debug'] == 1: 6569 print('self.mca_csd') 6570 print(self.mca_csd) 6571 print('self.mca_csd_row') 6572 print(self.mca_csd_row) 6573 print('self.mca_csd_col') 6574 print(self.mca_csd_col) 6575 6576 if self.__HAS_FLUX_CONSERVATION__ and self.__HAS_MOIETY_CONSERVATION__: 6577 self.cc_flux = numpy.concatenate( 6578 (self.mca_ci[: self.__kmatrix__.shape[1], :], self.mca_cjd) 6579 ) 6580 self.cc_flux_row = copy.copy( 6581 self.mca_ci_row[: self.__kmatrix__.shape[1]] + self.mca_cjd_row 6582 ) 6583 self.cc_flux_col = copy.copy(self.mca_ci_col) 6584 self.cc_conc = numpy.concatenate( 6585 (self.mca_ci[self.__kmatrix__.shape[1] :, :], self.mca_csd) 6586 ) 6587 self.cc_conc_row = copy.copy( 6588 self.mca_ci_row[self.__kmatrix__.shape[1] :] + self.mca_csd_row 6589 ) 6590 self.cc_conc_col = copy.copy(self.mca_ci_col) 6591 elif self.__HAS_FLUX_CONSERVATION__: 6592 self.cc_flux = numpy.concatenate( 6593 (self.mca_ci[: self.__kmatrix__.shape[1], :], self.mca_cjd) 6594 ) 6595 self.cc_flux_row = copy.copy( 6596 self.mca_ci_row[: self.__kmatrix__.shape[1]] + self.mca_cjd_row 6597 ) 6598 self.cc_flux_col = copy.copy(self.mca_ci_col) 6599 self.cc_conc = copy.copy(self.mca_ci[self.__kmatrix__.shape[1] :, :]) 6600 self.cc_conc_row = copy.copy(self.mca_ci_row[self.__kmatrix__.shape[1] :]) 6601 self.cc_conc_col = copy.copy(self.mca_ci_col) 6602 elif self.__HAS_MOIETY_CONSERVATION__: 6603 print( 6604 'INFO: this is interesting no dependent flux cc\'s only dependent conc cc\'s!' 6605 ) 6606 self.cc_flux = copy.copy(self.mca_ci[: self.__kmatrix__.shape[1], :]) 6607 self.cc_flux_row = copy.copy(self.mca_ci_row[: self.__kmatrix__.shape[1]]) 6608 self.cc_flux_col = copy.copy(self.mca_ci_col) 6609 self.cc_conc = numpy.concatenate( 6610 (self.mca_ci[self.__kmatrix__.shape[1] :, :], self.mca_csd) 6611 ) 6612 self.cc_conc_row = copy.copy( 6613 self.mca_ci_row[self.__kmatrix__.shape[1] :] + self.mca_csd_row 6614 ) 6615 self.cc_conc_col = copy.copy(self.mca_ci_col) 6616 else: 6617 print('INFO: this is interesting no dependent flux/conc coefficients!') 6618 self.cc_flux = copy.copy(self.mca_ci[: self.__kmatrix__.shape[1], :]) 6619 self.cc_flux_row = copy.copy(self.mca_ci_row[: self.__kmatrix__.shape[1]]) 6620 self.cc_flux_col = copy.copy(self.mca_ci_col) 6621 self.cc_conc = copy.copy(self.mca_ci[self.__kmatrix__.shape[1] :, :]) 6622 self.cc_conc_row = copy.copy(self.mca_ci_row[self.__kmatrix__.shape[1] :]) 6623 self.cc_conc_col = copy.copy(self.mca_ci_col) 6624 6625 self.cc_all = numpy.concatenate((self.cc_flux, self.cc_conc)) 6626 self.cc_all_row = self.cc_flux_row + self.cc_conc_row 6627 self.cc_all_col = copy.copy(self.mca_ci_col) 6628 6629 # the new way of doing things (test phase) brett - 2007 6630 self.cc = BagOfStuff(self.cc_all, self.cc_all_row, self.cc_all_col) 6631 self.cc.load() 6632 if self.__settings__['mode_mca_scaled'] == 1: 6633 self.cc.scaled = True 6634 else: 6635 self.cc.scaled = False 6636 6637 if self.__settings__['display_debug'] == 1: 6638 print('self.cc_flux') 6639 print(self.cc_flux_row) 6640 print(self.cc_flux_col) 6641 print(self.cc_flux.shape) 6642 6643 print('self.cc_conc') 6644 print(self.cc_conc_row) 6645 print(self.cc_conc_col) 6646 print(self.cc_conc.shape) 6647 6648 print('self.cc_all') 6649 print(self.cc_all_row) 6650 print(self.cc_all_col) 6651 print(self.cc_all.shape) 6652 6653 if self.__settings__["mca_ccj_upsymb"]: 6654 # CJ 6655 r, c = self.cc_all.shape 6656 CJoutput = '' 6657 for x in range(0, len(self.__reactions__)): 6658 for y in range(0, c): 6659 if self.__settings__['mode_mca_scaled']: 6660 setattr( 6661 self, 6662 'ccJ' + self.cc_all_row[x] + '_' + self.cc_all_col[y], 6663 self.cc_all[x, y], 6664 ) 6665 else: 6666 setattr( 6667 self, 6668 'uccJ' + self.cc_all_row[x] + '_' + self.cc_all_col[y], 6669 self.cc_all[x, y], 6670 ) 6671 6672 if self.__settings__["mca_ccs_upsymb"]: 6673 # CS 6674 CSoutput = '' 6675 for x in range(len(self.__reactions__), r): 6676 for y in range(0, c): 6677 if self.__settings__['mode_mca_scaled']: 6678 setattr( 6679 self, 6680 'cc' + self.cc_all_row[x] + '_' + self.cc_all_col[y], 6681 self.cc_all[x, y], 6682 ) 6683 else: 6684 setattr( 6685 self, 6686 'ucc' + self.cc_all_row[x] + '_' + self.cc_all_col[y], 6687 self.cc_all[x, y], 6688 ) 6689 6690 if self.__settings__['display_debug'] == 1: 6691 print('CJoutput') 6692 print(CJoutput) 6693 print('CSoutput') 6694 print(CSoutput) 6695 6696 def showCC(self, File=None): 6697 """ 6698 showCC(File=None) 6699 6700 Print all control coefficients as \'LaTex\' formatted strings to the screen or file. 6701 6702 Arguments: 6703 6704 File [default=None]: an open, writable Python file object 6705 6706 """ 6707 r, c = self.cc_all.shape 6708 if self.__settings__["mca_ccall_altout"]: 6709 CAltoutput = '' 6710 for x in range(0, c): 6711 col = self.cc_all_col[x] 6712 CAltoutput += '\n`Reaction ' + col + '\'\n' 6713 for y in range(0, r): 6714 if y == 0: 6715 CAltoutput += '\"Flux control coefficients\"\n' 6716 if y == len(self.__reactions__): 6717 CAltoutput += '\"Concentration control coefficients\"\n' 6718 row = self.cc_all_row[y] 6719 rtemp = self.__settings__['mode_number_format'] % self.cc_all[y, x] 6720 if y < len(self.__reactions__): 6721 if self.__settings__['mode_mca_scaled']: 6722 cc = '\\cc{J' + row + '}{' + col + '} = ' + rtemp 6723 else: 6724 cc = '\\ucc{J' + row + '}{' + col + '} = ' + rtemp 6725 else: 6726 if self.__settings__['mode_mca_scaled']: 6727 cc = '\\cc{' + row + '}{' + col + '} = ' + rtemp 6728 else: 6729 cc = '\\ucc{' + row + '}{' + col + '} = ' + rtemp 6730 CAltoutput += cc + '\n' 6731 else: 6732 if self.__settings__["mca_ccall_fluxout"]: 6733 # CJ 6734 CJoutput = '' 6735 for x in range(0, len(self.__reactions__)): 6736 row = self.cc_all_row[x] 6737 CJoutput += '\n`J' + row + '\'\n' 6738 for y in range(0, c): 6739 col = self.cc_all_col[y] 6740 rtemp = ( 6741 self.__settings__['mode_number_format'] % self.cc_all[x, y] 6742 ) 6743 if self.__settings__['mode_mca_scaled']: 6744 cc = '\\cc{J' + row + '}{' + col + '} = ' + rtemp 6745 else: 6746 cc = '\\ucc{J' + row + '}{' + col + '} = ' + rtemp 6747 CJoutput += cc + '\n' 6748 if self.__settings__["mca_ccall_concout"]: 6749 # CS 6750 CSoutput = '' 6751 for x in range(len(self.__reactions__), r): 6752 row = self.cc_all_row[x] 6753 CSoutput += '\n`' + row + '\'\n' 6754 for y in range(0, c): 6755 col = self.cc_all_col[y] 6756 rtemp = ( 6757 self.__settings__['mode_number_format'] % self.cc_all[x, y] 6758 ) 6759 if self.__settings__['mode_mca_scaled']: 6760 cc = '\\cc{' + row + '}{' + col + '} = ' + rtemp 6761 else: 6762 cc = '\\ucc{' + row + '}{' + col + '} = ' + rtemp 6763 CSoutput += cc + '\n' 6764 if File != None: 6765 # assert type(File) == file, 'showCC() needs an open file object' 6766 if self.__settings__["mca_ccall_altout"]: 6767 print('\nControl coefficients grouped by reaction') 6768 File.write('\n## Control coefficients grouped by reaction\n') 6769 File.write(CAltoutput) 6770 else: 6771 if self.__settings__["mca_ccall_fluxout"]: 6772 print('\nFlux control coefficients') 6773 File.write('\n## Flux control coefficients\n') 6774 File.write(CJoutput) 6775 if self.__settings__["mca_ccall_concout"]: 6776 print('\nConcentration control coefficients') 6777 File.write('\n## Concentration control coefficients\n') 6778 File.write(CSoutput) 6779 else: 6780 if self.__settings__["mca_ccall_altout"]: 6781 print('\nControl coefficients grouped by reaction') 6782 print(CAltoutput) 6783 else: 6784 if self.__settings__["mca_ccall_fluxout"]: 6785 print('\nFlux control coefficients') 6786 print(CJoutput) 6787 if self.__settings__["mca_ccall_concout"]: 6788 print('\nConcentration control coefficients') 6789 print(CSoutput) 6790 6791 def EvalRC(self): 6792 """ 6793 EvalRC() 6794 6795 Calculate the MCA response coefficients using the current steady-state solution. 6796 6797 Arguments: 6798 None 6799 6800 """ 6801 6802 reordered_cc_all = numpy.zeros(self.cc_all.shape, 'd') 6803 for reac in range(len(self.elas_par_row)): 6804 reordered_cc_all[:, reac] = self.cc_all[ 6805 :, list(self.cc_all_col).index(self.elas_par_row[reac]) 6806 ] 6807 6808 self.mca_rc_par = numpy.dot(reordered_cc_all, self.elas_par) 6809 self.mca_rc_par_row = self.cc_all_row 6810 self.mca_rc_par_col = self.elas_par_col 6811 del reordered_cc_all 6812 self.__structural__.MatrixFloatFix(self.mca_rc_par) 6813 self.rc = BagOfStuff(self.mca_rc_par, self.mca_rc_par_row, self.mca_rc_par_col) 6814 self.rc.load() 6815 if self.__settings__['mode_mca_scaled'] == 1: 6816 self.rc.scaled = True 6817 else: 6818 self.rc.scaled = False 6819 6820 def EvalRCT(self): 6821 """ 6822 EvalRCT() 6823 6824 Calculate the MCA response coefficients using the current steady-state solution. 6825 6826 Responses to changes in the sums of moiety conserved cycles are also calculated. 6827 6828 Arguments: 6829 None 6830 6831 """ 6832 6833 # We arbitrarily choose the order of reactions as that of elas_var_row 6834 # and the order of species as that of elas_var_col. The order of dependent 6835 # species (there is one for each moiety conserved cycle) is from the 6836 # conservation matrix (mod.Consmatrix.row). The final output is the 6837 # (m x (m-r)) R^S_T matrix and the (n x (m-r)) R^J_T matrix. 6838 # 6839 # See "Metabolic control analysis in a nutshell" by Hofmeyr and also 6840 # Kholodenko, Sauro, Westerhoff 1994 for the matrix formulations. 6841 # 6842 # Danie Palm - 2011-10-26 6843 6844 # Reorder reactions in Cs and CJ to match elas_var_row 6845 reaction_reordered_cc_conc = numpy.zeros(self.cc_conc.shape, 'd') 6846 for reac in range(len(self.elas_var_row)): 6847 reaction_reordered_cc_conc[:, reac] = self.cc_conc[ 6848 :, list(self.cc_conc_col).index(self.elas_var_row[reac]) 6849 ] 6850 reaction_reordered_cc_flux = numpy.zeros(self.cc_flux.shape, 'd') 6851 for reac in range(len(self.elas_var_row)): 6852 reaction_reordered_cc_flux[:, reac] = self.cc_flux[ 6853 :, list(self.cc_flux_col).index(self.elas_var_row[reac]) 6854 ] 6855 6856 # Reorder metabolites in Cs to match elas_var_col 6857 reordered_cc_conc = numpy.zeros(self.cc_conc.shape, 'd') 6858 for metab in range(len(self.elas_var_col)): 6859 reordered_cc_conc[metab, :] = reaction_reordered_cc_conc[ 6860 list(self.cc_conc_row).index(self.elas_var_col[metab]), : 6861 ] 6862 6863 # Reorder fluxes in CJ to match elas_var_row 6864 reordered_cc_flux = numpy.zeros(self.cc_flux.shape, 'd') 6865 for flux in range(len(self.elas_var_row)): 6866 reordered_cc_flux[flux, :] = reaction_reordered_cc_flux[ 6867 list(self.cc_flux_row).index(self.elas_var_row[flux]), : 6868 ] 6869 6870 # Some basic dimensions and matrices 6871 m = len(self.species) 6872 r = len(self.Lmatrix.col) 6873 Im = numpy.matrix(numpy.eye(m)) 6874 6875 # Construct the (right) pseudoinverse of the the conservation matrix in the order of elas_var_col 6876 reordered_zero_I = numpy.zeros((m, m - r), 'd') 6877 for species in self.elas_var_col: 6878 if species in self.Consmatrix.row: 6879 row = self.elas_var_col.index(species) 6880 col = self.Consmatrix.row.index(species) 6881 reordered_zero_I[row, col] = 1.0 6882 6883 # Unlike normal RCs, separate calculations are required for scaled 6884 # and unscaled RCT's. We also need to pick the right elasticity 6885 # matrix: elas_var for scaled and elas_var_u for unscaled. 6886 if self.__settings__['mode_mca_scaled'] == 1: 6887 # Some scaling matrices 6888 diag_T = None 6889 if self.__KeyWords__['Species_In_Conc']: 6890 diag_T = numpy.matrix(numpy.diag(self.__tvec_c__)) 6891 else: 6892 diag_T = numpy.matrix(numpy.diag(self.__tvec_a__)) 6893 diag_S = numpy.matrix( 6894 numpy.diag(self.data_sstate.getStateData(*self.elas_var_col)) 6895 ) 6896 6897 # Calculate concentration responses to changes in T 6898 # (Cs*es + Im) * diag_S.I * reordered_zero_I * diag_T 6899 self.mca_rct_conc = numpy.dot( 6900 numpy.dot( 6901 numpy.dot( 6902 numpy.dot(reordered_cc_conc, self.elas_var) + Im, 6903 numpy.linalg.inv(diag_S), 6904 ), 6905 reordered_zero_I, 6906 ), 6907 diag_T, 6908 ) 6909 6910 # Calculate flux responses to changes in T 6911 # CJ*es * diag_S.I * reordered_zero_I * diag_T 6912 self.mca_rct_flux = numpy.dot( 6913 numpy.dot( 6914 numpy.dot( 6915 numpy.dot(reordered_cc_flux, self.elas_var), 6916 numpy.linalg.inv(diag_S), 6917 ), 6918 reordered_zero_I, 6919 ), 6920 diag_T, 6921 ) 6922 else: 6923 # Calculate concentration responses to changes in T 6924 # (Cs*es + Im) * reordered_zero_I 6925 self.mca_rct_conc = numpy.dot( 6926 numpy.dot(reordered_cc_conc, self.elas_var_u) + Im, reordered_zero_I 6927 ) 6928 6929 # Calculate flux responses to changes in T 6930 # CJ*es * reordered_zero_I 6931 self.mca_rct_flux = numpy.dot( 6932 numpy.dot(reordered_cc_flux, self.elas_var_u), reordered_zero_I 6933 ) 6934 6935 # Cleanup 6936 del reordered_cc_conc 6937 del reordered_cc_flux 6938 6939 # We simply prepend 'T_' to the name of the dependent species to prevent 6940 # confusion with the real species. This is a parameter, not variable. 6941 moiety_sum_names = [ 6942 'T_{0}'.format(dependent_species) 6943 for dependent_species in self.Consmatrix.row 6944 ] 6945 6946 self.__structural__.MatrixFloatFix(self.mca_rct_conc) 6947 self.mca_rct_conc_row = self.elas_var_col 6948 self.mca_rct_conc_col = moiety_sum_names 6949 self.__structural__.MatrixFloatFix(self.mca_rct_flux) 6950 self.mca_rct_flux_row = self.elas_var_row 6951 self.mca_rct_flux_col = moiety_sum_names 6952 6953 # Augment the existing m.rc object (this could be more efficient) 6954 6955 # Reordering as a matter of principle: too many burnt fingers 6956 vstacked_col = list(self.elas_var_row) + list(self.elas_var_col) 6957 vstacked = numpy.vstack((self.mca_rct_flux, self.mca_rct_conc)) 6958 reordered_vstack = numpy.zeros(vstacked.shape, 'd') 6959 for row_index in range(len(self.rc.row)): 6960 reordered_vstack[row_index, :] = vstacked[ 6961 list(vstacked_col).index(self.rc.row[row_index]), : 6962 ] 6963 hstacked = numpy.hstack((self.rc.matrix, vstacked)) 6964 6965 self.rc = BagOfStuff(hstacked, self.rc.row, self.rc.col + moiety_sum_names) 6966 self.rc.load() 6967 if self.__settings__['mode_mca_scaled'] == 1: 6968 self.rc.scaled = True 6969 else: 6970 self.rc.scaled = False 6971 6972 def EvalEigen(self): 6973 """ 6974 EvalEigen() 6975 6976 Calculate the eigenvalues or vectors of the unscaled Jacobian matrix and thereby 6977 analyse the stability of a system 6978 6979 Arguments: 6980 None 6981 6982 """ 6983 Nr = copy.copy(self.__nrmatrix__) 6984 6985 # sort E to match K and L 6986 Es = numpy.zeros((self.elas_var_u.shape), 'd') 6987 # print e2 6988 6989 for x in range(0, len(self.nmatrix_col)): 6990 for y in range(0, len(self.lmatrix_row)): 6991 Es[x, y] = self.elas_var_u[self.nmatrix_col[x], self.lmatrix_row[y]] 6992 if self.__HAS_MOIETY_CONSERVATION__ == True: 6993 jac1 = numpy.dot(Nr, Es) 6994 jacobian = numpy.dot(jac1, copy.copy(self.__lmatrix__)) 6995 else: 6996 jacobian = numpy.dot(Nr, Es) 6997 Si = [] 6998 for x in range(0, len(self.lmatrix_col)): 6999 Si.append(self.__species__[self.lmatrix_col[x]]) 7000 7001 self.jacobian = tuple(jacobian) 7002 self.jacobian_row = tuple(Si) 7003 self.jacobian_col = tuple(Si) 7004 if self.__settings__['mode_eigen_output']: 7005 # returns eigenvalues as well as left and right eigenvectors 7006 eigenval, self.eigen_vecleft, self.eigen_vecright = scipy.linalg.eig( 7007 jacobian, numpy.identity(jacobian.shape[0], 'd'), left=1, right=1 7008 ) 7009 if self.__settings__['display_debug'] == 1: 7010 print('\nEigenvalues') 7011 print(eigenval) 7012 print('\nLeft Eigenvector') 7013 print(self.eigen_vecleft) 7014 print('\nRight Eigenvector') 7015 print(self.eigen_vecright) 7016 else: 7017 # returns eigenvalues 7018 eigenval = scipy.linalg.eigvals(jacobian) 7019 if self.__settings__['display_debug'] == 1: 7020 print('\nEigenvalues') 7021 print(eigenval) 7022 7023 self.eigen_values = eigenval 7024 # self.eigen_order = tuple(eigenorder) 7025 7026 for x in range(len(self.eigen_values)): 7027 setattr(self, 'lambda' + str(x + 1), self.eigen_values[x]) 7028 7029 if self.__settings__['display_debug'] == 1: 7030 print( 7031 '\nEigenvalues attached as lambda1 ... lambda' 7032 + repr(len(eigenorder)) 7033 + '\n' 7034 ) 7035 7036 def showEigen(self, File=None): 7037 """ 7038 showEigen(File=None) 7039 7040 Print the eigenvalues and stability analysis of a system generated with EvalEigen() 7041 to the screen or file. 7042 7043 Arguments: 7044 7045 File [default=None]: an open, writable Python file object 7046 7047 """ 7048 eigenstats = '' 7049 eigenstats += ( 7050 'Max real part: ' 7051 + self.__settings__['mode_number_format'] % max(self.eigen_values.real) 7052 + '\n' 7053 ) 7054 eigenstats += ( 7055 'Min real part: ' 7056 + self.__settings__['mode_number_format'] % min(self.eigen_values.real) 7057 + '\n' 7058 ) 7059 eigenstats += ( 7060 'Max absolute imaginary part: ' 7061 + self.__settings__['mode_number_format'] % max(abs(self.eigen_values.imag)) 7062 + '\n' 7063 ) 7064 eigenstats += ( 7065 'Min absolute imaginary part: ' 7066 + self.__settings__['mode_number_format'] % min(abs(self.eigen_values.imag)) 7067 + '\n' 7068 ) 7069 eigenstats += ( 7070 'Stiffness: ' 7071 + self.__settings__['mode_number_format'] 7072 % (max(abs(self.eigen_values.real)) / min(abs(self.eigen_values.real))) 7073 + '\n' 7074 ) 7075 7076 pure_real = 0 7077 pure_imag = 0 7078 negcplx = 0 7079 pure_cplx = 0 7080 pure_zero = 0 7081 pos_real = 0 7082 neg_real = 0 7083 for x in self.eigen_values: 7084 if x.real != 0.0 and x.imag == 0.0: 7085 pure_real += 1 7086 if x.real == 0.0 and x.imag != 0.0: 7087 pure_imag += 1 7088 if x.real == 0.0 and x.imag == 0.0: 7089 pure_zero += 1 7090 if x.real > 0.0: 7091 pos_real += 1 7092 if x.real < 0.0: 7093 neg_real += 1 7094 if x.real < 0.0 and x.imag != 0.0: 7095 negcplx += 1 7096 7097 eigenstats += '\n## Stability' 7098 eiglen = len(self.eigen_values) 7099 if neg_real == eiglen: 7100 eigenstats += ' --> Stable state' 7101 elif pure_zero > 0: 7102 eigenstats += ' --> Undetermined' 7103 elif pos_real == eiglen: 7104 eigenstats += ' --> Unstable state' 7105 7106 eigenstats += '\nPurely real: ' + repr(pure_real) 7107 eigenstats += '\nPurely imaginary: ' + repr(pure_imag) 7108 eigenstats += '\nZero: ' + repr(pure_zero) 7109 eigenstats += '\nPositive real part: ' + repr(pos_real) 7110 eigenstats += '\nNegative real part: ' + repr(neg_real) 7111 7112 if File != None: 7113 # assert type(File) == file, 'showEigen() needs an open file object' 7114 print('\nEigen values') 7115 File.write('\n## Eigen values\n') 7116 scipy.io.write_array(File, self.eigen_values, precision=2, keep_open=1) 7117 print('\nEigen statistics') 7118 File.write('\n## Eigen statistics\n') 7119 File.write(eigenstats) 7120 if self.__settings__['mode_eigen_output']: 7121 print('\nLeft eigen vector') 7122 File.write('\n## Left eigen vector\n') 7123 scipy.io.write_array(File, self.eigen_vecleft, precision=2, keep_open=1) 7124 print('\nRight eigen vector') 7125 File.write('\n## Right eigen vector\n') 7126 scipy.io.write_array( 7127 File, self.eigen_vecright, precision=2, keep_open=1 7128 ) 7129 else: 7130 print('\nEigen values') 7131 print(self.eigen_values) 7132 print('\nEigen statistics') 7133 print(eigenstats) 7134 if self.__settings__['mode_eigen_output']: 7135 print('\nLeft eigen vector') 7136 print(self.eigen_vecleft) 7137 print('\nRight eigen vector') 7138 print(self.eigen_vecright) 7139 7140 # Utility functions 7141 # new generation metafunctions 7142 ## def doLoad(self,stoich_load=0): 7143 ## """ 7144 ## doLoad(stoich_load=0) 7145 7146 ## Load and instantiate a PySCeS model so that it can be used for further analyses. 7147 7148 ## Calls model loading subroutines: 7149 ## Stoichiometry_Analyse() [override=0,load=stoich_load] 7150 ## InitialiseModel() 7151 7152 ## Arguments: 7153 7154 ## stoich_load [default=0]: try to load a stoichiometry saved with Stoichiometry_Save_Serial() 7155 7156 ## """ 7157 ## self.InitialiseInputFile() 7158 ## assert self.__parseOK, '\nError in input file, parsing could not complete' 7159 ## self.Stoichiometry_Analyse(override=0,load=stoich_load) 7160 ## # add new Style functions to model 7161 ## self.InitialiseFunctions() 7162 ## self.InitialiseCompartments() 7163 ## self.InitialiseRules() 7164 ## self.InitialiseEvents() 7165 ## self.InitialiseOldFunctions() # TODO replace this with initialisation functions 7166 ## self.InitialiseModel() 7167 ## self.InitialiseRuleChecks() 7168 7169 def doSim(self, end=10.0, points=21): 7170 """ 7171 doSim(end=10.0,points=20.0) 7172 7173 Run a time simulation from t=0 to t=sim_end with sim_points. 7174 7175 Calls: 7176 Simulate() 7177 7178 Arguments: 7179 7180 end [default=10.0]: simulation end time 7181 points [default=20.0]: number of points in the simulation 7182 7183 """ 7184 self.sim_end = end 7185 self.sim_points = points 7186 self.Simulate() 7187 7188 def doSimPlot( 7189 self, end=10.0, points=21, plot='species', fmt='lines', filename=None 7190 ): 7191 """ 7192 Run a time simulation from t=0 to t=sim_end with sim_points and plot the results. 7193 The required output data and format can be set: 7194 7195 - *end* the end time (default=10.0) 7196 - *points* the number of points in the simulation (default=20.0) 7197 - *plot* (default='species') select output data 7198 7199 - 'species' 7200 - 'rates' 7201 - 'all' both species and rates 7202 7203 - *fmt* plot format, UPI backend dependent (default='') or the *CommonStyle* 'lines' or 'points'. 7204 - *filename* if not None (default) then the plot is exported as *filename*.png 7205 7206 Calls: 7207 - **Simulate()** 7208 - **SimPlot()** 7209 """ 7210 self.sim_end = end 7211 self.sim_points = points 7212 self.Simulate() 7213 self.SimPlot(plot=plot, format=fmt, filename=filename) 7214 7215 def exportSimAsSedML( 7216 self, 7217 output='files', 7218 return_sed=False, 7219 vc_given='PySCeS', 7220 vc_family='Software', 7221 vc_email='', 7222 vc_org='pysces.sourceforge.net', 7223 ): 7224 """ 7225 Exports the current simulation as SED-ML in various ways it creates and stores the SED-ML files in a folder 7226 generated from the model name. 7227 7228 - *output* [default='files'] the SED-ML export type can be one or more comma separated e.g. 'files,combine' 7229 - *files* export the plain SBML and SEDML XML files 7230 - *archive* export as a SED-ML archive *<file>.sedx* containing the SBML and SEDML xml files 7231 - *combine* export as a COMBINE archive *<file>.omex* containing the SBML, SEDML, manifest (XML) and metadata (RDF) 7232 - *vc_given* [default='PySCeS'] 7233 - *vc_family* [default='Software'] 7234 - *vc_email* [default='bgoli@users.sourceforge.net'] 7235 - *vc_org* [default='<pysces.sourceforge.net>'] 7236 7237 """ 7238 sedname = self.ModelFile.replace('.psc', '') 7239 7240 sedout = os.path.join(OUTPUT_DIR, 'sedout') 7241 if not os.path.exists(sedout): 7242 os.makedirs(sedout) 7243 S = SED.SED(sedname + '_sed', sedout) 7244 S.addModel(sedname, self) 7245 S.addSimulation( 7246 'sim0', 7247 self.sim_start, 7248 self.sim_end, 7249 self.sim_points, 7250 self.__SIMPLOT_OUT__, 7251 initial=None, 7252 algorithm='KISAO:0000019', 7253 ) 7254 S.addTask('task0', 'sim0', sedname) 7255 S.addTaskDataGenerators('task0') 7256 S.addTaskPlot('task0') 7257 7258 output = [s_.strip() for s_ in output.split(',')] 7259 for s_ in output: 7260 if s_ == 'files': 7261 S.writeSedScript() 7262 S.writeSedXML() 7263 elif s_ == 'archive': 7264 S.writeSedXArchive() 7265 elif s_ == 'combine': 7266 S.writeCOMBINEArchive( 7267 vc_given=vc_given, 7268 vc_family=vc_family, 7269 vc_email=vc_email, 7270 vc_org=vc_org, 7271 ) 7272 S._SED_CURRENT_ = False 7273 if return_sed: 7274 return S 7275 else: 7276 del S 7277 7278 def doSimPerturb(self, pl, end): 7279 """ 7280 **Deprecated**: use events instead 7281 """ 7282 pass 7283 7284 def doState(self): 7285 """ 7286 doState() 7287 7288 Calculate the steady-state solution of the system. 7289 7290 Calls: 7291 State() 7292 7293 Arguments: 7294 None 7295 7296 """ 7297 self.State() 7298 7299 def doStateShow(self): 7300 """ 7301 doStateShow() 7302 7303 Calculate the steady-state solution of a system and show the results. 7304 7305 Calls: 7306 State() 7307 showState() 7308 7309 Arguments: 7310 None 7311 7312 """ 7313 self.State() 7314 assert ( 7315 self.__StateOK__ == True 7316 ), '\n\nINFO: Invalid steady state: run mod.doState() and check for errors' 7317 self.showState() 7318 7319 def doElas(self): 7320 """ 7321 doElas() 7322 7323 Calculate the model elasticities, this method automatically calculates a steady state. 7324 7325 Calls: 7326 State() 7327 EvalEvar() 7328 EvalEpar() 7329 7330 Arguments: 7331 None 7332 7333 """ 7334 self.State() 7335 assert ( 7336 self.__StateOK__ == True 7337 ), '\n\nINFO: Invalid steady state: run mod.doState() and check for errors' 7338 self.EvalEvar() 7339 self.EvalEpar() 7340 7341 def doEigen(self): 7342 """ 7343 doEigen() 7344 7345 Calculate the eigenvalues, automatically performs a steady state and elasticity analysis. 7346 7347 Calls: 7348 State() 7349 EvalEvar() 7350 Evaleigen() 7351 7352 Arguments: 7353 None 7354 7355 """ 7356 self.State() 7357 assert ( 7358 self.__StateOK__ == True 7359 ), '\n\nINFO: Invalid steady state: run mod.doState() and check for errors' 7360 self.__settings__["elas_evar_upsymb"] = 1 7361 self.EvalEvar() 7362 self.__settings__["elas_evar_upsymb"] = 1 7363 self.EvalEigen() 7364 7365 def doEigenShow(self): 7366 """ 7367 doEigenShow() 7368 7369 Calculate the eigenvalues, automatically performs a steady state and elasticity analysis 7370 and displays the results. 7371 7372 Calls: 7373 doEigen() 7374 showEigen() 7375 7376 Arguments: 7377 None 7378 7379 """ 7380 self.doEigen() 7381 self.showEigen() 7382 7383 def doEigenMca(self): 7384 """ 7385 doEigenMca() 7386 7387 Calculate a full Control Analysis and eigenvalues, automatically performs a steady state, elasticity, control analysis. 7388 7389 Calls: 7390 State() 7391 EvalEvar() 7392 EvalCC() 7393 Evaleigen() 7394 7395 Arguments: 7396 None 7397 7398 """ 7399 self.State() 7400 assert ( 7401 self.__StateOK__ == True 7402 ), '\n\nINFO: Invalid steady state: run mod.doState() and check for errors' 7403 self.EvalEvar() 7404 self.EvalCC() 7405 self.EvalEigen() 7406 7407 def doMca(self): 7408 """ 7409 doMca() 7410 7411 Perform a complete Metabolic Control Analysis on the model, automatically calculates a steady state. 7412 7413 Calls: 7414 State() 7415 EvalEvar() 7416 EvalEpar() 7417 EvalCC() 7418 7419 Arguments: 7420 None 7421 7422 """ 7423 self.State() 7424 assert ( 7425 self.__StateOK__ == True 7426 ), '\n\nINFO: Invalid steady state run: mod.doState() and check for errors' 7427 self.EvalEvar() 7428 self.EvalEpar() 7429 self.EvalCC() 7430 7431 def doMcaRC(self): 7432 """ 7433 doMca() 7434 7435 Perform a complete Metabolic Control Analysis on the model, automatically calculates a steady state. 7436 7437 Calls: 7438 State() 7439 EvalEvar() 7440 EvalEpar() 7441 EvalCC() 7442 EvalRC() 7443 7444 Arguments: 7445 None 7446 7447 """ 7448 self.State() 7449 assert ( 7450 self.__StateOK__ == True 7451 ), '\n\nINFO: Invalid steady state run: mod.doState() and check for errors' 7452 self.EvalEvar() 7453 self.EvalEpar() 7454 self.EvalCC() 7455 self.EvalRC() 7456 7457 def doMcaRCT(self): 7458 """ 7459 doMcaRCT() 7460 7461 Perform a complete Metabolic Control Analysis on the model, automatically calculates a steady state. 7462 7463 In additional, response coefficients to the sums of moiety-conserved cycles are calculated. 7464 7465 Calls: 7466 State() 7467 EvalEvar() 7468 EvalEpar() 7469 EvalCC() 7470 EvalRC() 7471 EvalRCT() 7472 7473 Arguments: 7474 None 7475 7476 """ 7477 self.State() 7478 assert ( 7479 self.__StateOK__ == True 7480 ), '\n\nINFO: Invalid steady state run: mod.doState() and check for errors' 7481 self.EvalEvar() 7482 self.EvalEpar() 7483 self.EvalCC() 7484 self.EvalRC() 7485 if self.__HAS_MOIETY_CONSERVATION__: 7486 self.EvalRCT() 7487 else: 7488 print('No moiety conservation detected, reverting to simple doMcaRC().') 7489 7490 # show/save function prototypes 7491 def showSpecies(self, File=None): 7492 """ 7493 showSpecies(File=None) 7494 7495 Prints the current value of the model's variable species (mod.X) to screen or file. 7496 7497 Arguments: 7498 7499 File [default=None]: an open, writable Python file object 7500 7501 """ 7502 out_list = [] 7503 7504 print('\nSpecies values') 7505 out_list.append('\n## species values\n') 7506 for x in range(len(self.__species__)): 7507 if File == None: 7508 ## print self.__species__[x] + ' = ' + self.__settings__['mode_number_format'] % eval('self.' + self.__species__[x]) 7509 print( 7510 self.__species__[x] 7511 + ' = ' 7512 + self.__settings__['mode_number_format'] 7513 % getattr(self, self.__species__[x]) 7514 ) 7515 else: 7516 ## out_list.append(self.__species__[x] + ' = ' + self.__settings__['mode_number_format'] % eval('self.' + self.__species__[x]) + '\n') 7517 out_list.append( 7518 self.__species__[x] 7519 + ' = ' 7520 + self.__settings__['mode_number_format'] 7521 % getattr(self, self.__species__[x]) 7522 + '\n' 7523 ) 7524 if File != None: 7525 for x in out_list: 7526 File.write(x) 7527 7528 def showSpeciesI(self, File=None): 7529 """ 7530 showSpeciesI(File=None) 7531 7532 Prints the current value of the model's variable species initial values (mod.X_init) to screen or file. 7533 7534 Arguments: 7535 7536 File [default=None]: an open, writable Python file object 7537 7538 """ 7539 out_list = [] 7540 7541 print('\nSpecies initial values') 7542 out_list.append('\n## species initial values\n') 7543 for x in range(len(self.__species__)): 7544 if File == None: 7545 print( 7546 self.__species__[x] 7547 + '_init = ' 7548 + self.__settings__['mode_number_format'] 7549 % getattr(self, self.__species__[x] + '_init') 7550 ) 7551 else: 7552 out_list.append( 7553 self.__species__[x] 7554 + ' = ' 7555 + self.__settings__['mode_number_format'] 7556 % getattr(self, self.__species__[x] + '_init') 7557 + '\n' 7558 ) 7559 if File != None: 7560 for x in out_list: 7561 File.write(x) 7562 7563 def showSpeciesFixed(self, File=None): 7564 """ 7565 showSpeciesFixed(File=None) 7566 7567 Prints the current value of the model's fixed species values (mod.X) to screen or file. 7568 7569 Arguments: 7570 7571 File [default=None]: an open, writable Python file object 7572 7573 """ 7574 out_list = [] 7575 7576 print('\nFixed species') 7577 out_list.append('\n## fixed species\n') 7578 for x in range(len(self.__fixed_species__)): 7579 if File == None: 7580 print( 7581 self.__fixed_species__[x] 7582 + ' = ' 7583 + self.__settings__['mode_number_format'] 7584 % getattr(self, self.__fixed_species__[x]) 7585 ) 7586 else: 7587 out_list.append( 7588 self.__fixed_species__[x] 7589 + ' = ' 7590 + self.__settings__['mode_number_format'] 7591 % getattr(self, self.__fixed_species__[x]) 7592 + '\n' 7593 ) 7594 if File != None: 7595 for x in out_list: 7596 File.write(x) 7597 7598 def showPar(self, File=None): 7599 """ 7600 showPar(File=None) 7601 7602 Prints the current value of the model's parameter values (mod.P) to screen or file. 7603 7604 Arguments: 7605 7606 File [default=None]: an open, writable Python file object 7607 7608 """ 7609 out_list = [] 7610 7611 print('\nParameters') 7612 out_list.append('\n## parameters\n') 7613 for x in range(len(self.__parameters__)): 7614 if File == None: 7615 print( 7616 self.__parameters__[x] 7617 + ' = ' 7618 + self.__settings__['mode_number_format'] 7619 % getattr(self, self.__parameters__[x]) 7620 ) 7621 else: 7622 out_list.append( 7623 self.__parameters__[x] 7624 + ' = ' 7625 + self.__settings__['mode_number_format'] 7626 % getattr(self, self.__parameters__[x]) 7627 + '\n' 7628 ) 7629 if File != None: 7630 for x in out_list: 7631 File.write(x) 7632 7633 def showModifiers(self, File=None): 7634 """ 7635 showModifiers(File=None) 7636 7637 Prints the current value of the model's modifiers per reaction to screen or file. 7638 7639 Arguments: 7640 7641 File [default=None]: an open, writable Python file object 7642 7643 """ 7644 noMod = [] 7645 out_list = [] 7646 7647 print('\nModifiers per reaction:') 7648 out_list.append('\n## modifiers per reaction\n') 7649 for reac in self.__modifiers__: 7650 rstr = '' 7651 if len(reac[1]) == 1: 7652 if File == None: 7653 print(reac[0] + ' has modifier:', end=' ') 7654 rstr = rstr + reac[0] + ' has modifier: ' 7655 for x in reac[1]: 7656 if File == None: 7657 print(x, end=' ') 7658 rstr = rstr + x + ' ' 7659 if File == None: 7660 print(' ') 7661 rstr += '\n' 7662 elif len(reac[1]) > 1: 7663 if File == None: 7664 print(reac[0] + ' has modifiers: ', end=' ') 7665 rstr = rstr + reac[0] + ' has modifiers: ' 7666 for x in reac[1]: 7667 if File == None: 7668 print(x, end=' ') 7669 rstr = rstr + x + ' ' 7670 if File == None: 7671 print(' ') 7672 rstr += '\n' 7673 else: 7674 noMod.append(reac[0]) 7675 7676 out_list.append(rstr) 7677 if len(noMod) > 0: 7678 print('\nReactions with no modifiers:') 7679 out_list.append('\n## reactions with no modifiers\n') 7680 cntr = 0 7681 rstr = '' 7682 for n in range(len(noMod)): 7683 cntr += 1 7684 if cntr > 6: 7685 if File == None: 7686 print(' ') 7687 rstr += '\n' 7688 cntr = 0 7689 if File == None: 7690 print(noMod[n], end=' ') 7691 rstr = rstr + noMod[n] + ' ' 7692 if File == None: 7693 print(' ') 7694 rstr += '\n' 7695 out_list.append(rstr) 7696 7697 if File != None: 7698 for x in out_list: 7699 File.write(x) 7700 7701 def showState(self, File=None): 7702 """ 7703 showState(File=None) 7704 7705 Prints the result of the last steady-state analyses. Both steady-state flux's and species concentrations are shown. 7706 7707 Arguments: 7708 7709 File [default=None]: an open, writable Python file object 7710 7711 """ 7712 out_list = [] 7713 7714 print('\nSteady-state species concentrations') 7715 out_list.append('\n## Current steady-state species concentrations\n') 7716 if self.__StateOK__: 7717 for x in range(len(self.state_species)): 7718 if File == None: 7719 print( 7720 self.__species__[x] 7721 + '_ss = ' 7722 + self.__settings__['mode_number_format'] 7723 % self.state_species[x] 7724 ) 7725 else: 7726 out_list.append( 7727 self.__species__[x] 7728 + '_ss = ' 7729 + self.__settings__['mode_number_format'] 7730 % self.state_species[x] 7731 + '\n' 7732 ) 7733 else: 7734 print('No valid steady state found') 7735 out_list.append('No valid steady state found.\n') 7736 7737 print('\nSteady-state fluxes') 7738 out_list.append('\n## Steady-state fluxes\n') 7739 if self.__StateOK__: 7740 for x in range(len(self.state_flux)): 7741 if File == None: 7742 print( 7743 'J_' 7744 + self.__reactions__[x] 7745 + ' = ' 7746 + self.__settings__['mode_number_format'] % self.state_flux[x] 7747 ) 7748 else: 7749 out_list.append( 7750 'J_' 7751 + self.__reactions__[x] 7752 + ' = ' 7753 + self.__settings__['mode_number_format'] % self.state_flux[x] 7754 + '\n' 7755 ) 7756 else: 7757 print('No valid steady state found') 7758 out_list.append('No valid steady state found.\n') 7759 7760 if File != None: 7761 for x in out_list: 7762 File.write(x) 7763 7764 def showRate(self, File=None): 7765 """ 7766 Prints the current rates of all the reactions using the current parameter values and species concentrations 7767 7768 - *File* an open, writable Python file object (default=None) 7769 7770 """ 7771 Vtemp = [r() for r in getattr(self, self.__reactions__[r])] 7772 outrate = '' 7773 for x in range(len(self.__reactions__)): 7774 outrate += ( 7775 self.__reactions__[x] 7776 + ' = ' 7777 + self.__settings__['mode_number_format'] % Vtemp[x] 7778 + '\n' 7779 ) 7780 del s, Vtemp 7781 7782 if File != None: 7783 print('\nReaction rates') 7784 File.write('\n##Reaction rates\n') 7785 File.write(outrate) 7786 else: 7787 print('\nReaction rates') 7788 print(outrate) 7789 7790 def showRateEq(self, File=None): 7791 """ 7792 showRateEq(File=None) 7793 7794 Prints the reaction stoichiometry and rate equations to screen or File. 7795 7796 Arguments: 7797 7798 File [default=None]: an open, writable Python file object 7799 """ 7800 out_list = [] 7801 7802 tnform = self.__settings__['mode_number_format'] 7803 7804 self.__settings__['mode_number_format'] = '%2.5f' 7805 7806 print('\nReaction stoichiometry and rate equations') 7807 out_list.append('\n## Reaction stoichiometry and rate equations\n') 7808 7809 # writes these out in a better order 7810 for key in self.Kmatrix.row: 7811 if File == None: 7812 print(key + ':') 7813 else: 7814 out_list.append(key + ':\n') 7815 reagL = [] 7816 reagR = [] 7817 for reagent in self.__nDict__[key]['Reagents']: 7818 if self.__nDict__[key]['Reagents'][reagent] > 0: 7819 if self.__nDict__[key]['Reagents'][reagent] == 1.0: 7820 reagR.append(reagent.replace('self.', '')) 7821 else: 7822 reagR.append( 7823 '{' 7824 + self.__settings__['mode_number_format'] 7825 % abs(self.__nDict__[key]['Reagents'][reagent]) 7826 + '}' 7827 + reagent.replace('self.', '') 7828 ) 7829 elif self.__nDict__[key]['Reagents'][reagent] < 0: 7830 if self.__nDict__[key]['Reagents'][reagent] == -1.0: 7831 reagL.append(reagent.replace('self.', '')) 7832 else: 7833 reagL.append( 7834 '{' 7835 + self.__settings__['mode_number_format'] 7836 % abs(self.__nDict__[key]['Reagents'][reagent]) 7837 + '}' 7838 + reagent.replace('self.', '') 7839 ) 7840 7841 substring = '' 7842 count = 0 7843 for x in reagL: 7844 if count != 0: 7845 substring += ' + ' 7846 substring += x.replace(' ', '') 7847 count += 1 7848 prodstring = '' 7849 count = 0 7850 for x in reagR: 7851 if count != 0: 7852 prodstring += ' + ' 7853 prodstring += x.replace(' ', '') 7854 count += 1 7855 7856 if self.__nDict__[key]['Type'] == 'Rever': 7857 symbol = ' = ' 7858 else: 7859 symbol = ' > ' 7860 if File == None: 7861 print('\t' + substring + symbol + prodstring) 7862 print('\t' + self.__nDict__[key]['RateEq'].replace('self.', '')) 7863 else: 7864 out_list.append('\t' + substring + symbol + prodstring + '\n') 7865 out_list.append( 7866 '\t' + self.__nDict__[key]['RateEq'].replace('self.', '') + '\n\n' 7867 ) 7868 if len(list(self.__rules__.keys())) > 0: 7869 out_list.append('\n# Assignment rules\n') 7870 for ass in self.__rules__: 7871 out_list.append( 7872 '!F {} = {}\n'.format( 7873 self.__rules__[ass]['name'], self.__rules__[ass]['formula'] 7874 ) 7875 ) 7876 out_list.append('\n') 7877 if File != None: 7878 for x in out_list: 7879 File.write(x) 7880 self.__settings__['mode_number_format'] = tnform 7881 7882 def showODE(self, File=None, fmt='%2.3f'): 7883 """ 7884 showODE(File=None,fmt='%2.3f') 7885 7886 Print a representation of the full set of ODE's generated by PySCeS to screen or file. 7887 7888 Arguments: 7889 7890 File [default=None]: an open, writable Python file object 7891 fmt [default='%2.3f']: output number format 7892 7893 """ 7894 maxmetlen = 0 7895 for x in self.__species__: 7896 if len(x) > maxmetlen: 7897 maxmetlen = len(x) 7898 7899 maxreaclen = 0 7900 for x in self.__reactions__: 7901 if len(x) > maxreaclen: 7902 maxreaclen = len(x) 7903 odes = '\n## ODE\'s (unreduced)\n' 7904 for x in range(self.__nmatrix__.shape[0]): 7905 odes += ( 7906 'd' 7907 + self.__species__[x] 7908 + (maxmetlen - len(self.__species__[x])) * ' ' 7909 + '|' 7910 ) 7911 beginline = 0 7912 for y in range(self.__nmatrix__.shape[1]): 7913 if abs(self.__nmatrix__[x, y]) > 0.0: 7914 if self.__nmatrix__[x, y] > 0.0: 7915 if beginline == 0: 7916 odes += ( 7917 ' ' 7918 + fmt % abs(self.__nmatrix__[x, y]) 7919 + '*' 7920 + self.__reactions__[y] 7921 + (maxreaclen - len(self.__reactions__[y])) * ' ' 7922 ) 7923 beginline = 1 7924 else: 7925 odes += ( 7926 ' + ' 7927 + fmt % abs(self.__nmatrix__[x, y]) 7928 + '*' 7929 + self.__reactions__[y] 7930 + (maxreaclen - len(self.__reactions__[y])) * ' ' 7931 ) 7932 else: 7933 if beginline == 0: 7934 odes += ( 7935 ' -' 7936 + fmt % abs(self.__nmatrix__[x, y]) 7937 + '*' 7938 + self.__reactions__[y] 7939 + (maxreaclen - len(self.__reactions__[y])) * ' ' 7940 ) 7941 else: 7942 odes += ( 7943 ' - ' 7944 + fmt % abs(self.__nmatrix__[x, y]) 7945 + '*' 7946 + self.__reactions__[y] 7947 + (maxreaclen - len(self.__reactions__[y])) * ' ' 7948 ) 7949 beginline = 1 7950 odes += '\n' 7951 7952 if self.__HAS_RATE_RULES__: 7953 odes += "\n## Rate Rules:\n" 7954 for rule in self.__rate_rules__: 7955 odes += "d{} | {}\n".format( 7956 rule, self.__rules__[rule]['formula'].replace('()', ''), 7957 ) 7958 odes += '\n' 7959 7960 if File != None: 7961 print('\nODE\'s (unreduced)\n') 7962 File.write(odes) 7963 else: 7964 print(odes) 7965 7966 def showODEr(self, File=None, fmt='%2.3f'): 7967 """ 7968 showODEr(File=None,fmt='%2.3f') 7969 7970 Print a representation of the reduced set of ODE's generated by PySCeS to screen or file. 7971 7972 Arguments: 7973 7974 File [default=None]: an open, writable Python file object 7975 fmt [default='%2.3f']: output number format 7976 7977 """ 7978 maxmetlen = 0 7979 for x in self.__species__: 7980 if len(x) > maxmetlen: 7981 maxmetlen = len(x) 7982 maxreaclen = 0 7983 for x in self.__reactions__: 7984 if len(x) > maxreaclen: 7985 maxreaclen = len(x) 7986 7987 odes = '\n## ODE\'s (reduced)\n' 7988 for x in range(self.nrmatrix.shape[0]): 7989 odes += ( 7990 'd' 7991 + self.__species__[self.nrmatrix_row[x]] 7992 + (maxmetlen - len(self.__species__[self.nrmatrix_row[x]])) * ' ' 7993 + '|' 7994 ) 7995 beginline = 0 7996 for y in range(self.__nrmatrix__.shape[1]): 7997 if abs(self.__nrmatrix__[x, y]) > 0.0: 7998 if self.__nrmatrix__[x, y] > 0.0: 7999 if beginline == 0: 8000 odes += ( 8001 ' ' 8002 + fmt % abs(self.__nrmatrix__[x, y]) 8003 + '*' 8004 + self.__reactions__[self.nrmatrix_col[y]] 8005 + ( 8006 maxreaclen 8007 - len(self.__reactions__[self.nrmatrix_col[y]]) 8008 ) 8009 * ' ' 8010 ) 8011 beginline = 1 8012 else: 8013 odes += ( 8014 ' + ' 8015 + fmt % abs(self.__nrmatrix__[x, y]) 8016 + '*' 8017 + self.__reactions__[self.nrmatrix_col[y]] 8018 + ( 8019 maxreaclen 8020 - len(self.__reactions__[self.nrmatrix_col[y]]) 8021 ) 8022 * ' ' 8023 ) 8024 else: 8025 if beginline == 0: 8026 odes += ( 8027 ' -' 8028 + fmt % abs(self.__nrmatrix__[x, y]) 8029 + '*' 8030 + self.__reactions__[self.nrmatrix_col[y]] 8031 + ( 8032 maxreaclen 8033 - len(self.__reactions__[self.nrmatrix_col[y]]) 8034 ) 8035 * ' ' 8036 ) 8037 else: 8038 odes += ( 8039 ' - ' 8040 + fmt % abs(self.__nrmatrix__[x, y]) 8041 + '*' 8042 + self.__reactions__[self.nrmatrix_col[y]] 8043 + ( 8044 maxreaclen 8045 - len(self.__reactions__[self.nrmatrix_col[y]]) 8046 ) 8047 * ' ' 8048 ) 8049 beginline = 1 8050 odes += '\n' 8051 8052 if self.__HAS_RATE_RULES__: 8053 odes += "\n## Rate Rules:\n" 8054 for rule in self.__rate_rules__: 8055 odes += "d{} | {}\n".format( 8056 rule, self.__rules__[rule]['formula'].replace('()', ''), 8057 ) 8058 odes += '\n' 8059 8060 if File != None: 8061 print('\nODE\'s (reduced)\n') 8062 File.write(odes) 8063 self.showConserved(File) 8064 else: 8065 print(odes) 8066 self.showConserved() 8067 8068 def showModel(self, filename=None, filepath=None, skipcheck=0): 8069 """ 8070 showModel(filename=None,filepath=None,skipcheck=0) 8071 8072 The PySCeS 'save' command, prints the entire model to screen or File in a PSC format. 8073 (Currently this only applies to basic model attributes, ! functions are not saved). 8074 8075 Arguments: 8076 8077 filename [default=None]: the output PSC file 8078 filepath [default=None]: the output directory 8079 skipcheck [default=0]: skip check to see if the file exists (1) auto-averwrite 8080 8081 """ 8082 if filepath == None: 8083 filepath = self.ModelDir 8084 8085 if filename == None: 8086 print('\nFixed species') 8087 if len(self.__fixed_species__) == 0: 8088 print('<none>') 8089 else: 8090 for x in self.__fixed_species__: 8091 print(x, end=' ') 8092 print(' ') 8093 8094 self.showRateEq() 8095 self.showSpeciesI() 8096 self.showPar() 8097 else: 8098 # assert type(filename) == str, 'showModel() takes a string filename argument' 8099 FBuf = 0 8100 if type(filename) == str: 8101 # filename = str(filename) 8102 filename = chkpsc(filename) 8103 go = 0 8104 loop = 0 8105 if skipcheck != 0: 8106 loop = 1 8107 go = 1 8108 while loop == 0: 8109 try: 8110 filex = os.path.join(filepath, filename) 8111 if os.path.isfile(filex): 8112 inp = input( 8113 '\nfile ' + filex + ' exists.\nOverwrite? ([y]/n) ' 8114 ) 8115 else: 8116 raise IOError 8117 if inp == 'y' or inp == '': 8118 go = 1 8119 loop = 1 8120 elif inp == 'n': 8121 filename = input( 8122 '\nfile "' 8123 + filename 8124 + '" exists. Enter a new filename: ' 8125 ) 8126 go = 1 8127 filex = os.path.join(filepath, filename) 8128 filename = chkpsc(filename) 8129 else: 8130 print('\nInvalid input') 8131 except IOError: 8132 print('\nfile "' + filex + '" does not exist, proceeding') 8133 loop = 1 8134 go = 1 8135 else: 8136 print('\nI hope we have a filebuffer') 8137 if ( 8138 type(filename).__name__ == 'file' 8139 or type(filename).__name__ == 'StringIO' 8140 or type(filename).__name__ == 'TextIOWrapper' 8141 ): 8142 go = 1 8143 FBuf = 1 8144 print('Seems like it') 8145 else: 8146 go = 0 8147 print('Are you sure you know what ur doing') 8148 8149 if go == 1: 8150 if not FBuf: 8151 if filex[-4:] == '.psc': 8152 pass 8153 else: 8154 print('Assuming extension is .psc') 8155 filex += '.psc' 8156 outFile = open(filex, 'w') 8157 else: 8158 outFile = filename 8159 8160 # header = '# \n' 8161 header = '# PySCeS (' + __version__ + ') model input file \n' 8162 # header += '# Copyright Brett G. Olivier, 2004 (bgoli at sun dot ac dot za) \n' 8163 header += '# http://pysces.sourceforge.net/ \n' 8164 header += '# \n' 8165 header += '# Original input file: ' + self.ModelFile + '\n' 8166 header += ( 8167 '# This file generated: ' 8168 + time.strftime("%a, %d %b %Y %H:%M:%S") 8169 + '\n\n' 8170 ) 8171 outFile.write(header) 8172 8173 outFile.write('## Fixed species\n') 8174 if len(self.__fixed_species__) == 0: 8175 outFile.write('# <none>') 8176 else: 8177 outFile.write('FIX: ') 8178 for x in self.__fixed_species__: 8179 outFile.write(x + ' ') 8180 outFile.write('\n') 8181 self.showRateEq(File=outFile) 8182 self.showSpeciesI(File=outFile) 8183 self.showPar(File=outFile) 8184 if ( 8185 type(filename) == str 8186 or type(filename).__name__ == 'file' 8187 or type(filename).__name__ == 'TextIOWrapper' 8188 ): 8189 # don't close StringIO objects 8190 outFile.close() 8191 8192 def showN(self, File=None, fmt='%2.3f'): 8193 """ 8194 showN(File=None,fmt='%2.3f') 8195 8196 Print the stoichiometric matrix (N), including row and column labels to screen or File. 8197 8198 Arguments: 8199 8200 File [default=None]: an open, writable Python file object 8201 fmt [default='%2.3f']: output number format 8202 """ 8203 km_trunc = 0 8204 km_trunc2 = 0 8205 km = '' 8206 rtr = [] 8207 ctr = [] 8208 for a in range(len(self.nmatrix_col)): 8209 if a == 0: 8210 km += 9 * ' ' 8211 if len(self.__reactions__[self.nmatrix_col[a]]) > 7: 8212 km += self.__reactions__[self.nmatrix_col[a]][:6] + '* ' 8213 km_trunc = 1 8214 ctr.append(a) 8215 else: 8216 km += self.__reactions__[a] + (9 - len(self.__reactions__[a]) - 1) * ' ' 8217 for x in range(len(self.nmatrix_row)): 8218 if len(self.__species__[x]) > 6: 8219 km += '\n' + self.__species__[x][:5] + '*' + 1 * ' ' 8220 km_trunc2 = 1 8221 rtr.append(x) 8222 else: 8223 km += ( 8224 '\n' 8225 + self.__species__[x] 8226 + (8 - len(self.__species__[x]) - 1) * ' ' 8227 ) 8228 # km += '\n' + self.__reactions__[self.nmatrix_row[x]] + 4*' ' 8229 for y in range(len(self.nmatrix_col)): 8230 if y != 0: 8231 km += 3 * ' ' 8232 if self.__nmatrix__[x, y] >= 10.0: 8233 km += ' ' + fmt % self.__nmatrix__[x, y] 8234 elif self.__nmatrix__[x, y] >= 0.0: 8235 km += ' ' + fmt % self.__nmatrix__[x, y] 8236 else: 8237 if abs(self.__nmatrix__[x, y]) >= 10.0: 8238 km += fmt % self.__nmatrix__[x, y] 8239 else: 8240 km += ' ' + fmt % self.__nmatrix__[x, y] 8241 if km_trunc: 8242 km += '\n\n' + '* indicates truncated (col) reaction\n(' 8243 for x in range(len(self.nmatrix_col)): 8244 try: 8245 ctr.index(x) 8246 km += self.__reactions__[x] + ', ' 8247 except: 8248 pass 8249 km += ')' 8250 if km_trunc2: 8251 km += '\n\n' + '* indicates truncated (row) species:\n(' 8252 for x in range(len(self.nmatrix_row)): 8253 try: 8254 rtr.index(x) 8255 km += self.__species__[x] + ', ' 8256 except: 8257 pass 8258 km += ')' 8259 8260 if File != None: 8261 print('\nStoichiometric matrix (N)') 8262 File.write('\n\n## Stoichiometric matrix (N)\n') 8263 File.write(km) 8264 File.write('\n') 8265 else: 8266 print('\nStoichiometric matrix (N)') 8267 print(km) 8268 8269 def showNr(self, File=None, fmt='%2.3f'): 8270 """ 8271 showNr(File=None,fmt='%2.3f') 8272 8273 Print the reduced stoichiometric matrix (Nr), including row and column labels to screen or File. 8274 8275 Arguments: 8276 8277 File [default=None]: an open, writable Python file object 8278 fmt [default='%2.3f']: output number format 8279 """ 8280 km_trunc = 0 8281 km_trunc2 = 0 8282 km = '' 8283 ctr = [] 8284 rtr = [] 8285 for a in range(len(self.nrmatrix_col)): 8286 if a == 0: 8287 km += 9 * ' ' 8288 if len(self.__reactions__[self.nrmatrix_col[a]]) > 7: 8289 km += self.__reactions__[self.nrmatrix_col[a]][:6] + '* ' 8290 km_trunc = 1 8291 ctr.append(a) 8292 else: 8293 km += self.__reactions__[a] + (9 - len(self.__reactions__[a]) - 1) * ' ' 8294 for x in range(len(self.nrmatrix_row)): 8295 if len(self.__species__[self.nrmatrix_row[x]]) > 6: 8296 km += '\n' + self.__species__[self.nrmatrix_row[x]][:5] + '*' + 1 * ' ' 8297 km_trunc2 = 1 8298 rtr.append(x) 8299 else: 8300 km += ( 8301 '\n' 8302 + self.__species__[self.nrmatrix_row[x]] 8303 + (8 - len(self.__species__[self.nrmatrix_row[x]]) - 1) * ' ' 8304 ) 8305 # km += '\n' + self.__reactions__[self.nrmatrix_row[x]] + 4*' ' 8306 for y in range(len(self.nrmatrix_col)): 8307 if y != 0: 8308 km += 3 * ' ' 8309 if self.__nrmatrix__[x, y] >= 10.0: 8310 km += ' ' + fmt % self.__nrmatrix__[x, y] 8311 elif self.__nrmatrix__[x, y] >= 0.0: 8312 km += ' ' + fmt % self.__nrmatrix__[x, y] 8313 else: 8314 if abs(self.__nrmatrix__[x, y]) >= 10.0: 8315 km += fmt % self.__nrmatrix__[x, y] 8316 else: 8317 km += ' ' + fmt % self.__nrmatrix__[x, y] 8318 if km_trunc: 8319 km += '\n\n' + '* indicates truncated (col) reaction:\n(' 8320 for x in range(len(self.nrmatrix_col)): 8321 try: 8322 ctr.index(x) 8323 km += self.__reactions__[x] + ', ' 8324 except: 8325 pass 8326 km += ')' 8327 if km_trunc2: 8328 km += '\n\n' + '* indicates truncated (row) species:\n(' 8329 for x in range(len(self.nrmatrix_row)): 8330 try: 8331 rtr.index(x) 8332 km += self.__species__[self.nrmatrix_row[x]] + ', ' 8333 except: 8334 pass 8335 km += ')' 8336 if File != None: 8337 print('\nReduced stoichiometric matrix (Nr)') 8338 File.write('\n\n## Reduced stoichiometric matrix (Nr)\n') 8339 File.write(km) 8340 File.write('\n') 8341 else: 8342 print('\nReduced stoichiometric matrix (Nr)') 8343 print(km) 8344 8345 def showK(self, File=None, fmt='%2.3f'): 8346 """ 8347 showK(File=None,fmt='%2.3f') 8348 8349 Print the Kernel matrix (K), including row and column labels to screen or File. 8350 8351 Arguments: 8352 8353 File [default=None]: an open, writable Python file object 8354 fmt [default='%2.3f']: output number format 8355 """ 8356 km_trunc = 0 8357 km_trunc2 = 0 8358 km = '' 8359 ctr = [] 8360 rtr = [] 8361 for a in range(len(self.kmatrix_col)): 8362 if a == 0: 8363 km += 8 * ' ' 8364 if len(self.__reactions__[self.kmatrix_col[a]]) > 7: 8365 km += self.__reactions__[self.kmatrix_col[a]][:6] + '* ' 8366 km_trunc = 1 8367 ctr.append(a) 8368 else: 8369 km += ( 8370 self.__reactions__[self.kmatrix_col[a]] 8371 + (9 - len(self.__reactions__[self.kmatrix_col[a]]) - 1) * ' ' 8372 ) 8373 8374 for x in range(len(self.kmatrix_row)): 8375 if len(self.__reactions__[self.kmatrix_row[x]]) > 6: 8376 km += '\n' + self.__reactions__[self.kmatrix_row[x]][:5] + '*' + 1 * ' ' 8377 km_trunc2 = 1 8378 rtr.append(x) 8379 else: 8380 km += ( 8381 '\n' 8382 + self.__reactions__[self.kmatrix_row[x]] 8383 + (8 - len(self.__reactions__[self.kmatrix_row[x]]) - 1) * ' ' 8384 ) 8385 # km += '\n' + self.__reactions__[self.kmatrix_row[x]] + 4*' ' 8386 for y in range(len(self.kmatrix_col)): 8387 if y != 0: 8388 km += 4 * ' ' 8389 if abs(self.__kmatrix__[x, y]) == 0.0: 8390 km += ' ' + fmt % abs(self.__kmatrix__[x, y]) 8391 elif self.__kmatrix__[x, y] < 0.0: 8392 km += fmt % self.__kmatrix__[x, y] 8393 else: 8394 km += ' ' + fmt % self.__kmatrix__[x, y] 8395 8396 if km_trunc: 8397 km += '\n\n' + '* indicates truncated (col) reaction:\n(' 8398 for x in range(len(self.kmatrix_col)): 8399 try: 8400 ctr.index(x) 8401 km += self.__reactions__[self.kmatrix_col[x]] + ', ' 8402 except: 8403 pass 8404 km += ')' 8405 if km_trunc2: 8406 km += '\n\n' + '* indicates truncated (row) reaction:\n(' 8407 for x in range(len(self.kmatrix_row)): 8408 try: 8409 rtr.index(x) 8410 km += self.__reactions__[self.kmatrix_row[x]] + ', ' 8411 except: 8412 pass 8413 km += ')' 8414 8415 if File != None: 8416 print('\nKernel matrix (K)') 8417 File.write('\n\n## Kernel matrix (K)\n') 8418 if not self.__HAS_FLUX_CONSERVATION__: 8419 File.write('No flux conservation\n') 8420 else: 8421 File.write(km) 8422 File.write('\n') 8423 else: 8424 print('\nKernel matrix (K)') 8425 if not self.__HAS_FLUX_CONSERVATION__: 8426 print('No flux conservation') 8427 else: 8428 print(km) 8429 8430 def showL(self, File=None, fmt='%2.3f'): 8431 """ 8432 showL(File=None,fmt='%2.3f') 8433 8434 Print the Link matrix (L), including row and column labels to screen or File. 8435 8436 Arguments: 8437 8438 File [default=None]: an open, writable Python file object 8439 fmt [default='%2.3f']: output number format 8440 """ 8441 km_trunc = 0 8442 km_trunc2 = 0 8443 km = '' 8444 ctr = [] 8445 rtr = [] 8446 for a in range(len(self.lmatrix_col)): 8447 if a == 0: 8448 km += 8 * ' ' 8449 if len(self.__species__[self.lmatrix_col[a]]) > 7: 8450 km += self.__species__[self.lmatrix_col[a]][:6] + '* ' 8451 km_trunc = 1 8452 ctr.append(a) 8453 else: 8454 km += ( 8455 self.__species__[self.lmatrix_col[a]] 8456 + (9 - len(self.__species__[self.lmatrix_col[a]]) - 1) * ' ' 8457 ) 8458 8459 for x in range(len(self.lmatrix_row)): 8460 if len(self.__species__[self.lmatrix_row[x]]) > 6: 8461 km += '\n' + self.__species__[self.lmatrix_row[x]][:5] + '* ' 8462 km_trunc2 = 1 8463 rtr.append(x) 8464 else: 8465 km += ( 8466 '\n' 8467 + self.__species__[self.lmatrix_row[x]] 8468 + (8 - len(self.__species__[self.lmatrix_row[x]]) - 1) * ' ' 8469 ) 8470 for y in range(len(self.lmatrix_col)): 8471 if y != 0: 8472 km += 4 * ' ' 8473 if abs(self.__lmatrix__[x, y]) == 0.0: 8474 km += ' ' + fmt % abs(self.__lmatrix__[x, y]) 8475 elif self.__lmatrix__[x, y] < 0.0: 8476 km += fmt % self.__lmatrix__[x, y] 8477 else: 8478 km += ' ' + fmt % self.__lmatrix__[x, y] 8479 8480 if km_trunc: 8481 km += '\n\n' + '* indicates truncated (col) species:\n(' 8482 for x in range(len(self.lmatrix_col)): 8483 try: 8484 ctr.index(x) 8485 km += self.__species__[self.lmatrix_col[x]] + ', ' 8486 except: 8487 pass 8488 km += ')' 8489 if km_trunc2: 8490 km += '\n\n' + '* indicates truncated (row) species:\n(' 8491 for x in range(len(self.lmatrix_row)): 8492 try: 8493 rtr.index(x) 8494 km += self.__species__[self.lmatrix_row[x]] + ', ' 8495 except: 8496 pass 8497 km += ')' 8498 8499 if File != None: 8500 print('\nLink matrix (L)') 8501 File.write('\n\n## Link matrix (L)\n') 8502 if not self.__HAS_MOIETY_CONSERVATION__: 8503 File.write('No moiety conservation\n') 8504 File.write(km) 8505 File.write('\n') 8506 else: 8507 File.write(km) 8508 File.write('\n') 8509 else: 8510 print('\nLink matrix (L)') 8511 if not self.__HAS_MOIETY_CONSERVATION__: 8512 print('\"No moiety conservation\"\n') 8513 print(km) 8514 else: 8515 print(km) 8516 8517 # Internal test functions 8518 def TestSimState(self, endTime=10000, points=101, diff=1.0e-5): 8519 """ 8520 **Deprecated** 8521 """ 8522 pass 8523 8524 def ResetNumberFormat(self): 8525 """ 8526 ResetNumberFormat() 8527 8528 Reset PySCeS default number format stored as mod.mode_number format to %2.4e 8529 8530 Arguments: 8531 None 8532 8533 """ 8534 self.__settings__['mode_number_format'] = '%2.4e' 8535 8536 def Write_array( 8537 self, inputd, File=None, Row=None, Col=None, close_file=0, separator=' ' 8538 ): 8539 """ 8540 Write_array(input,File=None,Row=None,Col=None,close_file=0,separator=' ') 8541 8542 Write an array to File with optional row/col labels. A ',' separator can be specified to create 8543 a CSV style file. 8544 8545 mod.__settings__['write_array_header']: add <filename> as a header line (1 = yes, 0 = no) 8546 mod.__settings__['write_array_spacer']: add a space after the header line (1 = yes, 0 = no) 8547 mod.__settings__['write_arr_lflush']: set the flush rate for large file writes 8548 8549 Arguments: 8550 8551 input: the array to be written 8552 File [default=None]: an open, writable Python file object 8553 Row [default=None]: a list of row labels 8554 Col [default=None]: a list of column labels 8555 close_file [default=0]: close the file after write (1) or leave open (0) 8556 separator [default=' ']: the column separator to use 8557 8558 """ 8559 fname = ( 8560 'Write_array_' 8561 + self.ModelFile.replace('.psc', '') 8562 + '_' 8563 + time.strftime("%H:%M:%S") 8564 ) 8565 8566 # seeing as row indexes are now tuples and not arrays - brett 20050713 8567 if type(inputd) == tuple or type(inputd) == list: 8568 inputd = numpy.array(inputd) 8569 8570 if Row != None: 8571 assert ( 8572 len(Row) == inputd.shape[0] 8573 ), 'len(Row) must be equal to len(input_row)' 8574 if Col != None: 8575 assert ( 8576 len(Col) == inputd.shape[1] 8577 ), 'len(Col) must be equal to len(input_col)' 8578 8579 if File != None: 8580 # assert File != file, 'WriteArray(input,File=None,Row=None,Col=None,close_file=0)' 8581 if self.__settings__['write_array_header']: 8582 File.write('\n## ' + fname + '\n') 8583 if self.__settings__['write_array_spacer']: 8584 File.write('\n') 8585 if Col != None: 8586 print('Writing column') 8587 File.write('#') 8588 try: 8589 input_width = ( 8590 len(self.__settings__['mode_number_format'] % inputd[0, 0]) 8591 + 1 8592 + len(str(separator)) 8593 ) 8594 except: 8595 input_width = ( 8596 len(self.__settings__['mode_number_format'] % inputd[0]) 8597 + 1 8598 + len(str(separator)) 8599 ) 8600 for x in Col: 8601 if len(str(x)) <= len(str(inputd[0, 0])): 8602 spacer = (input_width - len(str(x))) * ' ' 8603 File.write(str(x) + spacer) 8604 else: 8605 spacer = (len(str(separator))) * ' ' 8606 File.write(str(x[:input_width]) + spacer) 8607 File.write('\n') 8608 try: 8609 print('\nWriting array (normal) to file') 8610 # scipy.io.write_array(File,input,separator=' ',linesep='\n',precision=3,keep_open=1) 8611 flush_count = 0 8612 if self.__settings__['write_arr_lflush'] < 2: 8613 print('INFO: LineFlush must be >= 2') 8614 self.__settings__['write_arr_lflush'] = 2 8615 for x in range(inputd.shape[0]): 8616 flush_count += 1 8617 for y in range(inputd.shape[1]): 8618 if inputd[x, y] < 0.0: 8619 File.write( 8620 self.__settings__['mode_number_format'] % inputd[x, y] 8621 ) 8622 else: 8623 File.write( 8624 ' ' 8625 + self.__settings__['mode_number_format'] % inputd[x, y] 8626 ) 8627 if y < inputd.shape[1] - 1: 8628 File.write(separator) 8629 File.write('\n') 8630 if flush_count == self.__settings__['write_arr_lflush']: 8631 File.flush() 8632 flush_count = 0 8633 # print 'flushing' 8634 # File.write('\n') 8635 except IndexError as e: 8636 print('\nWriting vector (normal) to file') 8637 for x in range(len(inputd)): 8638 File.write(self.__settings__['mode_number_format'] % inputd[x]) 8639 if x < len(inputd) - 1: 8640 File.write(separator) 8641 File.write('\n') 8642 if Row != None: 8643 print('Writing row') 8644 File.write('# Row: ') 8645 for x in Row: 8646 File.write(str(x) + separator) 8647 File.write('\n') 8648 if close_file: 8649 File.close() 8650 else: 8651 print( 8652 'INFO: You need to supply an open writable file as the 2nd argument to this function' 8653 ) 8654 8655 def Write_array_latex(self, inputa, File=None, Row=None, Col=None, close_file=0): 8656 """ 8657 Write_array_latex(input,File=None,Row=None,Col=None,close_file=0) 8658 8659 Write an array to an open file as a \'LaTeX\' {array} 8660 8661 Arguments: 8662 8663 input: the array to be written 8664 File [default=None]: an open, writable Python file object 8665 Row [default=None]: a list of row labels 8666 Col [default=None]: a list of column labels 8667 close_file [default=0]: close the file after write (1) or leave open (0) 8668 8669 """ 8670 # seeing as row indexes are now tuples and not arrays - brett 20050713 8671 if type(inputa) == tuple or type(inputa) == list: 8672 inputa = numpy.array(inputa) 8673 8674 try: 8675 a = inputa.shape[1] 8676 except: 8677 inputa.shape = (1, inputa.shape[0]) 8678 8679 if Row != None: 8680 assert ( 8681 len(Row) == inputa.shape[0] 8682 ), 'len(Row) must be equal to len(input_row)' 8683 print('Writing row') 8684 if Col != None: 8685 assert ( 8686 len(Col) == inputa.shape[1] 8687 ), 'len(Col) must be equal to len(input_col)' 8688 print('Writing column') 8689 8690 if self.__settings__['write_arr_lflush'] < 2: 8691 print('INFO: LineFlush must be >= 2') 8692 self.__settings__['write_arr_lflush'] = 2 8693 8694 fname = ( 8695 'Write_array_latex_' 8696 + self.ModelFile.replace('.psc', '') 8697 + '_' 8698 + time.strftime("%H:%M:%S") 8699 ) 8700 if File != None: 8701 # assert File != file, 'WriteArray(input,File=None,Row=None,Col=None,close_file=0)' 8702 File.write('\n%% ' + fname + '\n') 8703 try: 8704 a = inputa.shape 8705 print('\nWriting array (LaTeX) to file') 8706 File.write('\\[\n') 8707 File.write('\\begin{array}{') 8708 if Row != None: 8709 File.write('r|') 8710 for x in range(inputa.shape[1]): 8711 File.write('r') 8712 8713 File.write('}\n ') 8714 flush_count = 0 8715 for x in range(inputa.shape[0]): 8716 if Col != None and x == 0: 8717 for el in range(len(Col)): 8718 elx = str(Col[el]).replace('_', '\_') 8719 if Row != None: 8720 File.write(' & $\\small{' + elx + '}$') 8721 else: 8722 if el == 0: 8723 File.write(' $\\small{' + elx + '}$') 8724 else: 8725 File.write(' & $\\small{' + elx + '}$') 8726 8727 File.write(' \\\\ \\hline\n ') 8728 flush_count += 1 8729 if Row != None: 8730 el2 = str(Row[x]).replace('_', '\\_') 8731 File.write('$\\small{' + el2 + '}$ &') 8732 8733 for y in range(inputa.shape[1]): 8734 if inputa[x, y] < 0.0: 8735 val = '%2.4f' % inputa[x, y] 8736 else: 8737 val = ' ' + '%2.4f' % inputa[x, y] 8738 8739 File.write(val) 8740 if y < inputa.shape[1] - 1: 8741 File.write(' &') 8742 File.write(' \\\\\n ') 8743 if flush_count == self.__settings__['write_arr_lflush']: 8744 File.flush() 8745 flush_count = 0 8746 # print 'flushing' 8747 # File.write('\n') 8748 File.write('\\end{array}\n') 8749 File.write('\\]\n\n') 8750 except: 8751 print( 8752 '\nINFO: Only arrays can currently be processed with this method.\n' 8753 ) 8754 if close_file: 8755 File.close() 8756 else: 8757 print( 8758 'INFO: You need to supply an open writable file as the 2nd argument to this method' 8759 ) 8760 8761 def Write_array_html( 8762 self, inputa, File=None, Row=None, Col=None, name=None, close_file=0 8763 ): 8764 """ 8765 Write_array_html(input,File=None,Row=None,Col=None,name=None,close_file=0) 8766 8767 Write an array as an HTML table (no header/footer) or complete document. Tables 8768 are formatted with coloured columns if they exceed a specified size. 8769 8770 mod.__settings__['write_array_html_header']: write the HTML document header 8771 mod.__settings__['write_array_html_footer']: write the HTML document footer 8772 8773 Arguments: 8774 8775 input: the array to be written 8776 File [default=None]: an open, writable Python file object 8777 Row [default=None]: a list of row labels 8778 Col [default=None]: a list of column labels 8779 name [default=None]: an HTML table description line 8780 close_file [default=0]: close the file after write (1) or leave open (0) 8781 8782 """ 8783 # seeing as row indexes are now tuples and not arrays - brett 20050713 8784 if type(inputa) == tuple or type(inputa) == list: 8785 inputa = numpy.array(inputa) 8786 try: 8787 a = inputa.shape[1] 8788 except: 8789 inputa.shape = (1, inputa.shape[0]) 8790 8791 if Row != None: 8792 assert ( 8793 len(Row) == inputa.shape[0] 8794 ), 'len(Row) must be equal to len(input_row)' 8795 print('Writing row') 8796 if Col != None: 8797 assert ( 8798 len(Col) == inputa.shape[1] 8799 ), 'len(Col) must be equal to len(input_col)' 8800 print('Writing column') 8801 if self.__settings__['write_arr_lflush'] < 2: 8802 print('INFO: LineFlush must be >= 2') 8803 self.__settings__['write_arr_lflush'] = 2 8804 if name != None: 8805 fname = ( 8806 'PySCeS data "' 8807 + name 8808 + '" generated from model file: ' 8809 + self.ModelFile 8810 + ' ' 8811 + time.strftime("%H:%M:%S") 8812 ) 8813 else: 8814 fname = ( 8815 'PySCeS data generated from model file: ' 8816 + self.ModelFile 8817 + ' ' 8818 + time.strftime("%H:%M:%S") 8819 ) 8820 if File != None: 8821 # assert File != file, 'WriteArray(input,File=None,Row=None,Col=None,close_file=0)' 8822 header = '\n' 8823 header += ( 8824 '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">\n' 8825 ) 8826 header += '<html>\n' 8827 header += '<head>\n' 8828 header += ( 8829 '<title>PySCeS data generated from: ' 8830 + self.ModelFile 8831 + ' - ' 8832 + time.strftime("%H:%M:%S (%Z)") 8833 + '</title>\n' 8834 ) 8835 header += '<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">\n' 8836 header += '</head>\n' 8837 header += '<body>\n\n' 8838 header += ( 8839 '<h4><a href="http://pysces.sourceforge.net">PySCeS</a> data generated from: ' 8840 + self.ModelFile 8841 + '</h4>\n\n' 8842 ) 8843 8844 if self.__settings__['write_array_html_header']: 8845 File.write(header) 8846 File.write('<!-- ' + fname + '-->\n') 8847 if name != None: 8848 File.write( 8849 '\n<p>\n<font face="Arial, Helvetica, sans-serif"><strong>' 8850 + name 8851 + '</strong></font>' 8852 ) 8853 else: 8854 File.write('\n<p>\n') 8855 File.write( 8856 '\n<table border="1" cellpadding="2" cellspacing="2" bgcolor="#FFFFFF">' 8857 ) 8858 try: 8859 a = inputa.shape 8860 print('Writing array (HTML) to file') 8861 8862 double_index = 15 8863 8864 if Col != None: 8865 File.write('\n<tr>\n') 8866 if Row != None: 8867 File.write(' <td> </td>\n') 8868 for col in Col: 8869 File.write( 8870 ' <td bgcolor="#CCCCCC"><div align="center"><b>' 8871 + str(col) 8872 + '</b></div></td>\n' 8873 ) 8874 if Row != None and inputa.shape[1] + 1 >= double_index: 8875 File.write(' <td> </td>\n') 8876 File.write('</tr>') 8877 8878 flush_count = 0 8879 colour_count_row = 6 8880 colour_count_col = 6 8881 rowcntr = 0 8882 html_colour = '#FFFFCC' 8883 for x in range(inputa.shape[0]): 8884 rowcntr += 1 8885 if ( 8886 rowcntr == colour_count_row 8887 and inputa.shape[0] > 3 * colour_count_row 8888 ): 8889 File.write('\n<tr bgcolor="' + html_colour + '">\n') 8890 rowcntr = 0 8891 else: 8892 File.write('\n<tr>\n') 8893 if Row != None: 8894 File.write( 8895 ' <td bgcolor="#CCCCCC"><div align="center"><b>' 8896 + str(Row[x]) 8897 + '</b></div></td>\n' 8898 ) 8899 colcntr = 0 8900 for y in range(inputa.shape[1]): 8901 colcntr += 1 8902 if ( 8903 colcntr == colour_count_col 8904 and inputa.shape[1] > 3 * colour_count_row 8905 ): 8906 File.write( 8907 ' <td nowrap bgcolor="' 8908 + html_colour 8909 + '">' 8910 + self.__settings__['write_array_html_format'] 8911 % inputa[x, y] 8912 + '</td>\n' 8913 ) 8914 colcntr = 0 8915 else: 8916 File.write( 8917 ' <td nowrap>' 8918 + self.__settings__['write_array_html_format'] 8919 % inputa[x, y] 8920 + '</td>\n' 8921 ) 8922 ## File.write(' <td nowrap>' + self.__settings__['write_array_html_format'] % input[x,y] + '</td>\n') 8923 if Row != None and inputa.shape[1] + 1 >= double_index: 8924 File.write( 8925 ' <td bgcolor="#CCCCCC"><div align="center"><b>' 8926 + Row[x] 8927 + '</b></div></td>\n' 8928 ) 8929 File.write('</tr>') 8930 8931 if flush_count == self.__settings__['write_arr_lflush']: 8932 File.flush() 8933 flush_count = 0 8934 # print 'flushing' 8935 if Col != None and inputa.shape[0] + 1 >= double_index: 8936 File.write('\n<tr>\n') 8937 if Row != None: 8938 File.write(' <td> </td>\n') 8939 for col in Col: 8940 File.write( 8941 ' <td bgcolor="#CCCCCC"><div align="center"><b>' 8942 + col 8943 + '</b></div></td>\n' 8944 ) 8945 if Row != None and inputa.shape[1] + 1 >= double_index: 8946 File.write(' <td> </td>\n') 8947 File.write('</tr>\n') 8948 except: 8949 print( 8950 '\nINFO: Only arrays can currently be processed with this method.\n' 8951 ) 8952 8953 File.write('\n</table>\n') 8954 File.write('</p>\n\n') 8955 if self.__settings__['write_array_html_footer']: 8956 try: 8957 File.write( 8958 '<p><a href="http://pysces.sourceforge.net"><font size="3">PySCeS ' 8959 + __version__ 8960 + '</font></a><font size="2"> HTML output (model <i>' 8961 + self.ModelFile 8962 + '</i> analysed at ' 8963 + time.strftime("%H:%M:%S") 8964 + ' by <i>' 8965 + getuser() 8966 + '</i>)</font></p>\n' 8967 ) 8968 except: 8969 File.write( 8970 '<p><a href="http://pysces.sourceforge.net"><font size="3">PySCeS ' 8971 + __version__ 8972 + '</font></a><font size="2"> HTML output (model <i>' 8973 + self.ModelFile 8974 + '</i> analysed at ' 8975 + time.strftime("%H:%M:%S - %Z") 8976 + ')</font></p>\n' 8977 ) 8978 File.write('</body>\n') 8979 File.write('</html>\n') 8980 else: 8981 File.write('\n') 8982 8983 if close_file: 8984 File.close() 8985 else: 8986 print( 8987 'INFO: You need to supply an open writable file as the 2nd argument to this method' 8988 ) 8989 8990 def SimPlot( 8991 self, plot='species', filename=None, title=None, log=None, format='lines' 8992 ): 8993 """ 8994 Plot the simulation results, uses the new UPI pysces.plt interface: 8995 8996 - *plot*: output to plot (default='species') 8997 8998 - 'all' rates and species 8999 - 'species' species 9000 - 'rates' reaction rates 9001 - `['S1', 'R1', ]` a list of model attributes (species, rates) 9002 9003 - *filename* if not None file is exported to filename (default=None) 9004 - *title* the plot title (default=None) 9005 - *log* use log axis for 'x', 'y', 'xy' (default=None) 9006 - *format* line format, backend dependant (default='') 9007 """ 9008 data = None 9009 labels = None 9010 allowedplots = ['all', 'species', 'rates'] 9011 if type(plot) != list and plot not in allowedplots: 9012 raise RuntimeError( 9013 '\nPlot must be one of {} not \"{}\"'.format(str(allowedplots), plot) 9014 ) 9015 if plot == 'all': 9016 data, labels = self.data_sim.getAllSimData(lbls=True) 9017 elif plot == 'species': 9018 data, labels = self.data_sim.getSpecies(lbls=True) 9019 elif plot == 'rates': 9020 data, labels = self.data_sim.getRates(lbls=True) 9021 else: 9022 plot = [ 9023 at 9024 for at in plot 9025 if at 9026 in self.__species__ + self.__reactions__ + [self.data_sim.time_label] 9027 ] 9028 kwargs = {'lbls': True} 9029 if len(plot) > 0: 9030 data, labels = self.data_sim.getSimData(*plot, **kwargs) 9031 del allowedplots 9032 self.__SIMPLOT_OUT__ = labels 9033 plt.plotLines( 9034 data, 0, list(range(1, data.shape[1])), titles=labels, formats=[format] 9035 ) 9036 # set the x-axis range so that it is original range + 0.2*sim_end 9037 # this is a sceintifcally dtermned amount of space that is needed for the title at the 9038 # end of the line :-) - brett 20040209 9039 RngTime = self.data_sim.getTime() 9040 end = RngTime[-1] + 0.2 * RngTime[-1] 9041 plt.setRange('x', RngTime[0], end) 9042 del RngTime 9043 9044 if self.__KeyWords__['Output_In_Conc']: 9045 M = 'Concentration' 9046 else: 9047 M = ( 9048 'Amount (%(multiplier)s x %(kind)s x 10**%(scale)s)**%(exponent)s' 9049 % self.__uDict__['substance'] 9050 ) 9051 xu = ( 9052 'Time (%(multiplier)s x %(kind)s x 10**%(scale)s)**%(exponent)s' 9053 % self.__uDict__['time'] 9054 ) 9055 plt.setAxisLabel('x', xu) 9056 9057 if plot == 'all': 9058 yl = 'Rates, {}'.format(M) 9059 elif plot == 'rates': 9060 yl = 'Rate' 9061 elif plot == 'species': 9062 yl = '{}'.format(M) 9063 else: 9064 yl = 'Rates, {}'.format(M) 9065 plt.setAxisLabel('y', yl) 9066 if log != None: 9067 plt.setLogScale(log) 9068 if title == None: 9069 plt.setGraphTitle( 9070 'PySCeS Simulation (' 9071 + self.ModelFile 9072 + ') ' 9073 + time.strftime("%a, %d %b %Y %H:%M:%S") 9074 ) 9075 else: 9076 plt.setGraphTitle(title) 9077 plt.replot() 9078 if filename != None: 9079 plt.export(filename, directory=self.ModelOutput, type='png') 9080 9081 def Scan1(self, range1=[], runUF=0): 9082 """ 9083 Scan1(range1=[],runUF=0) 9084 9085 Perform a single dimension parameter scan using the steady-state solvers. The parameter to be scanned is defined (as a model attribute "P") in mod.scan_in while the required output is entered into the list mod.scan_out. 9086 Results of a parameter scan can be easilly viewed with Scan1Plot(). 9087 9088 mod.scan_in - a model attribute written as in the input file (eg. P, Vmax1 etc) 9089 mod.scan_out - a list of required output ['A','T2', 'ecR1_s1', 'ccJR1_R1', 'rcJR1_s1', ...] 9090 mod.scan_res - the results of a parameter scan 9091 mod.scan - numpy record array with the scan results (scan_in and scan_out), call as mod.scan.Vmax, mod.scan.A_ss, mod.scan.J_R1, etc. 9092 mod.__settings__["scan1_mca_mode"] - force the scan algorithm to evaluate the elasticities (1) and control coefficients (2) 9093 (this should also be auto-detected by the Scan1 method). 9094 9095 Arguments: 9096 9097 range1 [default=[]]: a predefined range over which to scan. 9098 runUF [default=0]: run (1) the user defined function mod.User_Function (!U) before evaluating the steady state. 9099 9100 """ 9101 if self.__settings__["scan1_dropbad"] != 0: 9102 print( 9103 '\n****\nINFO: Dropping invalid steady states can mask interesting behaviour\n****\n' 9104 ) 9105 9106 # check the legitimacy of the input and output lists 9107 # self.__settings__["scan1_mca_mode"] = scanMCA 9108 run = 1 9109 # we now accept parameters and intial species values as scanable values - brett 20060519 9110 parameters = list(self.__parameters__) + [a + '_init' for a in self.__species__] 9111 scanpar = [] 9112 9113 scanIn = [self.scan_in] 9114 9115 for x in scanIn: 9116 try: 9117 a = parameters.index(x) 9118 scanpar.append(x) 9119 except: 9120 print(x, ' is not a parameter') 9121 if len(scanpar) < 1: 9122 print('mod.scan_in should contain something') 9123 run = 0 9124 elif len(scanpar) > 1: 9125 print('Only 1D scans possible - first element will be used') 9126 scanpar = scanpar[0] 9127 print('self.scan_in = ', scanpar) 9128 else: 9129 scanpar = scanpar[0] 9130 9131 self.scan_in = scanpar 9132 9133 wrong = [] 9134 9135 for x in self.scan_out: 9136 if x[:2] == 'ec' and self.__settings__["scan1_mca_mode"] < 2: 9137 self.__settings__["scan1_mca_mode"] = 1 9138 elif x[:2] == 'cc': 9139 self.__settings__["scan1_mca_mode"] = 2 9140 elif x[:2] == 'rc': 9141 self.__settings__["scan1_mca_mode"] = 3 9142 9143 if self.__settings__["scan1_mca_mode"] == 1: 9144 self.doElas() 9145 elif self.__settings__["scan1_mca_mode"] == 2: 9146 self.doMca() 9147 elif self.__settings__["scan1_mca_mode"] == 3: 9148 self.doMcaRC() 9149 else: 9150 self.doState() # we are going to run a whole bunch anyway 9151 9152 for x in self.scan_out: 9153 try: 9154 if x.startswith('ec'): 9155 try: 9156 getattr(self.ec, x[2:]) 9157 except AttributeError: 9158 getattr(self.ecp, x[2:]) 9159 elif x.startswith('cc'): 9160 if x.startswith('ccJ'): 9161 getattr(self.cc, x[3:]) 9162 else: 9163 getattr(self.cc, x[2:]) 9164 elif x.startswith('rc'): 9165 if x.startswith('rcJ'): 9166 getattr(self.rc, x[3:]) 9167 else: 9168 getattr(self.rc, x[2:]) 9169 elif x in self.reactions: 9170 getattr(self.data_sstate, x) 9171 print( 9172 "INFO: using steady-state flux for reaction ({} --> J_%s)".format( 9173 x, x 9174 ) 9175 ) 9176 self.scan_out[self.scan_out.index(x)] = 'J_{}'.format(x) 9177 elif x.startswith('J_'): 9178 getattr(self.data_sstate, x[2:]) 9179 elif x in self.species: 9180 getattr(self.data_sstate, x) 9181 print( 9182 "INFO: using steady-state concentration for species ({} --> {}_ss)".format( 9183 x, x 9184 ) 9185 ) 9186 self.scan_out[self.scan_out.index(x)] = '{}_ss'.format(x) 9187 elif x.endswith('_ss'): 9188 getattr(self.data_sstate, x[:-3]) 9189 else: 9190 getattr(self, x) 9191 except: 9192 print(x + ' is not a valid attribute') 9193 wrong.append(x) 9194 if x == self.scan_in: 9195 wrong.append(x) 9196 print(x, ' is mod.scan_in ') 9197 9198 if len(wrong) != 0: 9199 try: 9200 for x in wrong: 9201 print(self.scan_out) 9202 self.scan_out.remove(x) 9203 print(self.scan_out) 9204 except: 9205 print('No valid output') 9206 run = 0 9207 9208 assert ( 9209 len(self.scan_out) != 0 9210 ), "Output parameter list (mod.scan_out) empty - do the model attributes exist ... see manual for details." 9211 9212 # print run, self.scan_in, self.scan_out 9213 9214 self._scan = None 9215 result = [] 9216 cntr = 0 9217 cntr2 = 1 9218 cntr3 = 0 9219 9220 # reset scan error controls 9221 self.__scan_errors_par__ = None 9222 self.__scan_errors_idx__ = None 9223 9224 if self.__settings__['scan1_mesg']: 9225 print('\nScanning ...') 9226 if len(self.scan_in) > 0 and run == 1: 9227 badList = [] 9228 badList_idx = [] 9229 if self.__settings__['scan1_mesg']: 9230 print(len(range1) - (cntr * cntr2), end=' ') 9231 for xi in range(len(range1)): 9232 x = range1[xi] 9233 setattr(self, self.scan_in, x) 9234 ## exec('self.' + self.scan_in + ' = ' + `x`) 9235 if self.__settings__["scan1_mca_mode"] == 1: 9236 self.doElas() 9237 elif self.__settings__["scan1_mca_mode"] == 2: 9238 self.doMca() 9239 elif self.__settings__["scan1_mca_mode"] == 3: 9240 self.doMcaRC() 9241 else: 9242 self.State() 9243 rawres = [x] 9244 # these two lines are going to be terminated - brett 9245 # rawres.append(x) 9246 # rawres.insert(0,x) 9247 if runUF: 9248 self.User_Function() 9249 for res in self.scan_out: 9250 if res.startswith('ec'): 9251 try: 9252 rawres.append(getattr(self.ec, res[2:])) 9253 except AttributeError: 9254 rawres.append(getattr(self.ecp, res[2:])) 9255 elif res.startswith('cc'): 9256 if res.startswith('ccJ'): 9257 rawres.append(getattr(self.cc, res[3:])) 9258 else: 9259 rawres.append(getattr(self.cc, res[2:])) 9260 elif res.startswith('rc'): 9261 if res.startswith('rcJ'): 9262 rawres.append(getattr(self.rc, res[3:])) 9263 else: 9264 rawres.append(getattr(self.rc, res[2:])) 9265 elif res in self.reactions: 9266 rawres.append(getattr(self.data_sstate, res)) 9267 elif res.startswith('J_'): 9268 rawres.append(getattr(self.data_sstate, res[2:])) 9269 elif res in self.species: 9270 rawres.append(getattr(self.data_sstate, res)) 9271 elif res.endswith('_ss'): 9272 rawres.append(getattr(self.data_sstate, res[:-3])) 9273 else: 9274 rawres.append(getattr(self, res)) 9275 9276 # The following is for user friendly reporting: 9277 # next we check if the state is ok : if bad report it and add it : if good add it 9278 if not self.__StateOK__ and self.__settings__["scan1_dropbad"] != 0: 9279 pass 9280 elif not self.__StateOK__: 9281 if self.__settings__["scan1_nan_on_bad"]: 9282 result.append([numpy.NaN] * len(rawres)) 9283 else: 9284 result.append(rawres) 9285 badList.append(x) 9286 badList_idx.append(xi) 9287 else: 9288 result.append(rawres) 9289 cntr += 1 9290 cntr3 += 1 9291 if cntr == 20: 9292 if self.__settings__['scan1_mesg']: 9293 print(len(range1) - (cntr * cntr2), end=' ') 9294 cntr = 0 9295 cntr2 += 1 9296 if cntr3 == 101: 9297 if self.__settings__['scan1_mesg']: 9298 print(' ') 9299 cntr3 = 0 9300 if self.__settings__['scan1_mesg']: 9301 print('\ndone.\n') 9302 if len(badList) != 0: 9303 self.__scan_errors_par__ = badList 9304 self.__scan_errors_idx__ = badList_idx 9305 print( 9306 '\nINFO: ' 9307 + str(len(badList)) 9308 + ' invalid steady states detected at ' 9309 + self.scan_in 9310 + ' values:' 9311 ) 9312 print(badList) 9313 if len(result) == 0: 9314 self.scan_res = numpy.zeros((len(range1), len(self.scan_out) + 1)) 9315 else: 9316 self.scan_res = numpy.array(result) 9317 9318 @property 9319 def scan(self): 9320 if self._scan is None and self.scan_res is not None: 9321 self._scan = numpy.rec.fromrecords( 9322 self.scan_res, names=[self.scan_in] + self.scan_out 9323 ) 9324 return self._scan 9325 9326 def Scan1Plot(self, plot=[], title=None, log=None, format='lines', filename=None): 9327 """ 9328 Plot the results of a parameter scan generated with **Scan1()** 9329 9330 - *plot* if empty mod.scan_out is used, otherwise any subset of mod.scan_out (default=[]) 9331 - *filename* the filename of the PNG file (default=None, no export) 9332 - *title* the plot title (default=None) 9333 - *log* if None a linear axis is assumed otherwise one of ['x','xy','xyz'] (default=None) 9334 - *format* the backend dependent line format (default='lines') or the *CommonStyle* 'lines' or 'points'. 9335 """ 9336 data = self.scan_res 9337 labels = None 9338 if type(plot) == str: 9339 plot = [plot] 9340 if len(plot) == 0: 9341 plot = self.scan_out 9342 else: 9343 plot = [at for at in plot if hasattr(self, at)] 9344 if len(plot) == 0: 9345 print('No plottable output specified using self.scan_out') 9346 plot = self.scan_out 9347 9348 plotidx = [self.scan_out.index(c) + 1 for c in plot] 9349 labels = [self.scan_in] + self.scan_out 9350 plt.plotLines(data, 0, plotidx, titles=labels, formats=[format]) 9351 9352 end = data[-1, 0] + 0.2 * data[-1, 0] 9353 plt.setRange('x', data[0, 0], end) 9354 plt.setAxisLabel('x', self.scan_in) 9355 9356 yl = 'Steady-state variable' 9357 plt.setAxisLabel('y', yl) 9358 if log != None: 9359 plt.setLogScale(log) 9360 if title == None: 9361 plt.setGraphTitle( 9362 'PySCeS Scan1 (' 9363 + self.ModelFile 9364 + ') ' 9365 + time.strftime("%a, %d %b %Y %H:%M:%S") 9366 ) 9367 else: 9368 plt.setGraphTitle(title) 9369 plt.replot() 9370 if filename != None: 9371 plt.export(filename, directory=self.ModelOutput, type='png') 9372 9373 def Scan2D(self, p1, p2, output, log=False): 9374 """ 9375 Generate a 2 dimensional parameter scan using the steady-state solvers. 9376 9377 - *p1* is a list of [parameter1, start, end, points] 9378 - *p2* is a list of [parameter2, start, end, points] 9379 - *output* steady-state variable/properties e.g. 'J_R1', 'A_ss', 'ecR1_s1' 9380 - *log* scan using log ranges for both axes 9381 """ 9382 9383 for p in [p1, p2]: 9384 if not hasattr(self, p[0]): 9385 raise RuntimeError('\"{}\" is not a valid model attribute'.format(p[0])) 9386 9387 p1 = list(p1) + [log, False] 9388 p2 = list(p2) + [log, False] 9389 9390 sc1 = Scanner(self) 9391 sc1.quietRun = True 9392 sc1.addScanParameter(*p1) 9393 sc1.addScanParameter(*p2) 9394 sc1.addUserOutput(output) 9395 sc1.Run() 9396 9397 self.__scan2d_pars__ = [p1[0], p2[0], output] 9398 self.__scan2d_results__ = sc1.getResultMatrix() 9399 del sc1 9400 9401 def Scan2DPlot(self, title=None, log=None, format='lines', filename=None): 9402 """ 9403 Plot the results of a 2D scan generated with Scan2D 9404 9405 - *filename* the filename of the PNG file (default=None, no export) 9406 - *title* the plot title (default=None) 9407 - *log* if None a linear axis is assumed otherwise one of ['x','xy','xyz'] (default=None) 9408 - *format* the backend dependent line format (default='lines') or the *CommonStyle* 'lines' or 'points'. 9409 """ 9410 9411 plt.splot( 9412 self.__scan2d_results__, 0, 1, 2, titles=self.__scan2d_pars__, format=format 9413 ) 9414 plt.setRange('xyz') 9415 plt.setAxisLabel('x', self.__scan2d_pars__[0]) 9416 plt.setAxisLabel('y', self.__scan2d_pars__[1]) 9417 plt.setAxisLabel('z', 'Steady-state variable') 9418 if log != None: 9419 plt.setLogScale(log) 9420 if title == None: 9421 plt.setGraphTitle( 9422 'PySCeS Scan2D (' 9423 + self.ModelFile 9424 + ') ' 9425 + time.strftime("4a, %d %b %Y %H:%M:%S") 9426 ) 9427 else: 9428 plt.setGraphTitle(title) 9429 plt.replot() 9430 if filename != None: 9431 plt.export(filename, directory=self.ModelOutput, type='png') 9432 9433 def SetQuiet(self): 9434 """ 9435 SetQuiet() 9436 9437 Turn off as much solver reporting noise as possible: 9438 mod.__settings__['hybrd_mesg'] = 0 9439 mod.__settings__['nleq2_mesg'] = 0 9440 mod.__settings__["lsoda_mesg"] = 0 9441 mod.__settings__['mode_state_mesg'] = 0 9442 mod.__settings__['scan1_mesg'] = 0 9443 mod.__settings__['solver_switch_warning'] = False 9444 9445 Arguments: 9446 None 9447 9448 """ 9449 self.__settings__['hybrd_mesg'] = 0 9450 self.__settings__['nleq2_mesg'] = 0 9451 self.__settings__["lsoda_mesg"] = 0 9452 self.__settings__['mode_state_mesg'] = 0 9453 self.__settings__['scan1_mesg'] = 0 9454 self.__settings__['solver_switch_warning'] = False 9455 9456 def SetLoud(self): 9457 """ 9458 SetLoud() 9459 9460 Turn on as much solver reporting noise as possible: 9461 mod.__settings__['hybrd_mesg'] = 1 9462 mod.__settings__['nleq2_mesg'] = 1 9463 mod.__settings__["lsoda_mesg"] = 1 9464 mod.__settings__['mode_state_mesg'] = 1 9465 mod.__settings__['scan1_mesg'] = 1 9466 mod.__settings__['solver_switch_warning'] = True 9467 9468 Arguments: 9469 None 9470 9471 """ 9472 self.__settings__['hybrd_mesg'] = 1 9473 self.__settings__['nleq2_mesg'] = 1 9474 self.__settings__["lsoda_mesg"] = 1 9475 self.__settings__['mode_state_mesg'] = 1 9476 self.__settings__['scan1_mesg'] = 1 9477 self.__settings__['solver_switch_warning'] = True 9478 9479 def clone(self): 9480 """ 9481 Returns a deep copy of this model object (experimental!) 9482 9483 """ 9484 9485 print('\nINFO: Cloning function is currently experimental ... use with care.') 9486 return copy.deepcopy(self) 9487 9488 9489class WasteManagement(object): 9490 _gc_delay_hack = ( 9491 gplt # dont f$%^&*)ing even ask ... only 5 hrs to hack this workaround 9492 ) 9493 9494 9495__waste_manager = WasteManagement() 9496 9497# if __psyco_active__: 9498# psyco.bind(PysMod) 9499 9500if __name__ == '__main__': 9501 print('\nTo use PySCeS import it from a Python Shell: \n\timport pysces\n') 9502