Example #1
0
 def addMethods(self):
     # override to add _solver function
     ODEsystem.addMethods(self)
     # Jacobian ignored
     self._solver = euler_solver(getattr(self,self.funcspec.spec[1]))
     self._funcreg['_solver'] = ('self', 'euler_solver(getattr(self,' \
                                               + 'self.funcspec.spec[1]))')
Example #2
0
    def __init__(self, kw):
        ODEsystem.__init__(self, kw)

        self.diagnostics._errorcodes = \
                {0: 'Unrecognized error code returned (see stderr output)',
                -1: 'Excess work done on this call. (Perhaps wrong method MF.)',
                -2: 'Excess accuracy requested. (Tolerances too small.)',
                -3: 'Illegal input detected. (See printed message.)',
                -4: 'Repeated error test failures. (Check all input.)',
                -5: 'Repeated convergence failures. (Perhaps bad'
                    ' Jacobian supplied or wrong choice of method MF or tolerances.)',
                -6: 'Error weight became zero during problem. (Solution'
                    ' component i vanished, and ATOL or ATOL(i) = 0.)'
                }
        self.diagnostics.outputStatsInfo = {'errorStatus': 'Error status on completion.'}
        # note: VODE only supports array atol, not rtol.
        algparams_def = {'poly_interp': False,
                         'rtol': 1e-9,
                         'atol': [1e-12 for dimix in range(self.dimension)],
                         'stiff': False,
                         'max_step': 0.0,
                         'min_step': 0.0,
                         'init_step': 0.01,
                         'max_pts': 1000000,
                         'strictdt': False,
                         'use_special': False,
                         'specialtimes': []
                         }
        for k, v in algparams_def.items():
            if k not in self.algparams:
                self.algparams[k] = v
Example #3
0
 def __init__(self, kw):
     if 'user_func_beforestep' in kw:
         self.ufunc_before = kw['user_func_beforestep']
         # delete because not covered in ODEsystem
         del kw['user_func_beforestep']
     else:
         self.ufunc_before = _dummy_userfunc
     if 'user_func_afterstep' in kw:
         self.ufunc_after = kw['user_func_afterstep']
         # delete because not covered in ODEsystem
         del kw['user_func_afterstep']
     else:
         self.ufunc_after = _dummy_userfunc
     ODEsystem.__init__(self, kw)
     self._paraminfo = {'init_step': 'Fixed step size for time mesh.'}
     self.diagnostics._errorcodes = {1: 'Step OK'}
     self.diagnostics.outputStatsInfo = {
         'errorStatus': 'Error status on completion.'
     }
     algparams_def = {
         'poly_interp': False,
         'init_step': 0.01,
         'max_pts': 100000
     }
     for k, v in algparams_def.items():
         if k not in self.algparams:
             self.algparams[k] = v
Example #4
0
    def __init__(self, kw):
        ODEsystem.__init__(self, kw)

        self.diagnostics._errorcodes = \
                {0: 'Unrecognized error code returned (see stderr output)',
                -1: 'Excess work done on this call. (Perhaps wrong method MF.)',
                -2: 'Excess accuracy requested. (Tolerances too small.)',
                -3: 'Illegal input detected. (See printed message.)',
                -4: 'Repeated error test failures. (Check all input.)',
                -5: 'Repeated convergence failures. (Perhaps bad'
                    ' Jacobian supplied or wrong choice of method MF or tolerances.)',
                -6: 'Error weight became zero during problem. (Solution'
                    ' component i vanished, and ATOL or ATOL(i) = 0.)'
                }
        self.diagnostics.outputStatsInfo = {
            'errorStatus': 'Error status on completion.'
        }
        # note: VODE only supports array atol, not rtol.
        algparams_def = {
            'poly_interp': False,
            'rtol': 1e-9,
            'atol': [1e-12 for dimix in range(self.dimension)],
            'stiff': False,
            'max_step': 0.0,
            'min_step': 0.0,
            'init_step': 0.01,
            'max_pts': 1000000,
            'strictdt': False,
            'use_special': False,
            'specialtimes': []
        }
        for k, v in algparams_def.items():
            if k not in self.algparams:
                self.algparams[k] = v
 def __init__(self, kw):
     ODEsystem.__init__(self, kw)
     assert self.funcspec.targetlang == 'python', \
            ('Wrong target language for functional specification. '
             'Python needed for this class')
     assert isinstance(self.funcspec, RHSfuncSpec), ('Vode '
                                 'requires RHSfuncSpec type to proceed')
     self._paraminfo = {'init_step': 'Fixed step size for time mesh.',
                        'strictdt': 'Boolean determining whether to evenly space time mesh (default=False), or to use exactly dt spacing.',
                        'stiff': 'Boolean to activate the BDF method, otherwise Adams method used. Default False.'}
     self._errorcodes = {0: 'Unrecognized error code returned (see stderr output)',
             -1: 'Excess work done on this call. (Perhaps wrong method MF.)',
             -2: 'Excess accuracy requested. (Tolerances too small.)',
             -3: 'Illegal input detected. (See printed message.)',
             -4: 'Repeated error test failures. (Check all input.)',
             -5: 'Repeated convergence failures. (Perhaps bad'
                 ' Jacobian supplied or wrong choice of method MF or tolerances.)',
             -6: 'Error weight became zero during problem. (Solution'
                 ' component i vanished, and ATOL or ATOL(i) = 0.)'
             }
     self._outputinfo = {'errorStatus': 'Error status on completion.'}
     # note: VODE only supports array atol, not rtol.
     algparams_def = {'rtol': 1e-9,
                      'atol': [1e-12 for dimix in xrange(self.dimension)],
                      'stiff': False,
                      'max_step': 0.0,
                      'min_step': 0.0,
                      'init_step': 0.01,
                      'max_pts': 1000000,
                      'strictdt': False
                      }
     for k, v in algparams_def.iteritems():
         if k not in self._algparams:
             self._algparams[k] = v
     self.outputstats = {}
Example #6
0
 def addMethods(self):
     # override to add _solver function
     ODEsystem.addMethods(self, usePsyco=False)
     if self.haveJacobian():
         self._solver = ode(getattr(self,self.funcspec.spec[1]),
                         getattr(self,self.funcspec.auxfns["Jacobian"][1]))
         self._funcreg['_solver'] = ('self',
              'ode(getattr(self,self.funcspec.spec[1]),' \
                + 'getattr(self,self.funcspec.auxfns["Jacobian"][1]))')
     else:
         self._solver = ode(getattr(self,self.funcspec.spec[1]))
         self._funcreg['_solver'] = ('self', 'ode(getattr(self,' \
                                               + 'self.funcspec.spec[1]))')
Example #7
0
 def addMethods(self):
     # override to add _solver function
     ODEsystem.addMethods(self, usePsyco=False)
     if self.haveJacobian():
         self._solver = ode(getattr(self,self.funcspec.spec[1]),
                         getattr(self,self.funcspec.auxfns["Jacobian"][1]))
         self._funcreg['_solver'] = ('self',
              'ode(getattr(self,self.funcspec.spec[1]),' \
                + 'getattr(self,self.funcspec.auxfns["Jacobian"][1]))')
     else:
         self._solver = ode(getattr(self,self.funcspec.spec[1]))
         self._funcreg['_solver'] = ('self', 'ode(getattr(self,' \
                                               + 'self.funcspec.spec[1]))')
