Example #1
0
 def AuxVars(self, t, xdict, pdict=None, asarray=True):
     """asarray is an unused, dummy argument for compatibility with
     Model.AuxVars"""
     x = [float(val) for val in sortedDictValues(filteredDict(self._FScompatibleNames(xdict),
                                                              self.funcspec.vars))]
     if pdict is None:
         pdict = self.pars
         # internal self.pars already is FS-compatible
         p = sortedDictValues(pdict)
     else:
         p = sortedDictValues(self._FScompatibleNames(pdict))
     i = _pollInputs(sortedDictValues(self.inputs),
                     t, self.checklevel)
     return apply(getattr(self,self.funcspec.auxspec[1]), [t, x, p+i])
 def Rhs(self, t, xdict, pdict=None, asarray=True):
     """asarray is an unused, dummy argument for compatibility with Model.Rhs"""
     # must convert names to FS-compatible as '.' sorts before letters
     # while '_' sorts after!
     # also, ensure xdict doesn't contain elements like array([4.1]) instead of 4
     x = [float(val) for val in sortedDictValues(filteredDict(self._FScompatibleNames(xdict),
                                                              self.funcspec.vars))]
     if pdict is None:
         pdict = self.pars
         # internal self.pars already is FS-compatible
         p = sortedDictValues(pdict)
     else:
         p = sortedDictValues(self._FScompatibleNames(pdict))
     i = _pollInputs(sortedDictValues(self.inputs),
                     t, self.checklevel)
     return apply(getattr(self, self.funcspec.spec[1]), [t, x, p+i])
Example #3
0
 def JacobianP(self, t, xdict, pdict=None, asarray=True):
     """asarray is an unused, dummy argument for compatibility with
     Model.JacobianP"""
     if self.haveJacobian_pars():
         x = [float(val) for val in sortedDictValues(filteredDict(self._FScompatibleNames(xdict),
                                                                  self.funcspec.vars))]
         if pdict is None:
             pdict = self.pars
             # internal self.pars already is FS-compatible
             p = sortedDictValues(pdict)
         else:
             p = sortedDictValues(self._FScompatibleNames(pdict))
         i = _pollInputs(sortedDictValues(self.inputs),
                     t, self.checklevel)
         return apply(getattr(self,self.funcspec.auxfns["Jacobian_pars"][1]), \
                     [t, x, p+i])
     else:
         raise PyDSTool_ExistError("Jacobian w.r.t. parameters not defined")
 def Jacobian(self, t, xdict, pdict=None, asarray=True):
     """asarray is an unused, dummy argument for compatibility with
     Model.Jacobian"""
     if self.haveJacobian():
         # also, ensure xdict doesn't contain elements like array([4.1]) instead of 4
         x = [float(val) for val in sortedDictValues(filteredDict(self._FScompatibleNames(xdict),
                                                                  self.funcspec.vars))]
         if pdict is None:
             pdict = self.pars
             # internal self.pars already is FS-compatible
             p = sortedDictValues(pdict)
         else:
             p = sortedDictValues(self._FScompatibleNames(pdict))
         i = _pollInputs(sortedDictValues(self.inputs),
                     t, self.checklevel)
         return apply(getattr(self, self.funcspec.auxfns["Jacobian"][1]), \
                      [t, x, p+i])
     else:
         raise PyDSTool_ExistError("Jacobian not defined")
    def compute(self, trajname, dirn='f', ics=None):
        continue_integ = ODEsystem.prepDirection(self, dirn)
        if self._dircode == -1:
            raise NotImplementedError('Backwards integration is not implemented')
        if ics is not None:
            self.set(ics=ics)
        self.validateICs()
        self.diagnostics.clearWarnings()
        self.diagnostics.clearErrors()
        pnames = sortedDictKeys(self.pars)
        xnames = self._var_ixmap  # ensures correct order
        # Check i.c.'s are well defined (finite)
        self.checkInitialConditions()
        if self.algparams['stiff']:
            methstr = 'bdf'
            methcode = 2
        else:
            methstr = 'adams'
            methcode = 1
        haveJac = int(self.haveJacobian())
        if isinstance(self.algparams['atol'], list):
            if len(self.algparams['atol']) != self.dimension:
                raise ValueError('atol list must have same length as phase '
                                 'dimension')
        else:
            atol = self.algparams['atol']
            self.algparams['atol'] = [atol for dimix in xrange(self.dimension)]
        indepdom0, indepdom1 = self.indepvariable.depdomain.get()
        if continue_integ:
            if indepdom0 > self._solver.t:
                print "Previous end time is %f"%self._solver.t
                raise ValueError("Start time not correctly updated for "
                                 "continuing orbit")
            x0 = self._solver.y
            indepdom0 = self._solver.t
            self.indepvariable.depdomain.set((indepdom0,indepdom1))
        else:
            x0 = sortedDictValues(self.initialconditions,
                                        self.funcspec.vars)
        t0 = indepdom0
        if self.algparams['use_special']:
            tmesh_special = list(self.algparams['specialtimes'])
            if continue_integ:
                if self._solver.t not in tmesh_special:
                    raise ValueError("Invalid time to continue integration:"
                                     "it is not in 'special times'")
                tmesh_special = tmesh_special[tmesh_special.index(self._solver.t):]
            try:
                dt = min([tmesh_special[1]-t0, self.algparams['init_step']])
            except:
                raise ValueError("Invalid list of special times")
            if not isincreasing(tmesh_special):
                raise ValueError("special times must be given in increasing "
                                 "order")
            if self.indepvariable.depdomain.contains(t0) is notcontained or \
              self.indepvariable.depdomain.contains(tmesh_special[-1]) is notcontained:
                raise PyDSTool_BoundsError("special times were outside of independent "
                                 "variable bounds")
        else:
            tmesh_special = []
            dt = self.algparams['init_step']
        # speed up repeated access to solver by making a temp name for it
        solver = self._solver
        if solver._integrator is None:
            # Banded Jacobians not yet supported
            #
            # start a new integrator, because method may have been
            # switched
            solver.set_integrator('vode', method=methstr,
                                 rtol=self.algparams['rtol'],
                                 atol=self.algparams['atol'],
                                 nsteps=self.algparams['max_pts'],
                                 max_step=self.algparams['max_step'],
                                 min_step=self.algparams['min_step'],
                                 first_step=dt/2.,
                                 with_jacobian=haveJac)
        else:
            solver.with_jacobian = haveJac
