• Main Page
  • Namespaces
  • Classes
  • Files
  • File List

/home/mark/model/software/ScrumPy/ScrumPy/Model.py

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                                     #system imports
00032 
00033 
00034 
00035 
00036                                                           # ScrumPy imports
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                              # import structural components
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 # A place for the user to keep common modules related to the model.
00051 # See model.__init__()
00052 
00053 gui = None
00054 
00055 def Init(TheGui):
00056     global gui
00057     gui  = Kinetic.Model.gui = TheGui
00058     print "\n", Banner     # hello, punters
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,       # default options for directives
00079         "AutoExtern"     : False,          # parser can change these
00080         "AutoDuplicateR" : False,
00081         "PolymerCheck"   : "None",
00082         "UseKinetics"    : True, # self.WithKin  # according to input
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  # bool, i.e. if we are a child,  we must have a parent, unkown  at this point
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 # ie childen get path from parent, parent works it out for themself - Parser.Parser.Include
00106 
00107             if not os.path.exists(FName):
00108                 open(FName,"w") # if it doesn't exist create it
00109 
00110             if SBML.LooksLikeSBML(FName):            # if it's sbml,  convert into ScrumPy and read that.
00111                 spyf = SBML.SFNameToSpy(FName)   # name of the ScrumPy file
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() # otherwise treat as a ScrumPy text file
00119             #self.Edit()
00120 
00121         self.DynMons = []       # static and dynamic monitors,
00122         self.StatMons = []      # Added elsewhere
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 ==():          # no file name supplied
00133                 self.WithKin=False  # so we can't use Kinetics
00134                 gui.InfoMsg("Supply name of a model file to be created")
00135                 FName = gui.SaveFileName() # try and open a new one
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  # we don't mind if mon doesn't have NewSim
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():  # if the c compilation has failed using mons will segfault
00203                 for m in self.DynMons + self.StatMons:
00204                     self.Unregister(m)
00205                     try: m.destroy()
00206                     except: pass  # m may not have a destroy method, it's up to them
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         #self.LastTime = self["Time"]
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()  # will set edit to highlight correct line
00282             self.WithKin=False       # failed to build, so we can't have inited kinetics
00283         else:
00284             if not self.HasParent:  # if we have a parent, leave  it all to them
00285                 self.MergeChildren()
00286                 if len(self.ModelDesc.ErrList) >0:
00287                     self.ReportParseErrors() # problem with one or more children
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   # do we need this ??
00295                         if len(self.sm) ==0:
00296                             self.WithKin = ModelDesc.OptDic["UseKinetics"] = False
00297                         else:
00298                             KinMod.__init__(self) # don't try to init an empty model
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:  # if we are a child let our parent take care of us
00369             if not hasattr(self, "EdWin"):
00370                 self.EdWin = gui.EdWin(Model=self, Root=gui.root) # TODO: more checking on state of ed, tk vs wx etc.
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:    # ie old tk/idle interface
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): # update ourself with any included models
00400 
00401         NotIncl = []  # models that were included but are no longer
00402 
00403 
00404         for mod in self.Included:
00405             if mod.FileName in ModelFiles:     # reload the models that we have seen before
00406                 mod.Parent = None                    # make it independent from oursef first
00407                 mod.Reload()                             # reload it
00408                 mod.Parent = self                       # then reattach it
00409                 ModelFiles.remove(mod.FileName)
00410             else:
00411                 NotIncl.append(mod)               # these were included but are no more
00412 
00413         for mod in NotIncl:                           # destroy the models that we are no longer using
00414             self.Included.remove(mod)
00415             mod.EdWin._close()
00416             del mod
00417 
00418         for mfile in ModelFiles:                  # (try to) load models that haven't been previously included
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:            # finally merge the included models with ourself
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:  # if we have been included by something else they must reload
00461             self.Parent.Reload() # and will reload us later
00462         else:
00463             if gui.UIType == "wx":
00464                 self.EdWin.SaveFile()
00465                 self.EdWin.CloseFile()
00466 
00467             else:
00468                 try:
00469                     self.EdWin.wakeup()   # Throws an exception if user closed window via GUI
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 # usr may have closed the window !
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         #self.Edit(ErrList[0][1])
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 

Generated on Tue Sep 4 2012 15:38:01 for ScrumPy by  doxygen 1.7.1