Example #8
0
 def __init__(self, kw):
     ODEsystem.__init__(self, kw)
     assert self.funcspec.targetlang == 'python', \
            ('Wrong target language for functional specification. '
             'Python needed for this class')
     assert isinstance(
         self.funcspec,
         RHSfuncSpec), ('Vode '
                        'requires RHSfuncSpec type to proceed')
     self._paraminfo = {
         'init_step':
         'Fixed step size for time mesh.',
         'strictdt':
         'Boolean determining whether to evenly space time mesh (default=False), or to use exactly dt spacing.',
         'stiff':
         'Boolean to activate the BDF method, otherwise Adams method used. Default False.'
     }
     self._errorcodes = {
         0:
         'Unrecognized error code returned (see stderr output)',
         -1:
         'Excess work done on this call. (Perhaps wrong method MF.)',
         -2:
         'Excess accuracy requested. (Tolerances too small.)',
         -3:
         'Illegal input detected. (See printed message.)',
         -4:
         'Repeated error test failures. (Check all input.)',
         -5:
         'Repeated convergence failures. (Perhaps bad'
         ' Jacobian supplied or wrong choice of method MF or tolerances.)',
         -6:
         'Error weight became zero during problem. (Solution'
         ' component i vanished, and ATOL or ATOL(i) = 0.)'
     }
     self._outputinfo = {'errorStatus': 'Error status on completion.'}
     # note: VODE only supports array atol, not rtol.
     algparams_def = {
         'rtol': 1e-9,
         'atol': [1e-12 for dimix in xrange(self.dimension)],
         'stiff': False,
         'max_step': 0.0,
         'min_step': 0.0,
         'init_step': 0.01,
         'max_pts': 1000000,
         'strictdt': False
     }
     for k, v in algparams_def.iteritems():
         if k not in self._algparams:
             self._algparams[k] = v
     self.outputstats = {}
Example #9
0
 def __init__(self, kw):
     if 'user_func_beforestep' in kw:
         self.ufunc_before = kw['user_func_beforestep']
         # delete because not covered in ODEsystem
         del kw['user_func_beforestep']
     else:
         self.ufunc_before = _dummy_userfunc
     if 'user_func_afterstep' in kw:
         self.ufunc_after = kw['user_func_afterstep']
         # delete because not covered in ODEsystem
         del kw['user_func_afterstep']
     else:
         self.ufunc_after = _dummy_userfunc
     ODEsystem.__init__(self, kw)
     self._paraminfo = {'init_step': 'Fixed step size for time mesh.'}
     self.diagnostics._errorcodes = {1: 'Step OK'}
     self.diagnostics.outputStatsInfo = {'errorStatus': 'Error status on completion.'}
     algparams_def = {'poly_interp': False,
                      'init_step': 0.01,
                      'max_pts': 100000
                      }
     for k, v in algparams_def.items():
         if k not in self.algparams:
             self.algparams[k] = v
    def __init__(self, kw):
        """Use the nobuild key to postpone building of the library, e.g. in
        order to provide additional build options to makeLibSource and
        compileLib methods or to make changes to the C code by hand.
        No build options can be specified otherwise."""

        # Building is just doing make
        if 'nobuild' in kw:
            nobuild = kw['nobuild']
            del kw['nobuild']
        else:
            nobuild = False
        ODEsystem.__init__(self, kw)
        self._solver = None
        assert self.funcspec.targetlang == 'matlab', \
               ('Wrong target language for functional specification. '
                'matlab needed for this class')
        assert isinstance(self.funcspec, RHSfuncSpec), ('ADMC++ '
                                    'requires RHSfuncSpec type to proceed')
        assert not self.inputs, \
                        'ADMC++ does not support external inputs feature'
        self._errorcodes = {}
        self._paraminfo = {}

        self.vftype = 'vfieldts'

        # currently the final four of these params are for event handling
        # NEED TO CHECK WHICH ONES ARE SUPPORTED BY ADMC -- LOOKS LIKE EVTOLS ONLY FOR NOW
        # HACK: vftype is alg param for now, tells us whether parent class is hybridvf, vfieldts, etc.
        algparams_def = {'evtols' : 0.0001, 'vftype' : 'vfieldts'}

        # Remove this later
        for k, v in algparams_def.iteritems():
            if k not in self.algparams:
                self.algparams[k] = v

        # verify that no additional keys are present in algparams, after
        # defaults are added above
        if len(self.algparams) != len(algparams_def):
            raise ValueError("Invalid keys present in algparams argument: " \
                     + str(remain(self.algparams.keys(),algparams_def.keys())))

        thisplatform = platform.system()

        self._compilation_tempdir = os.path.join(os.getcwd(),
                                                      "admcpp_temp")
        if not os.path.isdir(self._compilation_tempdir):
            try:
                assert not os.path.isfile(self._compilation_tempdir), \
                     "A file already exists with the same name"
                os.mkdir(self._compilation_tempdir)
            except:
                print "Could not create compilation temp directory " + \
                      self._compilation_tempdir
                raise

        # ADMC targets must go in their own directories with appropriate names
        self._model_dir = "@"+self.name
        self._target_dir = os.path.join(self._compilation_tempdir,self._model_dir)
        # Make the target directory
        if not os.path.isdir(self._target_dir):
            try:
                assert not os.path.isfile(self._target_dir), \
                       "A file already exists with the same name"
                os.mkdir(self._target_dir)
            except:
                print "Could not creat target ADMC model directory " + \
                      self._target_dir
                raise


        """ An ADMC model has the following files:
        vfield.m -- contains code for the RHS of the vector field
        set.m -- a generic method that overload matlab's set method; only need to insert vfield name
        get.m -- a generic method that overloads matlab's get method; only need to insert appropriate parent name
        """

        # model.m, get.m, set.m, vfield.m are minimal files required. TO DO: EVENTS
        self._model_file = self.name+".m"
        self._ic_file = self.name+"_ics.m"
        self._param_file = self.name+"_params.m"
        self._set_file = "set.m"
        self._get_file = "get.m"
        self._vfield_file = "vfield.m"
        self._events_file = "events.m"

        self._vf_filename_ext = "_"+self._model_file[:-2]

        if not nobuild:
            self.makeLibSource()
        else:
            print "Build the library using the makeLib method, or in "
            print "stages using the makeLibSource and compileLib methods."
Example #11
0
 def __del__(self):
     ODEsystem.__del__(self)