#            self.mu = lband
#            self.ml = uband
            solver.rtol = self.algparams['rtol']
            solver.atol = self.algparams['atol']
            solver.method = methcode
#            self.order = order
            solver.nsteps = self.algparams['max_pts']
            solver.max_step = self.algparams['max_step']
            solver.min_step = self.algparams['min_step']
            solver.first_step = dt/2.
        solver.set_initial_value(x0, t0)
        ## Broken code for going backwards (doesn't respect 'continue' option
        ##  either)
##            if self._dircode == 1:
##                solver.set_initial_value(x0, t0)
##            else:
##                solver.set_initial_value(x0, indepdom1)
        # wrap up each dictionary initial value as a singleton list
        alltData = [t0]
        allxDataDict = dict(zip(xnames, map(listid, x0)))
        plist = sortedDictValues(self.pars)
        extralist = copy(plist)
        if self.inputs:
            # inputVarList is a list of Variables
            inames = sortedDictKeys(self.inputs)
            listend = self.numpars + len(self.inputs)
            inputVarList = sortedDictValues(self.inputs)
            ilist = _pollInputs(inputVarList, alltData[0]+self.globalt0,
                               self.checklevel)
        else:
            ilist = []
            inames = []
            listend = self.numpars
            inputVarList = []
        extralist.extend(ilist)
        solver.set_f_params(extralist)
        if haveJac:
            solver.set_jac_params(extralist)
        do_poly = self.algparams['poly_interp']
        if do_poly:
            rhsfn = getattr(self, self.funcspec.spec[1])
            dx0 = rhsfn(t0, x0, extralist)
            alldxDataDict = dict(zip(xnames, map(listid, dx0)))
        auxvarsfn = getattr(self,self.funcspec.auxspec[1])
        strict = self.algparams['strictdt']
        # Make t mesh if it wasn't given as 'specialtimes'
        if not all(isfinite(self.indepvariable.depdomain.get())):
            print "Time domain was: ", self.indepvariable.depdomain.get()
            raise ValueError("Ensure time domain is finite")
        if dt == indepdom1 - indepdom0:
            # single-step integration required
            # special times will not have been set (unless trivially
            # they are [indepdom0, indepdom1])
            tmesh = [indepdom0, indepdom1]
        else:
            notDone = True
            repeatTol = 10
            count = 0
            while notDone and count <= repeatTol:
                try:
                    tmesh = self.indepvariable.depdomain.sample(dt,
                                          strict=strict,
                                          avoidendpoints=self.checklevel>2)
                    notDone = False
                except AssertionError:
                    count += 1
                    dt = dt/3.0
            if count == repeatTol:
                raise AssertionError("Supplied time step is too large for "
                                     "selected time interval")
            # incorporate tmesh_special, if used, ensuring uniqueness
            if tmesh_special != []:
                tmesh.extend(tmesh_special)
                tmesh = list(unique(tmesh))
                tmesh.sort()
            if len(tmesh)<=2:
                # safety net, in case too few points in mesh
                # too few points unless we can add endpoint
                if tmesh[-1] != indepdom1:
                    # dt too large for tmesh to have more than one point
                    tmesh.append(indepdom1)
            if not strict:  # get actual time step used
                # don't use [0] in case avoided end points
                try:
                    dt = tmesh[2]-tmesh[1]
                except IndexError:
                    # can't avoid end points for such a small mesh
                    dt = tmesh[1]-tmesh[0]
        #if self.eventstruct.query(['lowlevel']) != []:
        #    raise ValueError("Only high level events can be passed to VODE")
        eventslist = self.eventstruct.query(['active', 'notvarlinked'])
        termevents = self.eventstruct.query(['term'], eventslist)
        # reverse time by reversing mesh doesn't work
##        if self._dircode == -1:
##            tmesh.reverse()
        tmesh.pop(0)  # get rid of first entry for initial condition
        xnames = self.funcspec.vars
        # storage of all auxiliary variable data
        allaDataDict = {}
        anames = self.funcspec.auxvars
        avals = auxvarsfn(t0, x0, extralist)
        for aix in range(len(anames)):
            aname = anames[aix]
            try:
                allaDataDict[aname] = [avals[aix]]
            except IndexError:
                print "\nVODE generator: There was a problem evaluating " \
                      + "an auxiliary variable"
                print "Debug info: avals (length", len(avals), ") was ", avals
                print "Index out of range was ", aix
                print self.funcspec.auxspec[1]
                print hasattr(self, self.funcspec.auxspec[1])
                print "Args were:", [t0, x0, extralist]
                raise
        # Initialize signs of event detection objects at IC
        self.setEventICs(self.initialconditions, self.globalt0)
        if self.inputs:
            parsinps = copy(self.pars)
            parsinps.update(dict(zip(inames,ilist)))
        else:
            parsinps = self.pars
        if eventslist != []:
            dataDict = copy(self.initialconditions)
            dataDict.update(dict(zip(anames, avals)))
            dataDict['t'] = t0
            evsflagged = self.eventstruct.pollHighLevelEvents(None,
                                                            dataDict,
                                                            parsinps,
                                                            eventslist)
            if len(evsflagged) > 0:
                raise RuntimeError("Some events flagged at initial condition")
            if continue_integ:
                # revert to prevprevsign, since prevsign changed after call
                self.eventstruct.resetHighLevelEvents(t0, eventslist, 'prev')
            elif self._for_hybrid_DS:
                # self._for_hybrid_DS is set internally by HybridModel class
                # to ensure not to reset events, because they may be about to
                # flag on first step if previous hybrid state was the same
                # generator and, for example, two variables are synchronizing
                # so that their events get very close together.
                # Just reset the starttimes of these events
                for evname, ev in eventslist:
                    ev.starttime = t0
            else:
                # default state is a one-off call to this generator
                self.eventstruct.resetHighLevelEvents(t0, eventslist, None)
                self.eventstruct.validateEvents(self.funcspec.vars + \
                                            self.funcspec.auxvars + \
                                            self.funcspec.inputs + \
                                            ['t'], eventslist)
        # temp storage of first time at which terminal events found
        # (this is used for keeping the correct end point of new mesh)
        first_found_t = None
        # list of precise non-terminal events to be resolved after integration
        nontermprecevs = []
        evnames = [ev[0] for ev in eventslist]
        lastevtime = {}.fromkeys(evnames, None)
        # initialize new event info dictionaries
        Evtimes = {}
        Evpoints = {}
        if continue_integ:
            for evname in evnames:
                try:
                    # these are in global time, so convert to local time
                    lastevtime[evname] = self.eventstruct.Evtimes[evname][-1] \
                                           - self.globalt0
                except (IndexError, KeyError):
                    # IndexError: Evtimes[evname] was None
                    # KeyError: Evtimes does not have key evname
                    pass
        for evname in evnames:
            Evtimes[evname] = []
            Evpoints[evname] = []
        # temp storage for repeatedly used object attributes (for lookup efficiency)
        depdomains = dict(zip(range(self.dimension),
                        [self.variables[xn].depdomain for xn in xnames]))
        # Main integration loop
        num_points = 0
        breakwhile = False
        while not breakwhile:
            try:
                new_t = tmesh.pop(0)  # this destroys tmesh for future use
            except IndexError:
                # empty
                break
            last_t = solver.t   # a record of previous time for use by event detector
            try:
                errcode = solver.integrate(new_t)
            except:
                print "Error calling right hand side function:"
                self.showSpec()
                print "Numerical traceback information (current state, " \
                      + "parameters, etc.)"
                print "in generator dictionary 'traceback'"
                self.traceback = {'vars': dict(zip(xnames,solver.y)),
                                  'pars': dict(zip(pnames,plist)),
                                  'inputs': dict(zip(inames,ilist)),
                                  self.indepvariable.name: new_t}
                raise
            avals = auxvarsfn(new_t, solver.y, extralist)
            # Uncomment the following assertion for debugging
