00001 """
00002
00003 ScrumPy -- Metabolic Modelling with Python
00004
00005 Copyright Mark Poolman 1995 - 2002
00006
00007 This file is part of ScrumPy.
00008
00009 ScrumPy is free software; you can redistribute it and/or modify
00010 it under the terms of the GNU General Public License as published by
00011 the Free Software Foundation; either version 2 of the License, or
00012 (at your option) any later version.
00013
00014 ScrumPy is distributed in the hope that it will be useful,
00015 but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00017 GNU General Public License for more details.
00018
00019 You should have received a copy of the GNU General Public License
00020 along with ScrumPy; if not, write to the Free Software
00021 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00022
00023 """
00024
00025
00026 """
00027 ScrumPy - a package of modules for doing Structural and Kinetic metabolic modelling
00028 and related activities
00029 """
00030
00031 import os,sys,random
00032
00033
00034
00035
00036
00037 from BasicInfo import Location, HeaderPath, Version, Banner
00038
00039 from Util import Seq, File,Types,Set,ErrRep
00040 from Parser import Parser, SBML
00041 from Data import DataSets
00042
00043 from Structural.Model import Model as StruMod
00044
00045 import Kinetic
00046 from Kinetic.Model import Param, Conc, Vel, dMet
00047 from Kinetic.Model import Model as KinMod
00048
00049 ToolBox="Tools"
00050
00051
00052
00053 gui = None
00054
00055 def Init(TheGui):
00056 global gui
00057 gui = Kinetic.Model.gui = TheGui
00058 print "\n", Banner
00059
00060
00061 def GetPath(FileName):
00062
00063 path = os.sep.join(FileName.split(os.sep)[:-1])
00064
00065 if path =="" or path == ".":
00066 path = "./"
00067
00068 elif not (path[0] == "/"):
00069 path = "./"+ path
00070
00071 return path + os.sep
00072
00073
00074 class Model(StruMod, KinMod):
00075
00076 FileName = "No File"
00077 DefaultOptDic = {
00078 "ElType" : Types.ArbRat,
00079 "AutoExtern" : False,
00080 "AutoDuplicateR" : False,
00081 "PolymerCheck" : "None",
00082 "UseKinetics" : True,
00083 "DeQuote" : False
00084 }
00085
00086
00087 def __init__(self, FName=None, AskUser=True, WithKin=True,ExtOnly=False, Child=False, Path=None):
00088
00089 self.Parent = None
00090 self.HasParent=Child
00091 self.Children=[]
00092 self.WithKin = WithKin
00093
00094 FName = self.GetFileName(FName, AskUser)
00095 if FName==None:
00096 gui.ErrorMsg("No input file !\nModel not usable")
00097 else:
00098
00099 if Child:
00100 FName = Path+FName
00101 else:
00102 Path = GetPath(FName)
00103 sys.path.append(os.sep.join((Path, ToolBox)))
00104
00105 self.Path = Path
00106
00107 if not os.path.exists(FName):
00108 open(FName,"w")
00109
00110 if SBML.LooksLikeSBML(FName):
00111 spyf = SBML.SFNameToSpy(FName)
00112 errs = SBML.SBML2Spy(FName, spyf)
00113 if len(errs)>0:
00114 ErrRep("ScrumPy: Errors in sbml file\nProceed with caution")
00115 FName = spyf
00116
00117 self.FileName = FName
00118 self.ReadFile()
00119
00120
00121 self.DynMons = []
00122 self.StatMons = []
00123 self.Edit()
00124
00125
00126 def __repr__(self):
00127 return "ScrumPy.Model from "+self.FileName
00128
00129 def GetFileName(self, FName, AskUser):
00130 if AskUser and FName==None:
00131 FName = gui.LoadFileName()
00132 if FName ==():
00133 self.WithKin=False
00134 gui.InfoMsg("Supply name of a model file to be created")
00135 FName = gui.SaveFileName()
00136
00137 if FName ==():
00138 FName = None
00139
00140 return FName
00141
00142
00143 def Raise(self, x=""):
00144 raise x
00145
00146 def Usable(self):
00147
00148 return self.WithKin and self.ParsedOK and KinMod.Usable(self)
00149
00150
00151
00152 def Destroy(self):
00153 StruMod.Destroy(self)
00154 if self.Usable():
00155 KinMod.Destroy(self)
00156
00157 def AddDynMonitor(self, mon=None, *args, **kwargs):
00158 if mon != None:
00159 self.DynMons.append(mon)
00160 else:
00161 mon = Kinetic.Model.Monitor(self, *args, **kwargs)
00162 mon.SetPlotX("Time")
00163 self.DynMons.append(mon)
00164 return mon
00165
00166
00167 def AddStatMonitor(self,mon=None, *args, **kwargs):
00168 if mon != None:
00169 self.StatMons.append(mon)
00170 else:
00171 mon = Kinetic.Model.Monitor(self, *args, **kwargs)
00172 self.StatMons.append(mon)
00173 return mon
00174
00175
00176
00177 def TellDynMons(self, Tincr, NSteps):
00178 for mon in self.DynMons:
00179 try: mon.NewSim(Tincr, NSteps)
00180 except: pass
00181
00182 def UpdateDynMons(self):
00183 for mon in self.DynMons:
00184 mon.Update()
00185
00186
00187 def UpdateStatMons(self):
00188 for mon in self.StatMons:
00189 mon.Update()
00190
00191 def Unregister(self, mon):
00192 if mon in self.StatMons:
00193 del self.StatMons[self.StatMons.index(mon)]
00194
00195 if mon in self.DynMons:
00196 del self.DynMons[self.DynMons.index(mon)]
00197
00198 def resetMons(self):
00199
00200 if self.WithKin:
00201
00202 if not self.Usable():
00203 for m in self.DynMons + self.StatMons:
00204 self.Unregister(m)
00205 try: m.destroy()
00206 except: pass
00207
00208 else:
00209 bad = []
00210 for m in self.DynMons + self.StatMons:
00211 try:
00212 m.Reset()
00213 except:
00214 bad.append(m)
00215 for b in bad:
00216 self.Unregister(b)
00217 try: b.destroy()
00218 except: pass
00219
00220 def destroyMons(self):
00221 for m in self.DynMons + self.StatMons:
00222 m.destroy()
00223
00224 m.StatMons = []
00225 m.DynMons = []
00226
00227 def Randomise(self, lorel=0.5, hirel=0.5, *args, **kwargs):
00228
00229 for name, val in self.ModelDesc.InitDic.items():
00230 self[name] = val * ( 1+random.uniform(-lorel, hirel))
00231
00232 self.InitCSums()
00233 self.Eval()
00234 self.ResetInteg()
00235
00236
00237
00238 def Simulate(self,TimeStep=None, NSteps=1, MonData=None, MonFuncs=[]):
00239
00240 self.TellDynMons(TimeStep, NSteps)
00241
00242 if TimeStep:
00243 self.SetTincr(TimeStep)
00244
00245 for n in range(NSteps):
00246 self.Sim(self,1)
00247 self.UpdateDynMons()
00248 for f in MonFuncs:
00249 f(self)
00250
00251 self.UpdateStatMons()
00252
00253
00254
00255
00256 def FindSS(self,*args,**kwargs):
00257 KinMod.FindSS(self,*args,**kwargs)
00258 self.UpdateStatMons()
00259
00260
00261
00262
00263
00264 def ReadFile(self):
00265 self.ModelName = os.path.split(self.FileName)[1].split(".")[0]
00266 Input = open(self.FileName).read()
00267
00268 ModelDesc = self.ModelDesc = Parser.ModelDescriptor()
00269 ModelDesc.FileName = self.FileName
00270 ModelDesc.Model = self
00271
00272 ModelDesc.OptDic = dict(self.DefaultOptDic)
00273
00274 Parser.Parse(Input,ModelDesc,Model)
00275 chdic={}
00276 self.LinkChildren(chdic)
00277
00278
00279 self.ParsedOK = len(self.ModelDesc.ErrList) ==0
00280 if not self.ParsedOK:
00281 self.ReportParseErrors()
00282 self.WithKin=False
00283 else:
00284 if not self.HasParent:
00285 self.MergeChildren()
00286 if len(self.ModelDesc.ErrList) >0:
00287 self.ReportParseErrors()
00288 else:
00289 self.ModelDesc.PolymerCheck()
00290 self.ReportWarnings()
00291 self.BuildStructural()
00292 self.WithKin = ModelDesc.OptDic["UseKinetics"]
00293 if ModelDesc.OptDic["UseKinetics"]:
00294 ModelDesc.sm = self.sm
00295 if len(self.sm) ==0:
00296 self.WithKin = ModelDesc.OptDic["UseKinetics"] = False
00297 else:
00298 KinMod.__init__(self)
00299
00300
00301
00302
00303
00304
00305 def MergeChildren(self):
00306
00307 descendents = self.GetDescendents()
00308
00309 for mod in descendents:
00310 if not mod.ParsedOK:
00311 self.ModelDesc.ErrList.append((
00312 "Errors in included model ",
00313 -1,
00314 mod.FileName,
00315 ""))
00316
00317
00318 if len(self.ModelDesc.ErrList) == 0:
00319 for mod in descendents:
00320 self.ModelDesc.Merge(mod.ModelDesc)
00321
00322
00323
00324
00325
00326
00327 def OldLinkChildren(self,seen=[]):
00328
00329 self.Children = self.ModelDesc.Children
00330
00331 for ch in self.Children:
00332 if ch in seen:
00333 sys.stderr.write("!\n! Multiple inclusion of " + ch.FileName + " - ignoring !\n!")
00334 else:
00335 ch.Parent = self
00336 seen.append(ch)
00337 ch.LinkChildren(seen)
00338
00339
00340
00341 def LinkChildren(self,seen):
00342
00343 self.Children = self.ModelDesc.Children
00344
00345 for ch in self.Children:
00346 if seen.has_key(ch):
00347 seen[ch] += 1
00348 else:
00349 ch.Parent = self
00350 seen[ch]=1
00351 ch.LinkChildren(seen)
00352
00353
00354
00355 def GetDescendents(self):
00356 """ pre: self.LinkChildren()
00357 post: return a list of all models included in self (excluding self) """
00358
00359 rv = []
00360 for ch in self.Children:
00361 rv.append(ch)
00362 rv+= ch.GetDescendents()
00363 return rv
00364
00365
00366 def Edit(self, LineNo=None):
00367
00368 if not self.HasParent:
00369 if not hasattr(self, "EdWin"):
00370 self.EdWin = gui.EdWin(Model=self, Root=gui.root)
00371
00372 if gui.UIType == "wx":
00373 self.EdWin.OpenFile(self.FileName)
00374 for c in self.GetDescendents():
00375 self.EdWin.OpenFile(c.FileName)
00376
00377 else:
00378 if LineNo:
00379 self.EdWin.gotoline(LineNo)
00380 for c in self.GetDescendents():
00381 if hasattr(c, "EdWin"):
00382 c.EdWin.close()
00383 c.EdWin = gui.EdWin(c, Root=gui.root)
00384
00385
00386
00387
00388
00389 def CheckXs(self, MetList, ExtList):
00390 rems = []
00391 for x in ExtList[:]:
00392 if not x in MetList:
00393 ExtList.remove(x)
00394 rems.append(x)
00395 if len(rems) >0:
00396 gui.WarnMsg("Metabolites declared external, but not used:" + str(rems))
00397
00398
00399 def Include(self, ModelFiles):
00400
00401 NotIncl = []
00402
00403
00404 for mod in self.Included:
00405 if mod.FileName in ModelFiles:
00406 mod.Parent = None
00407 mod.Reload()
00408 mod.Parent = self
00409 ModelFiles.remove(mod.FileName)
00410 else:
00411 NotIncl.append(mod)
00412
00413 for mod in NotIncl:
00414 self.Included.remove(mod)
00415 mod.EdWin._close()
00416 del mod
00417
00418 for mfile in ModelFiles:
00419 try:
00420 mod = Model(mfile)
00421 mod.Parent = self
00422 self.Included.append(mod)
00423 except:
00424 gui.WarnMsg("Couldn't include "+mfile+"(wrong filename ?)")
00425
00426
00427 for mod in self.Included:
00428 self.MergeWith(mod)
00429
00430
00431
00432
00433
00434 def MergeWith(self, mod):
00435
00436 clash = {}
00437
00438 def MergeMtx(m1, m2, clash):
00439
00440 for col in m2.cnames:
00441 if col in m1.cnames:
00442 clash[col] = 1
00443 else:
00444 for row in m2.rnames:
00445 el = m2[row,col]
00446 if el != 0:
00447 m1.NewEl(row,col,el)
00448
00449 MergeMtx(self.sm, mod.sm, clash)
00450 MergeMtx(self.smexterns, mod.smexterns,clash)
00451
00452 self.sm.Externs = self.smexterns.Externs = Set.Union(self.sm.Externs, mod.sm.Externs)
00453 if len(clash)>0:
00454 gui.WarnMsg("The following reactions were duplicated in included model "+mod.FileName+":" +str(clash.keys()) +":ignoring them")
00455
00456
00457
00458 def Reload(self):
00459
00460 if self.Parent != None:
00461 self.Parent.Reload()
00462 else:
00463 if gui.UIType == "wx":
00464 self.EdWin.SaveFile()
00465 self.EdWin.CloseFile()
00466
00467 else:
00468 try:
00469 self.EdWin.wakeup()
00470 self.EdWin.SaveFile()
00471 except:
00472 pass
00473
00474 for ch in self.Children:
00475 try:
00476 ch.EdWin.SaveFile()
00477 ch.EdWin.close()
00478 except:
00479 pass
00480
00481
00482
00483 self.Destroy()
00484 self.ReadFile()
00485 self.Edit()
00486 self.resetMons()
00487
00488
00489 def ReportParseErrors(self,MaxRep=10):
00490
00491 ErrList = self.ModelDesc.ErrList
00492
00493 gui.ErrorMsg("Errors reading "+self.FileName+"\nFirst was " + ErrList[0][0] +"\nat or before line no. " + str(ErrList[0][1])+
00494 '\nat or before: "'+ ErrList[0][2]+'"')
00495 for Err in ErrList[:MaxRep]:
00496 print Err[0], " at line ", Err[1], '"', Err[2], '"'
00497 lerr = len(ErrList)
00498 if lerr > MaxRep:
00499 print "A further ", MaxRep - lerr, " errors not reported"
00500
00501
00502
00503 def ReportWarnings(self):
00504 msg = self.ModelDesc.WarningsAsStr()
00505 print msg
00506 if msg !="":
00507 gui.WarnMsg(msg)
00508
00509
00510
00511
00512 def PostParseErrChk(self, FunList, IniDic, ErrList,OptDic):
00513
00514 for fun in FunList:
00515 name,line = fun[0],fun[1]
00516 if name in self.sm.cnames:
00517 ErrList.append((" reaction used as function", line,name, None))
00518 if name in self.sm.rnames:
00519 ErrList.append((" metabolite used as function", line,name, None))
00520 else:
00521 if name in IniDic.keys():
00522 ErrList.append((" parameter used as function", line,name, None))
00523
00524 if len(self.sm.rows) == 0 and len(OptDic["Include"]) ==0:
00525 ErrList.append((" no internal metabolites ", -1, "", None))
00526
00527 def ReportLastError(self):
00528 print "ReportLastError",self.ModelName +" : " +self.ErrorMsg()
00529 gui.ErrorMsg(self.ModelName +" : " +self.ErrorMsg())
00530
00531
00532 def AddReactions(self, *args, **kwargs):
00533
00534 print "ScrumPy.Model.AddReactions() due for removal"
00535
00536 if self.WithKin:
00537 raise exceptions.AtributeError, "can't add reactions to kinetic models (yet)"
00538 StruMod.AddReactions(self, *args,**kwargs)
00539
00540
00541
00542
00543
00544 def SaveSBML(self,fname=None):
00545 SBML.Spy2SBML(self,fname)
00546