Example #12
0
    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 range(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: %f" % 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 %d) was %r" %
                      (len(avals), avals))
                print("Index out of range was %d" % aix)
                print(self.funcspec.auxspec[1])
                print(hasattr(self, self.funcspec.auxspec[1]))
                print("Args were:%r" % [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
            ## Removed this "belt and braces" pre-check because it messes
            ## up some events in the middle of hybrid regime switches.
            ## Not sure yet what the problem is.  (Oct 2013)
            #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 = [e for e in termevents if e in evsflagged]
                nontermevsflagged = [
                    e for e in evsflagged if e not in termevsflagged
                ]
                # 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 range(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 range(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 = %f' % solver.t)
                    for f in inputVarList:
                        if f.diagnostics.hasWarnings():
                            print('External input variable %s out of range:' %
                                  f.name)
                            print('   t = %r, %s, %r' %
                                  (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 = %f'
                        % 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 =%f" % solver.t)
                    print("state =%r" % 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 =%f" % solver.t)
                print("x =%f" % 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:%s" % self.name)
                print("t = %r" % alltData)
                print("x = %r" % 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:%s" % self.name)
                print("t = %r" % alltData)
                print("x = %r" % 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",
                    end=' ')
                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 = list(ntpe_tdict.keys())
            evts.sort()
            for evix in range(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.items():
                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,
                              list(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: %d" % errcode)
                self.diagnostics.errors.append(
                    (E_COMPUTFAIL, (solver.t,
                                    self.diagnostics._errorcodes[0])))
            self.defined = False
Example #13
0
 def __del__(self):
     genDB.unregister(self)
     ODEsystem.__del__(self)
Example #14
0
    def compute(self, trajname, dirn='f', ics=None):
        continue_integ = ODEsystem.prepDirection(self, dirn)
        if ics is not None:
            self.set(ics=ics)
        self.validateICs()
        self.diagnostics.clearWarnings()
        self.diagnostics.clearErrors()
        if isinstance(self.algparams['rtol'], list):
            if len(self.algparams['rtol']) != self.dimension:
                raise ValueError('rtol list must have same length as phase dimension')
        else:
            rtol = self.algparams['rtol']
            self.algparams['rtol'] = [rtol for dimix in range(self.dimension)]
        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 range(self.dimension)]
        anames = self.funcspec.auxvars
        # Check i.c.'s are well defined (finite)
        self.checkInitialConditions()
        self.setEventICs(self.initialconditions, self.globalt0)
        # update event params in case changed since last run
        self._prepareEventSpecs()
        # Main integration
        t0 = self.indepvariable.depdomain[0]
        t1 = self.indepvariable.depdomain[1]
        plist = sortedDictValues(self.pars)
        self.algparams['hasJac'] = self.haveJacobian()
        self.algparams['hasJacP'] = self.haveJacobian_pars()
        if self._solver is None:
            self._solver = dopri(self.modname,
                                 rhs=self.name, phaseDim=self.dimension,
                                 paramDim=len(plist), nAux=len(anames),
                                 nEvents=len(self._eventNames),
                                 nExtInputs=len(self.inputs),
                                 hasJac=self.algparams['hasJac'],
                                 hasJacP=self.algparams['hasJacP'],
                                 hasMass=self.haveMass(),
                                 extraSpace=self.algparams['extraspace'],
                                 )
            try:
                genDB.register(self)
            except PyDSTool_KeyError:
                errstr = "Generator " + self.name + ": this vector field's " +\
                         "DLL is already in use"
                raise RuntimeError(errstr)
        if self._dircode == 1:
            tbegin = t0
            tend = t1
        elif self._dircode == -1:
            # dopri does reverse time integration simply by switching t0 and t1
            # and using negative steps
            tbegin = t1
            tend = t0
        if len(self.algparams['specialtimes'])>0:
            use_special = self.algparams['use_special']
        else:
            use_special = 0
        bounds = [[],[]]  # lower, then upper
        for v in self.funcspec.vars:
            bds = self.xdomain[v]
            bounds[0].append(bds[0])
            bounds[1].append(bds[1])
        for p in self.funcspec.pars:
            bds = self.pdomain[p]
            bounds[0].append(bds[0])
            bounds[1].append(bds[1])
        if continue_integ:
            x0 = self._solver.lastPoint
            # overwrite t0 from self.indepvariable.domain, but use its t1
            tbegin = self._solver.lastTime
            if abs(self._solver.lastStep) < abs(self.algparams['init_step']):
                self.algparams['init_step'] = self._solver.lastStep
            if abs(t1-tbegin) < abs(self.algparams['init_step']):
                raise ValueError("Integration end point too close to initial "
                                 "point")
#            if self.inputs and self._extInputsChanged:
#                self._extInputsChanged = False
#                self._solver.setContParams(tend, plist,
#                                           use_special,
#                                           self.algparams['verbose'],
#                                           True, deepcopy(self._inputVarList),
#                                           deepcopy(self._inputTimeList))
        else:
            if self._solver.numRuns > 0:
                self._solver.clearAll()
            x0 = sortedDictValues(self.initialconditions, self.funcspec.vars)
            self._solver.setInteg(maxpts=self.algparams['max_pts'],
                rtol=self.algparams['rtol'], atol=self.algparams['atol'])
            self._solver.setRunParams(ic=x0, params=plist,
                                  t0=tbegin, tend=tend, gt0=self.globalt0,
                                  refine=self.algparams['refine'],
                                  specTimes=self.algparams['specialtimes'],
                                  bounds=bounds)
        if self.inputs:
            # self._extInputsChanged if global t0 changed so that can
            # adjust times given to the integrator (it is blind to global t0
            # when accesses input variable times)
            self._ensure_inputs(self._extInputsChanged)
        # hinit only set if not continue_integ
        if len(anames)>0:
            check_aux = self.algparams['check_aux']
        else:
            check_aux = 0
        if self.algparams['max_step'] == 0:
            max_step = tend-tbegin
        else:
            max_step = self.algparams['max_step']
        init_step = self.algparams['init_step']
        if self._dircode == 1:
            if init_step < 0:
                init_step = -init_step
            if max_step < 0:
                max_step = -max_step
        else:
            if init_step > 0:
                init_step = -init_step
            if max_step > 0:
                max_step = -max_step
        if continue_integ:
            # record needed for bounds checking and truncation
            old_highest_ix = self._solver.points.shape[1]
            alltData, X, A, Stats, H, Err, Evtimes, \
                 Evpoints = self._solver.Continue(tend, plist,
                                  use_special, self.algparams['verbose'],
                                  self._extInputsChanged,
                                  deepcopy(self._inputVarList),
                                  deepcopy(self._inputTimeList),
                                  bounds)
        else:
            old_highest_ix = 0
            self._solver.setEvents(eventActive=self.algparams['eventActive'],
                eventTerm=self.algparams['eventTerm'],
                eventDir=self.algparams['eventDir'],
                eventDelay=self.algparams['eventDelay'],
                eventInt=self.algparams['eventInt'],
                eventTol=self.algparams['eventTol'],
                maxevtpts=self.algparams['maxevtpts'],
                maxbisect=self.algparams['maxbisect'])
            alltData, X, A, Stats, H, Err, Evtimes, \
                 Evpoints = self._solver.Run(init_step,
                                    max_step,
                                    check_aux,
                                    use_special,
                                    self.algparams['verbose'],
                                    self.algparams['fac1'],
                                    self.algparams['fac2'],
                                    self.algparams['safety'],
                                    self.algparams['beta'],
                                    self.algparams['checkBounds'],
                                    self.algparams['boundsCheckMaxSteps'],
                                    self.algparams['magBound'])
        self._extInputsChanged = False    # reset this now
        self.diagnostics.outputStats = {'last_step': H,
                            'last_time': self._solver.lastTime,
                            'last_point': self._solver.lastPoint,
                            'num_fcns': Stats[0],
                            'num_steps': Stats[1],
                            'num_accept': Stats[2],
                            'num_reject': Stats[3],
                            'errorStatus': Err
                            }
        if self._dircode == -1:
            # reverse the array object (no reverse method!)
            alltData = alltData[::-1]
            X = X[:,::-1]
            if anames != []:
                A = A[:,::-1]
        xnames = self._var_ixmap
        # Package up computed trajectory in Variable variables
        # Add external inputs warnings to self.diagnostics.warnings, if any
        # (not presently supported)
##        for f in inputVarList:
##            for winfo in f.diagnostics.warnings:
##                self.diagnostics.warnings.append((W_NONTERMSTATEBD,
##                                     (winfo[0], f.name, winfo[1],
##                                      f.depdomain)))
        eventslist = self.eventstruct.query(['lowlevel', 'active'])
        termevents = self.eventstruct.query(['term'], eventslist)
        if self._eventNames != []:
            # build self.diagnostics.warnings because events happened --
            # and keep a record of which times terminal events happened because
            # Model.py's event handling procedure assumes multiple events
            # happening at one time are listed in one warning
            termevtimes = {}
            nontermevtimes = {}
            try:
                for evix in range(len(self._eventNames)):
                    if Evpoints[evix] is None:
                        continue
                    evname = self._eventNames[evix]
                    numevs = len(Evtimes[evix])
                    if self.algparams['eventTerm'][evix]:
                        if numevs > 1:
                            print("Event info:%r, %r" % (Evpoints, Evtimes))
                        assert numevs <= 1, ("Internal error: more than one "
                                         "terminal event of same type found")
                        # For safety, we should assert that this event
                        # also appears in termevents, but we don't
                        if Evtimes[evix][0] in termevtimes.keys():
                            # append event name to this warning
                            warning_ix = termevtimes[Evtimes[evix][0]]
                            self.diagnostics.warnings[warning_ix][1][1].append(evname)
                        else:
                            # make new termevtime entry for the new warning
                            termevtimes[Evtimes[evix][0]] = len(self.diagnostics.warnings)
                            self.diagnostics.warnings.append((W_TERMEVENT,
                                             (Evtimes[evix][0],
                                             [self._eventNames[evix]])))
                    else:
                        for ev in range(numevs):
                            if Evtimes[evix][ev] in nontermevtimes.keys():
                                # append event name to this warning
                                warning_ix = nontermevtimes[Evtimes[evix][ev]]
                                self.diagnostics.warnings[warning_ix][1][1].append(evname)
                            else:
                                # make new nontermevtime entry for the new warning
                                nontermevtimes[Evtimes[evix][ev]] = \
                                                            len(self.diagnostics.warnings)
                                self.diagnostics.warnings.append((W_NONTERMEVENT,
                                                 (Evtimes[evix][ev],
                                                  [evname])))
            except IndexError:
                print("Events returned from integrator are the wrong size.")
                print("  Did you change the system and not refresh the C " \
                      + "library using the forcelibrefresh() method?")
                raise
        termcount = 0
        for (w,i) in self.diagnostics.warnings:
            if w == W_TERMEVENT or w == W_TERMSTATEBD:
                if termcount > 0:
                    raise ValueError("Internal error: more than one terminal "
                                     "event found")
                termcount += 1
        # post-process check of variable bounds (if defined and algparams['checkBounds'] True)
        if self._dircode > 0:
            compare = operator.lt
            last_ix = Inf
        else:
            compare = operator.gt
            last_ix = -Inf
        highest_ix = X.shape[1]-1
        last_t = Inf
        if self.algparams['checkBounds'] > 0:
            # temp storage for repeatedly used object attributes (for lookup efficiency)
            depdomains = dict(zip(range(self.dimension),
                                  [self.variables[xn].depdomain for xn in xnames]))
            offender_ix = None
            for xi in range(self.dimension):
                if not any(depdomains[xi].isfinite()):
                    # no point in checking when the bounds are +/- infinity
                    continue
                next_last_ix = array_bounds_check(X[xi][old_highest_ix:],
                                    depdomains[xi], self._dircode) + old_highest_ix
                if compare(next_last_ix, last_ix):
                    # won't count as truncating unless the following checks
                    # hold
                    last_ix = next_last_ix
                    offender_ix = xi
            if not isfinite(last_ix) and last_ix < 0:
                # only use +Inf hereon to flag no truncation needed
                last_ix = Inf
            elif last_ix >= 0 and last_ix < highest_ix:
                # truncate data
                last_t = alltData[last_ix]
                print("Warning; domain bound reached (because algparams['checkBounds'] > 0)")
                self.diagnostics.warnings.append((W_TERMSTATEBD,
                                    (last_t, xnames[offender_ix],
                                     X[offender_ix, last_ix],
                                     depdomains[offender_ix].get())))
        # Create variables (self.variables contains no actual data)
        variables = copyVarDict(self.variables)
        # build event pointset information (reset previous trajectory's)
        # don't include events after any truncation due to state bound violation
        self.trajevents = {}
        for evix in range(len(self._eventNames)):
            evname = self._eventNames[evix]
            if Evpoints[evix] is None:
                self.trajevents[evname] = None
            else:
                try:
                    ev_a_list = []
                    for t in Evtimes[evix]:
                        tix = find(alltData, t)
                        ev_a_list.append(A[:,tix])
                    ev_array = concatenate((Evpoints[evix],
                                         transpose(array(ev_a_list, 'd'))))
                    del ev_a_list, tix
                except TypeError:
                    # A is empty
                    ev_array = Evpoints[evix]
                if last_ix >= 0 and last_ix < highest_ix:
                    # don't count last_ix = -1 which is the same as highest_ix
                    last_ev_tix = npy.argmax(Evtimes[evix] >= alltData[last_ix])
                    if last_ev_tix == 0 and Evtimes[evix][0] >= last_t:
                        # checks that there was actually a violation
                        # - so no events to record
                        self.trajevents[evname] = None
                    else:
                        # truncation needed
                        ev_array = ev_array[:, :last_ev_tix+1]
                        ev_times = Evtimes[evix][:last_ev_tix+1]
                        self.trajevents[evname] = Pointset({'coordnames': xnames+anames,
                                               'indepvarname': 't',
                                               'coordarray': ev_array,
                                               'indepvararray': ev_times})
                else:
                    # no truncation needed
                    self.trajevents[evname] = Pointset({'coordnames': xnames+anames,
                                               'indepvarname': 't',
                                               'coordarray': ev_array,
                                               'indepvararray': Evtimes[evix]})
        if last_ix >= 0 and last_ix < highest_ix:
            # truncate
            X = X[:, :last_ix]
            alltData = alltData[:last_ix]
        try:
            allxDataDict = dict(zip(xnames,X))
        except IndexError:
            print("Integration returned variable values of unexpected dimensions.")
            print("  Did you change the system and not refresh the C library" \
                  + " using the forcelibrefresh() method?")
            raise
        # storage of all auxiliary variable data
        try:
            if anames != []:
                if last_ix < highest_ix:
                    A = A[:, :last_ix]
                try:
                    allaDataDict = dict(zip(anames,A))
                except TypeError:
                    print("Internal error!  Type of A: %s" % type(A))
                    raise
        except IndexError:
            print("Integration returned auxiliary values of unexpected dimensions.")
            print("  Did you change the system and not refresh the C library" \
                  + " using the forcelibrefresh() method?")
            raise
        if int(Err) == 1 or (int(Err) == 2 and termcount == 1):
            # output OK
            if self.algparams['poly_interp']:
                rhsfn = self._solver.Rhs
                # when Dopri can output the Rhs values alongside variable
                # values then this won't be necessary
                dxvals = zeros((len(alltData),self.dimension),float)
                for tix, tval in enumerate(alltData):
                    # solver's Rhs function already contains the inputs so no
                    # need to recompute and provide here.
                    #i = _pollInputs(sortedDictValues(self.inputs), tval,
                    #                        self.checklevel)
                    # X is the output variable array, but rhsfn demands a list
                    dxvals[tix] = rhsfn(tval, list(X[:,tix]), plist)[0]
            for xi, x in enumerate(xnames):
                if len(alltData) > 1:
                    if self.algparams['poly_interp']:
                        interp = PiecewisePolynomial(alltData,
                                    array([allxDataDict[x], dxvals[:,xi]]).T, 2)
                    else:
                        interp = interp1d(alltData, allxDataDict[x])
                    variables[x] = Variable(interp, 't', x, x)
                else:
                    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:
                    raise PyDSTool_ValueError("Fewer than 2 data points "
                                              "computed")
            # final checks
            #self.validateSpec()
            self.defined = True
            return Trajectory(trajname, list(variables.values()),
                              abseps=self._abseps, globalt0=self.globalt0,
                              checklevel=self.checklevel,
                              FScompatibleNames=self._FScompatibleNames,
                              FScompatibleNamesInv=self._FScompatibleNamesInv,
                              modelNames=self.name, events=self.trajevents,
                              modelEventStructs=self.eventstruct)
        else:
            try:
                diagnost_info = self.diagnostics._errorcodes[int(Err)]
            except TypeError:
                # errcode messed up from Dopri
                print("Error code: %d" % Err)
                diagnost_info = self.diagnostics._errorcodes[0]
            if self._solver.verbose:
                info(self.diagnostics.outputStats, "Output statistics")
            self.defined = False
            # Did the solver run out of memory?
            if (len(alltData) == self.algparams['max_pts'] or \
                self.diagnostics.outputStats['num_steps'] >= self.algparams['max_pts']) \
                   and alltData[-1] < tend:
                print("max_pts algorithmic parameter too small: current " + \
                      "value is %i"%self.algparams['max_pts'])
#                avstep = (self.algparams['init_step']+self.diagnostics.outputStats['last_step'])/2.
                if self.diagnostics.outputStats['last_time']-tbegin > 0:
                    ms = str(int(round(self.algparams['max_pts'] / \
                              (self.diagnostics.outputStats['last_time'] - \
                               tbegin)*(tend-tbegin))))
                else:
                    ms = 'Inf'
                print("(recommended value for this trajectory segment is " + \
                      "estimated to be %s (saved in diagnostics.errors attribute))"%str(ms))
                diagnost_info += " -- recommended value is " + ms
            self.diagnostics.errors.append((E_COMPUTFAIL,
                                    (self._solver.lastTime, diagnost_info)))
            raise PyDSTool_ExistError("No trajectory created")
Example #15
0
    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: %r" % 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 %d) was %r" % (len(avals), avals))
                print("Index out of range was %d" % aix)
                print(self.funcspec.auxspec[1])
                print(hasattr(self, self.funcspec.auxspec[1]))
                print("Args were: %r" % [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 = [e for e in termevents if e in evsflagged]
                nontermevsflagged = [e for e in evsflagged if e not in termevsflagged]
                # 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 range(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 = %f' % solver.t)
                    for f in inputVarList:
                        if f.diagnostics.hasWarnings():
                            print('External input variable %s out of range:'%f.name)
                            print('   t = %r, %s = %r' % (
                                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 = %f' % 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 =%f" % solver.t)
                    print("state =%r" % 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 =%f" % solver.t)
                print("x =%f" % 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:%s" % self.name)
                print("t = %r" % alltData)
                print("x = %r" % 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:%s" % self.name)
                print("t = %r" % alltData)
                print("x = %r" % 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.items():
                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, list(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: %d" % errcode)
                self.diagnostics.errors.append((E_COMPUTFAIL, (solver.t,
                                    self.diagnostics._errorcodes[0])))
            self.defined = False
Example #16
0
    def __init__(self, kw):
        """Use the nobuild key to postpone building of the library, e.g. in
        order to provide additional build options to makeLibSource and
        compileLib methods or to make changes to the C code by hand.
        No build options can be specified otherwise."""

        # Building is just doing make
        if 'nobuild' in kw:
            nobuild = kw['nobuild']
            del kw['nobuild']
        else:
            nobuild = False
        ODEsystem.__init__(self, kw)
        self._solver = None
        assert self.funcspec.targetlang == 'matlab', \
               ('Wrong target language for functional specification. '
                'matlab needed for this class')
        assert isinstance(
            self.funcspec,
            RHSfuncSpec), ('ADMC++ '
                           'requires RHSfuncSpec type to proceed')
        assert not self.inputs, \
                        'ADMC++ does not support external inputs feature'
        self._errorcodes = {}
        self._paraminfo = {}

        self.vftype = 'vfieldts'

        # currently the final four of these params are for event handling
        # NEED TO CHECK WHICH ONES ARE SUPPORTED BY ADMC -- LOOKS LIKE EVTOLS ONLY FOR NOW
        # HACK: vftype is alg param for now, tells us whether parent class is hybridvf, vfieldts, etc.
        algparams_def = {'evtols': 0.0001, 'vftype': 'vfieldts'}

        # Remove this later
        for k, v in algparams_def.items():
            if k not in self.algparams:
                self.algparams[k] = v

        # verify that no additional keys are present in algparams, after
        # defaults are added above
        if len(self.algparams) != len(algparams_def):
            raise ValueError("Invalid keys present in algparams argument: " \
                     + str(remain(self.algparams.keys(),algparams_def.keys())))

        thisplatform = platform.system()

        self._compilation_tempdir = os.path.join(os.getcwd(), "admcpp_temp")
        if not os.path.isdir(self._compilation_tempdir):
            try:
                assert not os.path.isfile(self._compilation_tempdir), \
                     "A file already exists with the same name"
                os.mkdir(self._compilation_tempdir)
            except:
                print("Could not create compilation temp directory " + \
                      self._compilation_tempdir)
                raise

        # ADMC targets must go in their own directories with appropriate names
        self._model_dir = "@" + self.name
        self._target_dir = os.path.join(self._compilation_tempdir,
                                        self._model_dir)
        # Make the target directory
        if not os.path.isdir(self._target_dir):
            try:
                assert not os.path.isfile(self._target_dir), \
                       "A file already exists with the same name"
                os.mkdir(self._target_dir)
            except:
                print("Could not creat target ADMC model directory " + \
                      self._target_dir)
                raise
        """ An ADMC model has the following files:
        vfield.m -- contains code for the RHS of the vector field
        set.m -- a generic method that overload matlab's set method; only need to insert vfield name
        get.m -- a generic method that overloads matlab's get method; only need to insert appropriate parent name
        """

        # model.m, get.m, set.m, vfield.m are minimal files required. TO DO: EVENTS
        self._model_file = self.name + ".m"
        self._ic_file = self.name + "_ics.m"
        self._param_file = self.name + "_params.m"
        self._set_file = "set.m"
        self._get_file = "get.m"
        self._vfield_file = "vfield.m"
        self._events_file = "events.m"

        self._vf_filename_ext = "_" + self._model_file[:-2]

        if not nobuild:
            self.makeLibSource()
        else:
            print("Build the library using the makeLib method, or in ")
            print("stages using the makeLibSource and compileLib methods.")
Example #17
0
    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 range(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: %f" % 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 %d) was %r" % (len(avals), avals))
                print("Index out of range was %d" % aix)
                print(self.funcspec.auxspec[1])
                print(hasattr(self, self.funcspec.auxspec[1]))
                print("Args were:%r" % [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
            ## Removed this "belt and braces" pre-check because it messes
            ## up some events in the middle of hybrid regime switches.
            ## Not sure yet what the problem is.  (Oct 2013)
            #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 = [e for e in termevents if e in evsflagged]
                nontermevsflagged = [e for e in evsflagged if e not in termevsflagged]
                # 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 range(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 range(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 = %f' % solver.t)
                    for f in inputVarList:
                        if f.diagnostics.hasWarnings():
                            print('External input variable %s out of range:'%f.name)
                            print('   t = %r, %s, %r' % (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 = %f' % 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 =%f" % solver.t)
                    print("state =%r" % 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 =%f" % solver.t)
                print("x =%f" % 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:%s" % self.name)
                print("t = %r" % alltData)
                print("x = %r" % 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:%s" % self.name)
                print("t = %r" % alltData)
                print("x = %r" % 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", end=' ')
                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 = list(ntpe_tdict.keys())
            evts.sort()
            for evix in range(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.items():
                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, list(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: %d" % errcode)
                self.diagnostics.errors.append((E_COMPUTFAIL, (solver.t,
                                    self.diagnostics._errorcodes[0])))
            self.defined = False
Example #18
0
    def __init__(self, kw):
        """Use the nobuild key to postpone building of the library, e.g. in
        order to provide additional build options to makeLibSource and
        compileLib methods or to make changes to the C code by hand.
        No build options can be specified otherwise."""

        # delete because not covered in ODEsystem
        nobuild = kw.pop('nobuild', False)
        ODEsystem.__init__(self, kw)
        self.diagnostics.outputStatsInfo = {
            'last_step': 'Predicted step size of the last accepted step (useful for a subsequent call to dop853).',
            'num_steps': 'Number of used steps.',
            'num_accept': 'Number of accepted steps.',
            'num_reject': 'Number of rejected steps.',
            'num_fcns': 'Number of function calls.',
            'errorStatus': 'Error status on completion.'
                        }
        self.diagnostics._errorcodes = {
             0 : 'Unrecognized error code returned (see stderr output)',
            -1 : 'input is not consistent',
            -2 : 'larger nmax is needed',
            2 : 'larger nmax or maxevtpts is probably needed (error raised by solout)',
            -3 : 'step size becomes too small',
            -4 : 'the problem is probably stiff (interrupted)',
            -8 : 'The solution exceeded a magbound (poor choice of initial step)'}
        self._solver = None
        algparams_def = {'poly_interp': False,
                        'init_step': 0,
                        'max_step': 0,
                        'rtol': [1e-9 for i in range(self.dimension)],
                        'atol': [1e-12 for i in range(self.dimension)],
                        'fac1': 0.2,
                        'fac2': 10.0,
                        'safety': 0.9,
                        'beta': 0.04,
                        'max_pts': 10000,
                        'refine': 0,
                        'maxbisect': [], # for events
                        'maxevtpts': 1000, # for events
                        'eventInt': [],  # set using setEventInterval only
                        'eventDelay': [], # set using setEventDelay only
                        'eventTol': [], # set using setEventTol only
                        'use_special': 0,
                        'specialtimes': [],
                        'check_aux': 1,
                        'extraspace': 100,
                        'verbose': 0,
                        'hasJac': 0,
                        'hasJacP': 0,
                        'magBound': 1e7,
                        'boundsCheckMaxSteps': 1000,
                        'checkBounds': self.checklevel
                        }
        for k, v in algparams_def.items():
            if k not in self.algparams:
                self.algparams[k] = v
        # verify that no additional keys are present in algparams, after
        # defaults are added above
        if len(self.algparams) != len(algparams_def):
            raise ValueError("Invalid keys present in algparams argument: " \
                     + str(remain(self.algparams.keys(),algparams_def.keys())))

        if self.haveMass():
            raise ValueError("Mass matrix declaration is incompatible "
                             "with Dopri853 integrator system specification")

        self._prepareEventSpecs()
        self._inputVarList = []
        self._inputTimeList = []

        if nobuild:
            print("Build the library using the makeLib method, or in ")
            print("stages using the makeLibSource and compileLib methods.")
        else:
            self.makeLib()
Example #19
0
    def compute(self, trajname, dirn='f'):
        continue_integ = ODEsystem.prepDirection(self, dirn)
        if self._dircode == -1:
            raise NotImplementedError, (
                'Backwards integration is not implemented')
        # validate spec if there exists a prior trajectory computation
        if self.defined:
            self.validateSpec()
            self.validateICs()
            self.clearWarnings()
            self.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
        if self.haveJacobian():
            haveJac = 1
        else:
            haveJac = 0
        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 = self.indepvariable.depdomain.get(0)
        indepdom1 = self.indepvariable.depdomain.get(1)
        if continue_integ:
            if self._tdata[0] != 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
        else:
            x0 = sortedDictValues(self.initialconditions, self.funcspec.vars)
        if self._solver._integrator is None:
            # Banded Jacobians not yet supported
            #
            # start a new integrator, because method may have been
            # switched
            self._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=self._algparams['init_step'],
                with_jacobian=haveJac)
            # speed up repeated access to solver by making a temp name for it
            solver = self._solver
        else:
            # speed up repeated access to solver by making a temp name for it
            solver = self._solver
            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 = self._algparams['init_step']
        solver.set_initial_value(x0, indepdom0)
        ##            if self._dircode == 1:
        ##                solver.set_initial_value(x0, indepdom0)
        ##            else:
        ##                solver.set_initial_value(x0, indepdom1)
        # wrap up each dictionary initial value as a singleton list
        alltData = [indepdom0]
        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)
        dt = self._algparams['init_step']
        strict = self._algparams['strictdt']
        # Make t mesh
        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
            tmesh = [indepdom0, indepdom1]
        else:
            notDone = True
            repeatTol = 10
            count = 0
            while notDone and count <= repeatTol:
                try:
                    tmesh = self.indepvariable.depdomain.uniformSample(
                        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")
            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
                dt = tmesh[2] - tmesh[1]
        if self.eventstruct.query(['lowlevel']) != []:
            raise ValueError, "Only high level events can be passed to VODE"
        eventslist = self.eventstruct.query(
            ['highlevel', '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
        # per-iteration storage of variable data (initial values are irrelevant)
        xDataDict = {}
        xnames = self.funcspec.vars
        # storage of all auxiliary variable data
        allaDataDict = {}
        anames = self.funcspec.auxvars
        avals = apply(getattr(self, self.funcspec.auxspec[1]),
                      [indepdom0, 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:", [indepdom0, x0, extralist]
                raise
        # Initialize signs of event detection objects at IC
        dataDict = copy(self.initialconditions)
        dataDict.update(dict(zip(anames, avals)))
        dataDict['t'] = indepdom0
        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 != []:
            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(indepdom0, eventslist,
                                                      'prev')
            else:
                self.eventstruct.resetHighLevelEvents(indepdom0,
                                                      eventslist)  #, 'off')
                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 = {}
        for xi in xrange(self.dimension):
            depdomains[xi] = self.variables[xnames[xi]].depdomain
        # 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:
                break
            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
            for xi in xrange(self.dimension):
                xDataDict[xnames[xi]] = solver.y[xi]
                if not self.contains(depdomains[xi], solver.y[xi],
                                     self.checklevel):
                    self.warnings.append(
                        (W_TERMSTATEBD, (solver.t, xnames[xi], solver.y[xi],
                                         depdomains[xi].get())))
                    breakwhile = True
                    break  # for loop
            if breakwhile:
                break
            avals = apply(
                getattr(self, self.funcspec.auxspec[1]),
                [new_t, sortedDictValues(xDataDict), extralist])
            # Uncomment the following assertion for debugging
            #            assert all([isfinite(a) for a in avals]), \
            #               "Some auxiliary variable values not finite"
            aDataDict = dict(zip(anames, avals))
            if eventslist != []:
                dataDict = copy(xDataDict)
                dataDict.update(aDataDict)
                dataDict['t'] = new_t
                if self.inputs:
                    parsinps = copy(self.pars)
                    parsinps.update(
                        dict(
                            zip(
                                inames,
                                ##                                    extralist[self.numpars:listend])))
                                _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
                    nonprec_evnames = remain(evnames, prec_evnames)
                    # only record events if they have not been previously
                    # flagged within their event interval
                    if nonprec_evnames != []:
                        temp_names = []
                        for evname in nonprec_evnames:
                            prevevt_time = lastevtime[evname]
                            if prevevt_time is None:
                                ignore_ev = False
                            else:
                                if solver.t - prevevt_time < e[1].eventinterval:
                                    ignore_ev = True
                                else:
                                    ignore_ev = False
                            if not ignore_ev:
                                temp_names.append(evname)
                                lastevtime[evname] = solver.t
                        self.warnings.append(
                            (W_NONTERMEVENT, (solver.t, temp_names)))
                        for evname in temp_names:
                            Evtimes[evname].append(solver.t)
                            Evpoints[evname].append(solver.y)
                    for e in precEvts:
                        # only record events if they have not been previously
                        # flagged within their event interval
                        prevevt_time = lastevtime[e[0]]
                        if prevevt_time is None:
                            ignore_ev = False
                        else:
                            if solver.t - dt - prevevt_time < e[
                                    1].eventinterval:
                                ignore_ev = True
                            else:
                                ignore_ev = False
                        if not ignore_ev:
                            nontermprecevs.append((solver.t - dt, solver.t, e))
                            # be conservative as to where the event is, so
                            # that don't miss any events.
                            lastevtime[e[0]] = solver.t - dt
                        e[1].reset()  #e[1].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 solver.t - dt - 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.warnings.append((W_TERMEVENT, \
                                             (solver.t, evnames)))
                        for evname in evnames:
                            Evtimes[evname].append(solver.t)
                            Evpoints[evname].append(solver.y)
                        breakwhile = True  # break while loop after appending t, x
                    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.warnings.append(
                                    (W_BISECTLIMIT, (solver.t, evnames)))
                                breakwhile = True
                        # find minimum eventtol in precEvts
                        dt_min = min([e[1].eventtol for e in precEvts])
                        # 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 = solver.t - dt
                        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))
                        if dt_min >= dt:
                            ##                            print "Registering event:", dt_min, dt
                            # register the event in the warnings
                            self.warnings.append(
                                (W_TERMEVENT, (solver.t, evnames)))
                            for evname in evnames:
                                Evtimes[evname].append(solver.t)
                                Evpoints[evname].append(solver.y)
                            # 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.
                            breakwhile = True  # while loop
                        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)
                            ]
                            # 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] = \
                                    [apply(f, [told+self.globalt0,
                                               self.checklevel]) \
                                                  for f in inputVarList]
                            solver.set_f_params(extralist)
                            # continue integrating over new mesh
                            continue  # while
            alltData.append(solver.t)
            for xi in xrange(self.dimension):
                allxDataDict[xnames[xi]].append(solver.y[xi])
            for aix in xrange(len(anames)):
                aname = anames[aix]
                allaDataDict[aname].append(avals[aix])
            num_points += 1
            if not breakwhile:
                try:
                    extralist[self.numpars:listend] = [apply(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 len(f.warnings):
                            print 'External input variable %s out of range:' % f.name
                            print '   t = ', repr(f.warnings[-1][0]), ', ', \
                                  f.name, ' = ', repr(f.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.warnings[-1][0] not in [W_TERMEVENT, W_TERMSTATEBD]:
                    raise RuntimeError("Event finding code for terminal event "
                                       "failed in Generator " + self.name + \
                                       ": try decreasing eventdelay or "
                                       "eventinterval below eventtol")
            except IndexError:
                print "Output stats: ", self.outputstats
                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.warnings, if any
        for f in inputVarList:
            for winfo in f.warnings:
                self.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.warnings:
            if w == W_TERMEVENT or w == W_TERMSTATEBD:
                termcount += 1
                if termcount > 1:
                    self.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:
                variables[x] = Variable(interp1d(alltData, allxDataDict[x]),
                                        '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
            search_dt = max((et1 - et0) / 5, e[1].eventinterval)
            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)
            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:
                raise PyDSTool_ExistError, \
                      ("Internal error: A non-terminal, 'precise' event '"
                       +e[0]+"' was lost after integration!")
        # 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.warnings.append((W_NONTERMEVENT, (evt, evnames)))
                xval = [variables[x](evt) for x in xnames]
                for evname in evnames:
                    Evtimes[evname].append(evt)
                    Evpoints[evname].append(array(xval))
                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)
                    xvaldict = dict(zip(xnames, xval))
                    for x in xnames:
                        allxDataDict[x].insert(tix, xvaldict[x])
                    for a in anames:
                        allaDataDict[a].insert(tix, variables[a](evt))
            for x in xnames:
                variables[x] = Variable(interp1d(alltData, allxDataDict[x]),
                                        't', x, x)
            for a in anames:
                variables[a] = Variable(interp1d(alltData, allaDataDict[a]),
                                        't', a, a)
        self.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,
                        'indepvarname':
                        't',
                        'coordarray':
                        evpt,
                        'indepvararray':
                        Evtimes[evname],
                        'indepvartype':
                        Float
                    })
            self.defined = True
            return Trajectory(trajname, variables.values(), self.globalt0,
                              self.checklevel)
        else:
            try:
                self.errors.append(
                    (E_COMPUTFAIL, (solver.t, self._errorcodes[errcode])))
            except TypeError:
                # e.g. when errcode has been used to return info list
                print "Error information: ", errcode
                self.errors.append(
                    (E_COMPUTFAIL, (solver.t, self._errorcodes[0])))
            self.defined = False
Example #20
0
 def __del__(self):
     ODEsystem.__del__(self)
    def compute(self, trajname, dirn='f'):
        continue_integ = ODEsystem.prepDirection(self, dirn)
        if self._dircode == -1:
            raise NotImplementedError, ('Backwards integration is not implemented')
        # validate spec if there exists a prior trajectory computation
        if self.defined:
            self.validateSpec()
            self.validateICs()
            self.clearWarnings()
            self.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
        if self.haveJacobian():
            haveJac = 1
        else:
            haveJac = 0
        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 = self.indepvariable.depdomain.get(0)
        indepdom1 = self.indepvariable.depdomain.get(1)
        if continue_integ:
            if self._tdata[0] != 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
        else:
            x0 = sortedDictValues(self.initialconditions,
                                        self.funcspec.vars)
        if self._solver._integrator is None:
            # Banded Jacobians not yet supported
            #
            # start a new integrator, because method may have been
            # switched
            self._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=self._algparams['init_step'],
                                 with_jacobian=haveJac)
            # speed up repeated access to solver by making a temp name for it
            solver = self._solver
        else:
            # speed up repeated access to solver by making a temp name for it
            solver = self._solver
            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 = self._algparams['init_step']
        solver.set_initial_value(x0, indepdom0)
##            if self._dircode == 1:
##                solver.set_initial_value(x0, indepdom0)
##            else:
##                solver.set_initial_value(x0, indepdom1)
        # wrap up each dictionary initial value as a singleton list
        alltData = [indepdom0]
        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)
        dt = self._algparams['init_step']
        strict = self._algparams['strictdt']
        # Make t mesh
        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
            tmesh = [indepdom0, indepdom1]
        else:
            notDone = True
            repeatTol = 10
            count = 0
            while notDone and count <= repeatTol:
                try:
                    tmesh = self.indepvariable.depdomain.uniformSample(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")
            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
                dt = tmesh[2]-tmesh[1]
        if self.eventstruct.query(['lowlevel']) != []:
            raise ValueError, "Only high level events can be passed to VODE"
        eventslist = self.eventstruct.query(['highlevel', '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
        # per-iteration storage of variable data (initial values are irrelevant)
        xDataDict = {}
        xnames = self.funcspec.vars
        # storage of all auxiliary variable data
        allaDataDict = {}
        anames = self.funcspec.auxvars
        avals = apply(getattr(self,self.funcspec.auxspec[1]),
                      [indepdom0, 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:", [indepdom0, x0, extralist]
                raise
        # Initialize signs of event detection objects at IC
        dataDict = copy(self.initialconditions)
        dataDict.update(dict(zip(anames, avals)))
        dataDict['t'] = indepdom0
        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 != []:
            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(indepdom0, eventslist, 'prev')
            else:
                self.eventstruct.resetHighLevelEvents(indepdom0, eventslist) #, 'off')
                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 = {}
        for xi in xrange(self.dimension):
            depdomains[xi] = self.variables[xnames[xi]].depdomain
        # 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:
                break
            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
            for xi in xrange(self.dimension):
                xDataDict[xnames[xi]] = solver.y[xi]
                if not self.contains(depdomains[xi],
                                 solver.y[xi],
                                 self.checklevel):
                    self.warnings.append((W_TERMSTATEBD,
                                (solver.t,
                                 xnames[xi],solver.y[xi],
                                 depdomains[xi].get())))
                    breakwhile = True
                    break  # for loop
            if breakwhile:
                break
            avals = apply(getattr(self,self.funcspec.auxspec[1]), [new_t,
                            sortedDictValues(xDataDict),
                            extralist])
            # Uncomment the following assertion for debugging
#            assert all([isfinite(a) for a in avals]), \
#               "Some auxiliary variable values not finite"
            aDataDict = dict(zip(anames,avals))
            if eventslist != []:
                dataDict = copy(xDataDict)
                dataDict.update(aDataDict)
                dataDict['t'] = new_t
                if self.inputs:
                    parsinps = copy(self.pars)
                    parsinps.update(dict(zip(inames,
##                                    extralist[self.numpars:listend])))
                              _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
                    nonprec_evnames = remain(evnames, prec_evnames)
                    # only record events if they have not been previously
                    # flagged within their event interval
                    if nonprec_evnames != []:
                        temp_names = []
                        for evname in nonprec_evnames:
                            prevevt_time = lastevtime[evname]
                            if prevevt_time is None:
                                ignore_ev = False
                            else:
                                if solver.t-prevevt_time < e[1].eventinterval:
                                    ignore_ev = True
                                else:
                                    ignore_ev = False
                            if not ignore_ev:
                                temp_names.append(evname)
                                lastevtime[evname] = solver.t
                        self.warnings.append((W_NONTERMEVENT,
                                     (solver.t, temp_names)))
                        for evname in temp_names:
                            Evtimes[evname].append(solver.t)
                            Evpoints[evname].append(solver.y)
                    for e in precEvts:
                        # only record events if they have not been previously
                        # flagged within their event interval
                        prevevt_time = lastevtime[e[0]]
                        if prevevt_time is None:
                            ignore_ev = False
                        else:
                            if solver.t-dt-prevevt_time < e[1].eventinterval:
                                ignore_ev = True
                            else:
                                ignore_ev = False
                        if not ignore_ev:
                            nontermprecevs.append((solver.t-dt, solver.t,e))
                            # be conservative as to where the event is, so
                            # that don't miss any events.
                            lastevtime[e[0]] = solver.t-dt
                        e[1].reset() #e[1].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 solver.t-dt-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.warnings.append((W_TERMEVENT, \
                                             (solver.t, evnames)))
                        for evname in evnames:
                            Evtimes[evname].append(solver.t)
                            Evpoints[evname].append(solver.y)
                        breakwhile = True  # break while loop after appending t, x
                    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.warnings.append((W_BISECTLIMIT,
                                                      (solver.t, evnames)))
                                breakwhile = True
                        # find minimum eventtol in precEvts
                        dt_min = min([e[1].eventtol for e in precEvts])
                        # 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 = solver.t-dt
                        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))
                        if dt_min >= dt:
##                            print "Registering event:", dt_min, dt
                            # register the event in the warnings
                            self.warnings.append((W_TERMEVENT,
                                             (solver.t, evnames)))
                            for evname in evnames:
                                Evtimes[evname].append(solver.t)
                                Evpoints[evname].append(solver.y)
                            # 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.
                            breakwhile = True  # while loop
                        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)]
                            # 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] = \
                                    [apply(f, [told+self.globalt0,
                                               self.checklevel]) \
                                                  for f in inputVarList]
                            solver.set_f_params(extralist)
                            # continue integrating over new mesh
                            continue  # while
            alltData.append(solver.t)
            for xi in xrange(self.dimension):
                allxDataDict[xnames[xi]].append(solver.y[xi])
            for aix in xrange(len(anames)):
                aname = anames[aix]
                allaDataDict[aname].append(avals[aix])
            num_points += 1
            if not breakwhile:
                try:
                    extralist[self.numpars:listend] = [apply(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 len(f.warnings):
                            print 'External input variable %s out of range:' % f.name
                            print '   t = ', repr(f.warnings[-1][0]), ', ', \
                                  f.name, ' = ', repr(f.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.warnings[-1][0] not in [W_TERMEVENT, W_TERMSTATEBD]:
                    raise RuntimeError("Event finding code for terminal event "
                                       "failed in Generator " + self.name + \
                                       ": try decreasing eventdelay or "
                                       "eventinterval below eventtol")
            except IndexError:
                print "Output stats: ", self.outputstats
                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.warnings, if any
        for f in inputVarList:
            for winfo in f.warnings:
                self.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.warnings:
            if w == W_TERMEVENT or w == W_TERMSTATEBD:
                termcount += 1
                if termcount > 1:
                    self.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:
                variables[x] = Variable(interp1d(alltData, allxDataDict[x]),
                                             '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
            search_dt = max((et1-et0)/5,e[1].eventinterval)
            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)
            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:
                raise PyDSTool_ExistError, \
                      ("Internal error: A non-terminal, 'precise' event '"
                       +e[0]+"' was lost after integration!")
        # 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.warnings.append((W_NONTERMEVENT, (evt, evnames)))
                xval = [variables[x](evt) for x in xnames]
                for evname in evnames:
                    Evtimes[evname].append(evt)
                    Evpoints[evname].append(array(xval))
                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)
                    xvaldict = dict(zip(xnames, xval))
                    for x in xnames:
                        allxDataDict[x].insert(tix, xvaldict[x])
                    for a in anames:
                        allaDataDict[a].insert(tix, variables[a](evt))
            for x in xnames:
                variables[x] = Variable(interp1d(alltData, allxDataDict[x]),
                                                 't', x, x)
            for a in anames:
                variables[a] = Variable(interp1d(alltData, allaDataDict[a]),
                                            't', a, a)
        self.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,
                                               'indepvarname': 't',
                                               'coordarray': evpt,
                                               'indepvararray': Evtimes[evname],
                                               'indepvartype': Float})
            self.defined = True
            return Trajectory(trajname, variables.values(),
                              self.globalt0, self.checklevel)
        else:
            try:
                self.errors.append((E_COMPUTFAIL, (solver.t,
                                              self._errorcodes[errcode])))
            except TypeError:
                # e.g. when errcode has been used to return info list
                print "Error information: ", errcode
                self.errors.append((E_COMPUTFAIL, (solver.t,
                                              self._errorcodes[0])))
            self.defined = False