#            assert all([isfinite(a) for a in avals]), \
#               "Some auxiliary variable values not finite"
            if eventslist != []:
                dataDict = dict(zip(xnames,solver.y))
                dataDict.update(dict(zip(anames,avals)))
                dataDict['t'] = new_t
                if self.inputs:
                    parsinps = copy(self.pars)
                    parsinps.update(dict(zip(inames,
                              _pollInputs(inputVarList, new_t+self.globalt0,
                                          self.checklevel))))
                else:
                    parsinps = self.pars
                evsflagged = self.eventstruct.pollHighLevelEvents(None,
                                                            dataDict,
                                                            parsinps,
                                                            eventslist)
##                print new_t, evsflagged
##                evsflagged = [ev for ev in evsflagged if solver.t-indepdom0 > ev[1].eventinterval]
                termevsflagged = filter(lambda e: e in evsflagged, termevents)
                nontermevsflagged = filter(lambda e: e not in termevsflagged,
                                           evsflagged)
                # register any non-terminating events in the warnings
                # list, unless they are 'precise' in which case flag
                # them to be resolved after integration completes
                if len(nontermevsflagged) > 0:
                    evnames = [ev[0] for ev in nontermevsflagged]
                    precEvts = self.eventstruct.query(['precise'],
                                                          nontermevsflagged)
                    prec_evnames = [e[0] for e in precEvts]
                    # first register non-precise events
                    nonprecEvts = self.eventstruct.query(['notprecise'],
                                                         nontermevsflagged)
                    nonprec_evnames = [e[0] for e in nonprecEvts]
                    # only record events if they have not been previously
                    # flagged within their event interval
                    if nonprec_evnames != []:
                        temp_names = []
                        for evname, ev in nonprecEvts:
                            prevevt_time = lastevtime[evname]
                            if prevevt_time is None:
                                ignore_ev = False
                            else:
                                if solver.t-prevevt_time < ev.eventinterval:
                                    ignore_ev = True
                                else:
                                    ignore_ev = False
                            if not ignore_ev:
                                temp_names.append(evname)
                                lastevtime[evname] = solver.t
                        self.diagnostics.warnings.append((W_NONTERMEVENT,
                                     (solver.t, temp_names)))
                        for evname in temp_names:
                            Evtimes[evname].append(solver.t)
                            xv = solver.y
                            av = array(avals)
                            Evpoints[evname].append(concatenate((xv, av)))
                    for evname, ev in precEvts:
                        # only record events if they have not been previously
                        # flagged within their event interval
                        prevevt_time = lastevtime[evname]
                        if prevevt_time is None:
                            ignore_ev = False
                        else:
                            if last_t-prevevt_time < ev.eventinterval:
                                ignore_ev = True
                            else:
                                ignore_ev = False
                        if not ignore_ev:
                            nontermprecevs.append((last_t, solver.t, (evname, ev)))
                            # be conservative as to where the event is, so
                            # that don't miss any events.
                            lastevtime[evname] = last_t # solver.t-dt
                        ev.reset() #ev.prevsign = None #
                do_termevs = []
                if termevsflagged != []:
                    # only record events if they have not been previously
                    # flagged within their event interval
                    for e in termevsflagged:
                        prevevt_time = lastevtime[e[0]]
##                        print "Event %s flagged."%e[0]
##                        print "  ... last time was ", prevevt_time
##                        print "  ... event interval = ", e[1].eventinterval
##                        print "  ... t = %f, dt = %f"%(solver.t, dt)
                        if prevevt_time is None:
                            ignore_ev = False
                        else:
##                            print "  ... comparison = %f < %f"%(solver.t-dt-prevevt_time, e[1].eventinterval)
                            if last_t-prevevt_time < e[1].eventinterval:
                                ignore_ev = True
##                                print "VODE ignore ev"
                            else:
                                ignore_ev = False
                        if not ignore_ev:
                            do_termevs.append(e)
                if len(do_termevs) > 0:
                    # >= 1 active terminal event flagged at this time point
                    if all([not ev[1].preciseFlag for ev in do_termevs]):
                        # then none of the events specify greater accuracy
                        # register the event in the warnings
                        evnames = [ev[0] for ev in do_termevs]
                        self.diagnostics.warnings.append((W_TERMEVENT, \
                                             (solver.t, evnames)))
                        for evname in evnames:
                            Evtimes[evname].append(solver.t)
                            xv = solver.y
                            av = array(avals)
                            Evpoints[evname].append(concatenate((xv, av)))
                        # break while loop after appending t, x
                        breakwhile = True
                    else:
                        # find which are the 'precise' events that flagged
                        precEvts = self.eventstruct.query(['precise'],
                                                          do_termevs)
                        # these events have flagged once so eventdelay has
                        # been used. now switch it off while finding event
                        # precisely (should be redundant after change to
                        # eventinterval and eventdelay parameters)
                        evnames = [ev[0] for ev in precEvts]
                        if first_found_t is None:
##                            print "first time round at", solver.t
                            numtries = 0
                            first_found_t = solver.t
                            restore_evts = deepcopy(precEvts)
                            minbisectlimit = min([ev[1].bisectlimit for ev in precEvts])
                            for ev in precEvts:
                                ev[1].eventdelay = 0.
                        else:
                            numtries += 1
##                            print "time round: ", numtries
                            if numtries > minbisectlimit:
                                self.diagnostics.warnings.append((W_BISECTLIMIT,
                                                      (solver.t, evnames)))
                                breakwhile = True

                        # get previous time point
                        if len(alltData)>=1:
                            # take one step back -> told, which will
                            # get dt added back to first new meshpoint
                            # (solver.t is the step *after* the event was
                            # detected)
                            told = last_t # solver.t-dt without the loss of precision from subtraction
                        else:
                            raise ValueError("Event %s found too "%evnames[0]+\
                                 "close to local start time: try decreasing "
                                 "initial step size (current size is "
                                 "%f @ t=%f)"%(dt,solver.t+self.globalt0))

                        # absolute tolerance check on event function values (not t)
                        in_tols = [abs(e[1].fval) < e[1].eventtol for e in precEvts]
                        if all(in_tols):
##                            print "Registering event:", dt_min, dt
                            # register the event in the warnings
                            self.diagnostics.warnings.append((W_TERMEVENT,
                                             (solver.t, evnames)))
                            for evname in evnames:
                                Evtimes[evname].append(solver.t)
                                xv = solver.y
                                av = array(avals)
                                Evpoints[evname].append(concatenate((xv, av)))
                            # Cannot continue -- dt_min no smaller than
                            # previous dt. If this is more than the first time
                            # in this code then have found the event to within
                            # the minimum 'precise' event's eventtol, o/w need
                            # to set eventtol smaller.
                            # Before exiting event-finding loop, reset prevsign
                            # of flagged events
                            self.eventstruct.resetHighLevelEvents(0,
                                                             precEvts)
                            breakwhile = True  # while loop, but append point first
                        if not breakwhile:
                            dt_new = dt/5.0
                            # calc new tmesh
                            trangewidth = 2*dt #first_found_t - told
                            numpoints = int(math.ceil(trangewidth/dt_new))
                            # choose slightly smaller dt to fit trange exactly
                            dt = trangewidth/numpoints

                            tmesh = [told + i*dt for i in xrange(1, numpoints+1)]
                            # linspace version is *much* slower for numpoints ~ 10 and 100
                            #tmesh = list(told+linspace(dt, numpoints*dt, numpoints))

                            # reset events according to new time mesh,
                            # setting known previous event state to be their
                            # "no event found" state
                            self.eventstruct.resetHighLevelEvents(told,
                                                                  precEvts,
                                                                  state='off')
                            # build new ic with last good values (at t=told)
                            if len(alltData)>1:
                                new_ic = [allxDataDict[xname][-1] \
                                            for xname in xnames]
                            else:
                                new_ic = x0
                            # reset integrator
                            solver.set_initial_value(new_ic, told)
                            extralist[self.numpars:listend] = _pollInputs(inputVarList,
                                                    told+self.globalt0, self.checklevel)
                            solver.set_f_params(extralist)
                            # continue integrating over new mesh
                            continue  # while
            # after events have had a chance to be detected at a state boundary
            # we check for any that have not been caught by an event (will be
            # much less accurately determined)
            if not breakwhile:
                # only here if a terminal event hasn't just flagged
                for xi in xrange(self.dimension):
                    if not self.contains(depdomains[xi],
                                     solver.y[xi],
                                     self.checklevel):
                        self.diagnostics.warnings.append((W_TERMSTATEBD,
                                    (solver.t,
                                     xnames[xi],solver.y[xi],
                                     depdomains[xi].get())))
                        breakwhile = True
                        break  # for loop
                if breakwhile:
                    break  # while loop
            alltData.append(solver.t)
            if do_poly:
                dxvals = rhsfn(solver.t, solver.y, extralist)
                for xi, xname in enumerate(xnames):
                    allxDataDict[xname].append(solver.y[xi])
                    alldxDataDict[xname].append(dxvals[xi])
            else:
                for xi, xname in enumerate(xnames):
                    allxDataDict[xname].append(solver.y[xi])
            for aix, aname in enumerate(anames):
                allaDataDict[aname].append(avals[aix])
            num_points += 1
            if not breakwhile:
                try:
                    extralist[self.numpars:listend] = [f(solver.t+self.globalt0,
                                                         self.checklevel) \
                                                  for f in inputVarList]
                except ValueError:
                    print 'External input call caused value out of range error:',\
                          't = ', solver.t
                    for f in inputVarList:
                        if f.diagnostics.hasWarnings():
                            print 'External input variable %s out of range:'%f.name
                            print '   t = ', repr(f.diagnostics.warnings[-1][0]), ', ', \
                                  f.name, ' = ', repr(f.diagnostics.warnings[-1][1])
                    raise
                except AssertionError:
                    print 'External input call caused t out of range error: t = ', \
                          solver.t
                    raise
                solver.set_f_params(extralist)
                breakwhile = not solver.successful()
        # Check that any terminal events found terminated the code correctly
        if first_found_t is not None:
            # ... then terminal events were found. Those that were 'precise' had
            # their 'eventdelay' attribute temporarily set to 0. It now should
            # be restored.
            for evname1, ev1 in termevents:
                # restore_evts are copies of the originally flagged 'precise'
                # events
                for evname2, ev2 in restore_evts:
                    if evname2 == evname1:
                        ev1.eventdelay = ev2.eventdelay
            try:
                if self.diagnostics.warnings[-1][0] not in [W_TERMEVENT,
                                                            W_TERMSTATEBD]:
                    print "t =", solver.t
                    print "state =", dict(zip(xnames,solver.y))
                    raise RuntimeError("Event finding code for terminal event "
                                       "failed in Generator " + self.name + \
                                       ": try decreasing eventdelay or "
                                       "eventinterval below eventtol, or the "
                                       "atol and rtol parameters")
            except IndexError:
                info(self.diagnostics.outputStats, "Output statistics")
                print "t =", solver.t
                print "x =", solver.y
                raise RuntimeError("Event finding failed in Generator " + \
                                   self.name + ": try decreasing eventdelay "
                                   "or eventinterval below eventtol, or the "
                                   "atol and rtol parameters")
        # Package up computed trajectory in Variable variables
        # Add external inputs warnings to self.diagnostics.warnings, if any
        for f in inputVarList:
            for winfo in f.diagnostics.warnings:
                self.diagnostics.warnings.append((W_NONTERMSTATEBD,
                                     (winfo[0], f.name, winfo[1],
                                      f.depdomain.get())))
        # check for non-unique terminal event
        termcount = 0
        for (w,i) in self.diagnostics.warnings:
            if w == W_TERMEVENT or w == W_TERMSTATEBD:
                termcount += 1
                if termcount > 1:
                    self.diagnostics.errors.append((E_NONUNIQUETERM,
                                                    (alltData[-1], i[1])))
        # uncomment the following lines for debugging
#        assert len(alltData) == len(allxDataDict.values()[0]) \
#             == len(allaDataDict.values()[0]), "Output data size mismatch"
#        for val_list in allaDataDict.values():
#            assert all([isfinite(x) for x in val_list])
        # Create variables (self.variables contains no actual data)
        # These versions of the variables are only final if no non-terminal
        # events need to be inserted.
        variables = copyVarDict(self.variables)
        for x in xnames:
            if len(alltData) > 1:
                if do_poly:
                    xvals = array([allxDataDict[x], alldxDataDict[x]]).T
                    interp = PiecewisePolynomial(alltData, xvals, 2)
                else:
                    xvals = allxDataDict[x]
                    interp = interp1d(alltData, xvals)
                variables[x] = Variable(interp, 't', x, x)
            else:
                print "Error in Generator:", self.name
                print "t = ", alltData
                print "x = ", allxDataDict
                raise PyDSTool_ValueError("Fewer than 2 data points computed")
        for a in anames:
            if len(alltData) > 1:
                variables[a] = Variable(interp1d(alltData, allaDataDict[a]),
                                        't', a, a)
            else:
                print "Error in Generator:", self.name
                print "t = ", alltData
                print "x = ", allxDataDict
                raise PyDSTool_ValueError("Fewer than 2 data points computed")
        # Resolve non-terminal 'precise' events that were flagged, using the
        # variables created. Then, add them to a new version of the variables.
        ntpe_tdict = {}
        for (et0,et1,e) in nontermprecevs:
            lost_evt = False
            # problem if eventinterval > et1-et0 !!
            # was: search_dt = max((et1-et0)/5,e[1].eventinterval)
            search_dt = (et1-et0)/5
            try:
                et_precise_list = e[1].searchForEvents(trange=[et0,et1],
                                              dt=search_dt,
                                              checklevel=self.checklevel,
                                              parDict=self.pars,
                                              vars=variables,
                                              inputs=self.inputs,
                                              abseps=self._abseps,
                                              eventdelay=False,
                                              globalt0=self.globalt0)
            except (ValueError, PyDSTool_BoundsError):
                # dt too large for trange, e.g. if event tol is smaller than time mesh
                et_precise_list = [(et0, (et0, et1))]
            if et_precise_list == []:
                lost_evt = True
            for et_precise in et_precise_list:
                if et_precise[0] is not None:
                    if et_precise[0] in ntpe_tdict:
                        # add event name at this time (that already exists in the dict)
                        ntpe_tdict[et_precise[0]].append(e[0])
                    else:
                        # add event name at this time (when time is not already in dict)
                        ntpe_tdict[et_precise[0]] = [e[0]]
                else:
                    lost_evt = True
            if lost_evt:
                print "Error: A non-terminal, 'precise' event was lost -- did you reset",
                print "events prior to integration?"
                raise PyDSTool_ExistError("Internal error: A non-terminal, "
                    "'precise' event '%s' was lost after integration!"%e[0])
        # add non-terminal event points to variables
        if ntpe_tdict != {}:
            # find indices of times at which event times will be inserted
            tix = 0
            evts = ntpe_tdict.keys()
            evts.sort()
            for evix in xrange(len(evts)):
                evt = evts[evix]
                evnames = ntpe_tdict[evt]
                self.diagnostics.warnings.append((W_NONTERMEVENT, (evt, evnames)))
                xval = [variables[x](evt) for x in xnames]
                ilist = _pollInputs(inputVarList, evt+self.globalt0,
                               self.checklevel)
                # find accurate dx and aux vars value at this time
                if do_poly:
                    dxval = rhsfn(evt, xval, plist+ilist)
                aval = list(auxvarsfn(evt, xval, plist+ilist))
                for evname in evnames:
                    Evtimes[evname].append(evt)
                    Evpoints[evname].append(array(xval+aval))
                tcond = less_equal(alltData[tix:], evt).tolist()
                try:
                    tix = tcond.index(0) + tix  # lowest index for t > evt
                    do_insert = (alltData[tix-1] != evt)
                except ValueError:
                    # evt = last t value so no need to add it
                    do_insert = False
                if do_insert:
                    alltData.insert(tix, evt)
                    for ai, a in enumerate(anames):
                        allaDataDict[a].insert(tix, aval[ai])
                    if do_poly:
                        for xi, x in enumerate(xnames):
                            allxDataDict[x].insert(tix, xval[xi])
                            alldxDataDict[x].insert(tix, dxval[xi])
                    else:
                        for xi, x in enumerate(xnames):
                            allxDataDict[x].insert(tix, xval[xi])
            for x in xnames:
                if do_poly:
                    # use asarray in case points added to sequences as a list
                    xvals = array([asarray(allxDataDict[x]),
                                   asarray(alldxDataDict[x])]).T
                    interp = PiecewisePolynomial(alltData, xvals, 2)
                else:
                    xvals = allxDataDict[x]
                    interp = interp1d(alltData, xvals)
                variables[x] = Variable(interp, 't', x, x)
            for a in anames:
                variables[a] = Variable(interp1d(alltData, allaDataDict[a]),
                                            't', a, a)
        self.diagnostics.outputStats = {'last_step': dt,
                            'num_fcns': num_points,
                            'num_steps': num_points,
                            'errorStatus': errcode
                            }
        if solver.successful():
            #self.validateSpec()
            for evname, evtlist in Evtimes.iteritems():
                try:
                    self.eventstruct.Evtimes[evname].extend([et+self.globalt0 \
                                            for et in evtlist])
                except KeyError:
                    self.eventstruct.Evtimes[evname] = [et+self.globalt0 \
                                            for et in evtlist]
            # build event pointset information (reset previous trajectory's)
            self.trajevents = {}
            for (evname, ev) in eventslist:
                evpt = Evpoints[evname]
                if evpt == []:
                    self.trajevents[evname] = None
                else:
                    evpt = transpose(array(evpt))
                    self.trajevents[evname] = Pointset({'coordnames': xnames+anames,
                                               'indepvarname': 't',
                                               'coordarray': evpt,
                                               'indepvararray': Evtimes[evname],
                                               'indepvartype': float})
            self.defined = True
            return Trajectory(trajname, variables.values(),
                              abseps=self._abseps, globalt0=self.globalt0,
                              checklevel=self.checklevel,
                              FScompatibleNames=self._FScompatibleNames,
                              FScompatibleNamesInv=self._FScompatibleNamesInv,
                              events=self.trajevents,
                              modelNames=self.name,
                              modelEventStructs=self.eventstruct)
        else:
            try:
                self.diagnostics.errors.append((E_COMPUTFAIL, (solver.t,
                                    self.diagnostics._errorcodes[errcode])))
            except TypeError:
                # e.g. when errcode has been used to return info list
                print "Error information: ", errcode
                self.diagnostics.errors.append((E_COMPUTFAIL, (solver.t,
                                    self.diagnostics._errorcodes[0])))
            self.defined = False
    def compute(self, trajname, dirn='f', ics=None):
        continue_integ = ODEsystem.prepDirection(self, dirn)
        if self._dircode == -1:
            raise NotImplementedError('Backwards integration is not implemented')
        if ics is not None:
            self.set(ics=ics)
        self.validateICs()
        self.diagnostics.clearWarnings()
        self.diagnostics.clearErrors()
        pnames = sortedDictKeys(self.pars)
        xnames = self._var_ixmap  # ensures correct order
        # Check i.c.'s are well defined (finite)
        self.checkInitialConditions()
        haveJac = int(self.haveJacobian())
        indepdom0, indepdom1 = self.indepvariable.depdomain.get()
        if continue_integ:
            if indepdom0 > self._solver.t:
                print "Previous end time is %f"%self._solver.t
                raise ValueError("Start time not correctly updated for "
                                 "continuing orbit")
            x0 = self._solver.y
            indepdom0 = self._solver.t
            self.indepvariable.depdomain.set((indepdom0,indepdom1))
        else:
            x0 = sortedDictValues(self.initialconditions,
                                        self.funcspec.vars)
        t0 = indepdom0
        dt = self.algparams['init_step']
        # speed up repeated access to solver by making a temp name for it
        solver = self._solver
        solver.set_initial_value(x0, t0)
        solver.dt = dt
        # wrap up each dictionary initial value as a singleton list
        alltData = [t0]
        allxDataDict = dict(zip(xnames, map(listid, x0)))
        plist = sortedDictValues(self.pars)
        extralist = copy(plist)
        if self.inputs:
            # inputVarList is a list of Variables
            inames = sortedDictKeys(self.inputs)
            listend = self.numpars + len(self.inputs)
            inputVarList = sortedDictValues(self.inputs)
            ilist = _pollInputs(inputVarList, alltData[0]+self.globalt0,
                               self.checklevel)
        else:
            ilist = []
            inames = []
            listend = self.numpars
            inputVarList = []
        extralist.extend(ilist)
        solver.set_f_params(extralist)
        if haveJac:
            solver.set_jac_params(extralist)
        do_poly = self.algparams['poly_interp']
        if do_poly:
            rhsfn = getattr(self, self.funcspec.spec[1])
            dx0 = rhsfn(t0, x0, extralist)
            alldxDataDict = dict(zip(xnames, map(listid, dx0)))
        auxvarsfn = getattr(self,self.funcspec.auxspec[1])
        # Make t mesh if it wasn't given as 'specialtimes'
        if not all(isfinite(self.indepvariable.depdomain.get())):
            print "Time domain was: ", self.indepvariable.depdomain.get()
            raise ValueError("Ensure time domain is finite")
        if dt == indepdom1 - indepdom0:
            # single-step integration required
            # special times will not have been set (unless trivially
            # they are [indepdom0, indepdom1])
            tmesh = [indepdom0, indepdom1]
        else:
            notDone = True
            while notDone:
                tmesh = self.indepvariable.depdomain.sample(dt,
                                          strict=True,
                                          avoidendpoints=True)
                notDone = False
        first_found_t = None
        eventslist = self.eventstruct.query(['active', 'notvarlinked'])
        termevents = self.eventstruct.query(['term'], eventslist)
        tmesh.pop(0)  # get rid of first entry for initial condition
        xnames = self.funcspec.vars
        # storage of all auxiliary variable data
        allaDataDict = {}
        anames = self.funcspec.auxvars
        avals = auxvarsfn(t0, x0, extralist)
        for aix in range(len(anames)):
            aname = anames[aix]
            try:
                allaDataDict[aname] = [avals[aix]]
            except IndexError:
                print "\nEuler generator: There was a problem evaluating " \
                      + "an auxiliary variable"
                print "Debug info: avals (length", len(avals), ") was ", avals
                print "Index out of range was ", aix
                print self.funcspec.auxspec[1]
                print hasattr(self, self.funcspec.auxspec[1])
                print "Args were:", [t0, x0, extralist]
                raise
        # Initialize signs of event detection objects at IC
        self.setEventICs(self.initialconditions, self.globalt0)
        if self.inputs:
            parsinps = copy(self.pars)
            parsinps.update(dict(zip(inames,ilist)))
        else:
            parsinps = self.pars
        if eventslist != []:
            dataDict = copy(self.initialconditions)
            dataDict.update(dict(zip(anames, avals)))
            dataDict['t'] = t0
            evsflagged = self.eventstruct.pollHighLevelEvents(None,
                                                            dataDict,
                                                            parsinps,
                                                            eventslist)
            if len(evsflagged) > 0:
                raise RuntimeError("Some events flagged at initial condition")
            if continue_integ:
                # revert to prevprevsign, since prevsign changed after call
                self.eventstruct.resetHighLevelEvents(t0, eventslist, 'prev')
            elif self._for_hybrid_DS:
                # self._for_hybrid_DS is set internally by HybridModel class
                # to ensure not to reset events, because they may be about to
                # flag on first step if previous hybrid state was the same
                # generator and, for example, two variables are synchronizing
                # so that their events get very close together.
                # Just reset the starttimes of these events
                for evname, ev in eventslist:
                    ev.starttime = t0
            else:
                # default state is a one-off call to this generator
                self.eventstruct.resetHighLevelEvents(t0, eventslist, None)
                self.eventstruct.validateEvents(self.funcspec.vars + \
                                            self.funcspec.auxvars + \
                                            self.funcspec.inputs + \
                                            ['t'], eventslist)
        evnames = [ev[0] for ev in eventslist]
        lastevtime = {}.fromkeys(evnames, None)
        # initialize new event info dictionaries
        Evtimes = {}
        Evpoints = {}
        if continue_integ:
            for evname in evnames:
                try:
                    # these are in global time, so convert to local time
                    lastevtime[evname] = self.eventstruct.Evtimes[evname][-1] \
                                           - self.globalt0
                except (IndexError, KeyError):
                    # IndexError: Evtimes[evname] was None
                    # KeyError: Evtimes does not have key evname
                    pass
        for evname in evnames:
            Evtimes[evname] = []
            Evpoints[evname] = []
        # temp storage for repeatedly used object attributes (for lookup efficiency)
        depdomains = dict(zip(range(self.dimension),
                        [self.variables[xn].depdomain for xn in xnames]))
        # Main integration loop
        num_points = 0
        breakwhile = False
        while not breakwhile:
            try:
                new_t = tmesh.pop(0)  # this destroys tmesh for future use
            except IndexError:
                # empty
                break
            # optional user function (not a method)
            self.ufunc_before(self)
            try:
                errcode = solver.integrate(dt)
            except:
                print "Error calling right hand side function:"
                self.showSpec()
                print "Numerical traceback information (current state, " \
                      + "parameters, etc.)"
                print "in generator dictionary 'traceback'"
                self.traceback = {'vars': dict(zip(xnames,solver.y)),
                                  'pars': dict(zip(pnames,plist)),
                                  'inputs': dict(zip(inames,ilist)),
                                  self.indepvariable.name: new_t}
                raise
            avals = auxvarsfn(new_t, solver.y, extralist)
            # Uncomment the following assertion for debugging
#            assert all([isfinite(a) for a in avals]), \
#               "Some auxiliary variable values not finite"
            if eventslist != []:
                dataDict = dict(zip(xnames,solver.y))
                dataDict.update(dict(zip(anames,avals)))
                dataDict['t'] = new_t
                if self.inputs:
                    parsinps = copy(self.pars)
                    parsinps.update(dict(zip(inames,
                              _pollInputs(inputVarList, new_t+self.globalt0,
                                          self.checklevel))))
                else:
                    parsinps = self.pars
                evsflagged = self.eventstruct.pollHighLevelEvents(None,
                                                            dataDict,
                                                            parsinps,
                                                            eventslist)
##                print new_t, evsflagged
##                evsflagged = [ev for ev in evsflagged if solver.t-indepdom0 > ev[1].eventinterval]
                termevsflagged = filter(lambda e: e in evsflagged, termevents)
                nontermevsflagged = filter(lambda e: e not in termevsflagged,
                                           evsflagged)
                # register any non-terminating events in the warnings
                # list, unless they are 'precise' in which case flag
                # them to be resolved after integration completes
                if len(nontermevsflagged) > 0:
                    evnames = [ev[0] for ev in nontermevsflagged]
                    precEvts = self.eventstruct.query(['precise'],
                                                          nontermevsflagged)
                    # register both precise and non-precise events the same
                    # (Euler currently ignores precise events with its fixed time step)
                    nonprecEvts = self.eventstruct.query(['notprecise'],
                                                         nontermevsflagged) + precEvts
                    nonprec_evnames = [e[0] for e in nonprecEvts] + [e[0] for e in precEvts]
                    # only record events if they have not been previously
                    # flagged within their event interval
                    if nonprec_evnames != []:
                        temp_names = []
                        for evname, ev in nonprecEvts:
                            prevevt_time = lastevtime[evname]
                            if prevevt_time is None:
                                ignore_ev = False
                            else:
                                if solver.t-prevevt_time < ev.eventinterval:
                                    ignore_ev = True
                                else:
                                    ignore_ev = False
                            if not ignore_ev:
                                temp_names.append(evname)
                                lastevtime[evname] = solver.t
                        self.diagnostics.warnings.append((W_NONTERMEVENT,
                                     (solver.t, temp_names)))
                        for evname in temp_names:
                            Evtimes[evname].append(solver.t)
                            xv = solver.y
                            av = array(avals)
                            Evpoints[evname].append(concatenate((xv, av)))
                do_termevs = []
                if termevsflagged != []:
                    # only record events if they have not been previously
                    # flagged within their event interval
                    for e in termevsflagged:
                        prevevt_time = lastevtime[e[0]]
##                        print "Event %s flagged."%e[0]
##                        print "  ... last time was ", prevevt_time
##                        print "  ... event interval = ", e[1].eventinterval
##                        print "  ... t = %f, dt = %f"%(solver.t, dt)
                        if prevevt_time is None:
                            ignore_ev = False
                        else:
##                            print "  ... comparison = %f < %f"%(solver.t-dt-prevevt_time, e[1].eventinterval)
                            if solver.t-dt-prevevt_time < e[1].eventinterval:
                                ignore_ev = True
##                                print "Euler ignore ev"
                            else:
                                ignore_ev = False
                        if not ignore_ev:
                            do_termevs.append(e)
                if len(do_termevs) > 0:
                    # >= 1 active terminal event flagged at this time point
                    evnames = [ev[0] for ev in do_termevs]
                    self.diagnostics.warnings.append((W_TERMEVENT, \
                                         (solver.t, evnames)))
                    first_found_t = solver.t
                    for evname in evnames:
                        Evtimes[evname].append(solver.t)
                        xv = solver.y
                        av = array(avals)
                        Evpoints[evname].append(concatenate((xv, av)))
                    # break while loop after appending t, x
                    breakwhile = True
            # after events have had a chance to be detected at a state boundary
            # we check for any that have not been caught by an event (will be
            # much less accurately determined)
            if not breakwhile:
                # only here if a terminal event hasn't just flagged
                for xi in xrange(self.dimension):
                    if not self.contains(depdomains[xi],
                                     solver.y[xi],
                                     self.checklevel):
                        self.diagnostics.warnings.append((W_TERMSTATEBD,
                                    (solver.t,
                                     xnames[xi],solver.y[xi],
                                     depdomains[xi].get())))
                        breakwhile = True
                        break  # for loop
                if breakwhile:
                    break  # while loop
            alltData.append(solver.t)
            if do_poly:
                dxvals = rhsfn(solver.t, solver.y, extralist)
                for xi, xname in enumerate(xnames):
                    allxDataDict[xname].append(solver.y[xi])
                    alldxDataDict[xname].append(dxvals[xi])
            else:
                for xi, xname in enumerate(xnames):
                    allxDataDict[xname].append(solver.y[xi])
            for aix, aname in enumerate(anames):
                allaDataDict[aname].append(avals[aix])
            num_points += 1
            if not breakwhile:
                try:
                    extralist[self.numpars:listend] = [f(solver.t+self.globalt0,
                                                         self.checklevel) \
                                                  for f in inputVarList]
                except ValueError:
                    print 'External input call caused value out of range error:',\
                          't = ', solver.t
                    for f in inputVarList:
                        if f.diagnostics.hasWarnings():
                            print 'External input variable %s out of range:'%f.name
                            print '   t = ', repr(f.diagnostics.warnings[-1][0]), ', ', \
                                  f.name, ' = ', repr(f.diagnostics.warnings[-1][1])
                    raise
                except AssertionError:
                    print 'External input call caused t out of range error: t = ', \
                          solver.t
                    raise
                solver.set_f_params(extralist)
                breakwhile = not solver.successful()
            # optional user function (not a method)
            self.ufunc_after(self)

        # Check that any terminal events found terminated the code correctly
        if first_found_t is not None:
            # ... then terminal events were found.
            try:
                if self.diagnostics.warnings[-1][0] not in [W_TERMEVENT,
                                                            W_TERMSTATEBD]:
                    print "t =", solver.t
                    print "state =", dict(zip(xnames,solver.y))
                    raise RuntimeError("Event finding code for terminal event "
                                       "failed in Generator " + self.name + \
                                       ": try decreasing eventdelay or "
                                       "eventinterval below eventtol, or the "
                                       "atol and rtol parameters")
            except IndexError:
                info(self.diagnostics.outputStats, "Output statistics")
                print "t =", solver.t
                print "x =", solver.y
                raise RuntimeError("Event finding failed in Generator " + \
                                   self.name + ": try decreasing eventdelay "
                                   "or eventinterval below eventtol")
        # Package up computed trajectory in Variable variables
        # Add external inputs warnings to self.diagnostics.warnings, if any
        for f in inputVarList:
            for winfo in f.diagnostics.warnings:
                self.diagnostics.warnings.append((W_NONTERMSTATEBD,
                                     (winfo[0], f.name, winfo[1],
                                      f.depdomain.get())))
        # check for non-unique terminal event
        termcount = 0
        for (w,i) in self.diagnostics.warnings:
            if w == W_TERMEVENT or w == W_TERMSTATEBD:
                termcount += 1
                if termcount > 1:
                    self.diagnostics.errors.append((E_NONUNIQUETERM,
                                                    (alltData[-1], i[1])))
        # uncomment the following lines for debugging
#        assert len(alltData) == len(allxDataDict.values()[0]) \
#             == len(allaDataDict.values()[0]), "Output data size mismatch"
#        for val_list in allaDataDict.values():
#            assert all([isfinite(x) for x in val_list])
        # Create variables (self.variables contains no actual data)
        # These versions of the variables are only final if no non-terminal
        # events need to be inserted.
        variables = copyVarDict(self.variables)
        for x in xnames:
            if len(alltData) > 1:
                if do_poly:
                    xvals = array([allxDataDict[x], alldxDataDict[x]]).T
                    interp = PiecewisePolynomial(alltData, xvals, 2)
                else:
                    xvals = allxDataDict[x]
                    interp = interp1d(alltData, xvals)
                variables[x] = Variable(interp, 't', x, x)
            else:
                print "Error in Generator:", self.name
                print "t = ", alltData
                print "x = ", allxDataDict
                raise PyDSTool_ValueError("Fewer than 2 data points computed")
        for a in anames:
            if len(alltData) > 1:
                variables[a] = Variable(interp1d(alltData, allaDataDict[a]),
                                        't', a, a)
            else:
                print "Error in Generator:", self.name
                print "t = ", alltData
                print "x = ", allxDataDict
                raise PyDSTool_ValueError("Fewer than 2 data points computed")
        self.diagnostics.outputStats = {'last_step': dt,
                            'num_fcns': num_points,
                            'num_steps': num_points,
                            'errorStatus': errcode
                            }
        if solver.successful():
            #self.validateSpec()
            for evname, evtlist in Evtimes.iteritems():
                try:
                    self.eventstruct.Evtimes[evname].extend([et+self.globalt0 \
                                            for et in evtlist])
                except KeyError:
                    self.eventstruct.Evtimes[evname] = [et+self.globalt0 \
                                            for et in evtlist]
            # build event pointset information (reset previous trajectory's)
            self.trajevents = {}
            for (evname, ev) in eventslist:
                evpt = Evpoints[evname]
                if evpt == []:
                    self.trajevents[evname] = None
                else:
                    evpt = transpose(array(evpt))
                    self.trajevents[evname] = Pointset({'coordnames': xnames+anames,
                                               'indepvarname': 't',
                                               'coordarray': evpt,
                                               'indepvararray': Evtimes[evname],
                                               'indepvartype': float})
            self.defined = True
            return Trajectory(trajname, variables.values(),
                              abseps=self._abseps, globalt0=self.globalt0,
                              checklevel=self.checklevel,
                              FScompatibleNames=self._FScompatibleNames,
                              FScompatibleNamesInv=self._FScompatibleNamesInv,
                              events=self.trajevents,
                              modelNames=self.name,
                              modelEventStructs=self.eventstruct)
        else:
            try:
                self.diagnostics.errors.append((E_COMPUTFAIL, (solver.t,
                                    self.diagnostics._errorcodes[errcode])))
            except TypeError:
                # e.g. when errcode has been used to return info list
                print "Error information: ", errcode
                self.diagnostics.errors.append((E_COMPUTFAIL, (solver.t,
                                    self.diagnostics._errorcodes[0])))
            self.defined = False