def test_odemass_vec_nomass(self):
     opts = {}
     massType, massM, massFcn = odemass(self.t0,self.y0_vec,opts,[])
     self.assertEqual(massType,0)
     sparse = sp.eye(2)
     self.assertEqual((sparse - massM).nnz, 0)
     self.assertEqual(massFcn,None)
 def test_odemass_vec_function_state(self):
     opts = {'Mass':self.mass_state_vec,'MStateDependence':'weak'}
     massType, massM, massFcn = odemass(self.t0,self.y0_vec,opts,[])
     self.assertEqual(massType,3)
     array = np.array([[1,4],[2,3]])
     for i in range(len(array)):
         for j in range(len(array[0])):
             self.assertEqual(massM[i][j],array[i][j])
     self.assertEqual(massFcn,self.mass_state_vec)
def ode45(odefun, tspan, y0, options=None, varargin=None):
    '''Function to solve non-stiff differential equations using the dormund prince method
        with adaptive step. The system of differential equations takes the form y' = f(t,y)
        with y(0) = y_0 for t = {t_0, t_end}.
            
    Parameters
    ----------
    odefun : callable
        Callable function which represents the system of differential equations to be
        solved. The function must take the form y' = f(t,y), eg:
            
            def dydt(t,y):
                return [math.cos(t), y[1] * math.sin(t)]
                
        Where t must be a float and y must be an array_like of size n, where n is the
        size of the system. The function must return an array_like with the same size
        of size n.
            
    tspan : array_like, shape(2,) || shape(k,)
        This array represents the span over which odefun will be evaluated. It can either
        be an array of size 2 which represents [t_0, t_end], or it can either be a larger
        array which would represent a series of chosen points. If tspan is a series of 
        chosen points, then the function will only be evaluated at those points.
    
    y0 : array_like, shape(n,)
        This array are the initial values for the odefun function. It must be the same size
        as the array returned by the odefun.
    
    options : dictionary
        This dictionary contains the user options, the keys are represented by the option
        name, and the values are the value of the options. If a default value is shown, then
        this is the value the option will be set to automatically. The possible options are:
            
            AbsTol : float || array_like, shape(n,)    (default : 1e-6)
                Absolute error tolerance, can be a positive float or an array of positive
                floats.
                
            RelTol : float    (default : 1e-3)
                Relative error tolerance.
                
            NormControl : 'on' || 'off'    (default : 'off')
                If 'off' then error at each step:
                    error[i] <= max(RelTol * y[i], AbsTol[i])
                If 'on' then error at each step:
                    |error| <= max(RelTol * |y|, |AbsTol|)
                If NormControl is 'on' then AbsTol must be a float and not an array_like.
            
            Stats : 'on' || 'off'    (default : 'off')
                If 'on' then the function will print a series of stats about the execution.
            
            InitialStep : float
                Size of the initial step, must be a positive float.
                
            MaxStep : float    (default : 0.1 * abs(t_0 - t_end))
                Size of the maximun step, must be a positive float.
            
            Refine : integer    (default : 4)
                Determines the refinement to be performed at each step. If refine is set to 
                one, then no refinement will be performed.
                
            NonNegative : array_like    (default : [])
                List solutions of the differential system which will be kept positive. The
                list must contain only integers representing the indices of the solutions.
            
            Events : callable
                Function which must return value, isterminal, direction, all of which are 
                array_like of the same size. When the value of any of the values is 0 then
                an event is triggered. The isterminal determines whether the event should stop
                the execution and can only take value of 0 or 1. The direction determines from
                which direction the event should be triggered, if -1 then the event triggers
                if coming from the negative direction, whereas 1 will trigger if coming from 
                the positive direction, and 0 will trigger when coming from any direction. E.g :
                    
                    def events(t,y):
                        value = [10 - t, y[1]]
                        isterminal = [0, 1]
                        direction = [1, 0]
                        return value, isterminal, direction
                
            Mass : callable || array_like, shape(n,n)
                The mass option can either be a constant mass matrix, a time dependent function
                or a state-time dependent function.
                
                    Mass Matrix : array_like, shape(n,n)
                        Will solve for y s.t. M y' = f(t,y), M must be a square matrix.
                    
                    Time Dependent Function : callable
                        Will solve for y s.t. M(t) y' = f(t,y), M(t) must be a function in the
                        which takes t as argument an return an array_like(n,n)
                    
                    State-Time Dependent Function : callable
                        Will solve for y s.t. M(t,y) y' = f(t,y), M(t,y) must be a function in the
                        which takes t and y as argument an return an array_like(n,n)
            
            MStateDependence : 'none' || 'weak'    (default : 'none')
                Must be set to 'weak' if the Mass option is a state-time dependent function, otherwise
                it must be set to 'none'.
                    
    varargin : array_like, shape(t,)
        These are extra arguments that can be passed to the odefun. For example:
            
            def dydt(t,y,a,b):
                return [math.cos(t) + a, y[1] * math.sin(t) + b]
            
            varagin = [a,b]
        Note that these extra argument will also be passed to any events or mass function.
            

    Returns
    -------
    _ : odefinalize
        The function will return an object of type odefinalize (see odefinalize).
    
    '''

    solver_name = 'ode45'

    nsteps = 0
    nfailed = 0
    nfevals = 0

    if isinstance(options, type(None)):
        options = {}

    if isinstance(varargin, type(None)):
        varargin = []

    #Handle solver arguments
    neq, tspan, ntspan, nex, t0, tfinal, tdir, y0, f0, odeArgs, odeFcn, options, threshold, rtol, normcontrol, normy, hmax, htry, htspan, dataType = odearguments(
        odefun, tspan, y0, options, varargin)
    nfevals = nfevals + 1

    refine = max(1, odeget(options, 'Refine', 4))
    if len(tspan) > 2:
        outputAt = 'RequestedPoints'
    elif refine == 1:
        outputAt = 'SolverSteps'
    else:
        outputAt = 'RefinedSteps'
        s = np.array(range(1, refine)) / refine

    printstats = (odeget(options, 'Stats', 'off') == 'on')

    #Handle the event function
    haveEventFcn, eventFcn, eventArgs, valt, teout, yeout, ieout = odeevents(
        t0, y0, options, varargin)

    #Handle the mass matrix
    Mtype, M, Mfun = odemass(t0, y0, options, varargin)
    if Mtype > 0:
        odeFcn, odeArgs = odemassexplicit(Mtype, odeFcn, odeArgs, Mfun, M)
        f0 = feval(odeFcn, t0, y0, odeArgs)
        nfevals = nfevals + 1

    #Non-negative solution components
    idxNonNegative = odeget(options, 'NonNegative', [])
    nonNegative = False
    if len(idxNonNegative) != 0:
        odeFcn, thresholdNonNegative = odenonnegative(odeFcn, y0, threshold,
                                                      idxNonNegative)
        f0 = feval(odeFcn, t0, y0, odeArgs)
        nfevals = nfevals + 1
        nonNegative = True

    t = t0
    y = y0

    #Memory Allocation
    nout = 1
    yout = np.array([], dtype=dataType)
    tout = np.array([], dtype=dataType)
    if ntspan > 2:
        tout = np.zeros((1, ntspan), dtype=dataType)
        yout = np.zeros((neq, ntspan), dtype=dataType)
    else:
        chunk = min(max(100, 50 * refine),
                    refine + math.floor(math.pow(2, 11) / neq))
        tout = np.zeros((1, chunk), dtype=dataType)
        yout = np.zeros((neq, chunk), dtype=dataType)

    nout = 1
    tout[nout - 1] = t
    yout[:, nout - 1] = y.copy()

    #Initialize method parameters
    stop = 0
    power = 1 / 5
    A = np.array([1. / 5., 3. / 10., 4. / 5., 8. / 9., 1., 1.], dtype=dataType)
    B = np.array([[
        1. / 5., 3. / 40., 44. / 45., 19372. / 6561., 9017. / 3168., 35. / 384.
    ], [0., 9. / 40., -56. / 15., -25360. / 2187., -355. / 33., 0.
        ], [0., 0., 32. / 9., 64448. / 6561., 46732. / 5247., 500. / 1113.],
                  [0., 0., 0., -212. / 729., 49. / 176., 125. / 192.],
                  [0., 0., 0., 0., -5103. / 18656., -2187. / 6784.],
                  [0., 0., 0., 0., 0., 11. / 84.], [0., 0., 0., 0., 0., 0.]],
                 dtype=dataType)

    E = np.array([[71. / 57600.], [0.], [-71. / 16695.], [71. / 1920.],
                  [-17253. / 339200.], [22. / 525.], [-1. / 40.]],
                 dtype=dataType)
    f = np.zeros((neq, 7), dtype=dataType)
    hmin = 16 * np.spacing(float(t))
    np.set_printoptions(precision=16)

    #Initial step
    if htry == 0:
        absh = min(hmax, htspan)
        if normcontrol:
            rh = (np.linalg.norm(f0) /
                  max(normy, threshold)) / (0.8 * math.pow(rtol, power))
        else:
            if isinstance(threshold, list):
                rh = np.linalg.norm(f0 / np.maximum(np.abs(y), threshold),
                                    np.inf) / (0.8 * math.pow(rtol, power))
            else:
                rh = np.linalg.norm(
                    f0 / np.maximum(np.abs(y), np.repeat(threshold, len(y))),
                    np.inf) / (0.8 * math.pow(rtol, power))
        if (absh * rh) > 1:
            absh = 1 / rh
        absh = max(absh, hmin)
    else:
        absh = min(hmax, max(hmin, htry))

    f[:, 0] = f0

    ynew = np.zeros(neq, dtype=dataType)

    #Main loop
    done = False
    while not done:
        hmin = 16 * np.spacing(float(t))
        absh = min(hmax, max(hmin, absh))
        h = tdir * absh

        #If next step is within 10% of finish
        if 1.1 * absh >= abs(tfinal - t):
            h = tfinal - t
            absh = abs(h)
            done = True

        #Advancing one step
        nofailed = True
        while True:
            hA = h * A
            hB = h * B
            f[:, 1] = feval(odeFcn, t + hA[0], y + np.matmul(f, hB[:, 0]),
                            odeArgs)
            f[:, 2] = feval(odeFcn, t + hA[1], y + np.matmul(f, hB[:, 1]),
                            odeArgs)
            f[:, 3] = feval(odeFcn, t + hA[2], y + np.matmul(f, hB[:, 2]),
                            odeArgs)
            f[:, 4] = feval(odeFcn, t + hA[3], y + np.matmul(f, hB[:, 3]),
                            odeArgs)
            f[:, 5] = feval(odeFcn, t + hA[4], y + np.matmul(f, hB[:, 4]),
                            odeArgs)

            tnew = t + hA[5]
            if done:
                tnew = tfinal  #Hit end point exactly
            h = tnew - t

            ynew = y + np.matmul(f, hB[:, 5])
            f[:, 6] = feval(odeFcn, tnew, ynew, odeArgs)
            nfevals = nfevals + 6

            #Estimation of the error
            NNrejectStep = False
            if normcontrol:
                normynew = np.linalg.norm(ynew)
                errwt = max(max(normy, normynew), threshold)
                err = absh * np.linalg.norm(np.matmul(f, E)[:, 0]) / errwt
                if nonNegative and err <= rtol and any(
                    [True for i in idxNonNegative if ynew[i] < 0]):
                    errNN = np.linalg.norm(
                        [max(0, -1 * ynew[i]) for i in idxNonNegative]) / errwt
                    if errNN > rtol:
                        err = errNN
                        NNrejectStep = True
            else:
                denom = np.maximum(np.maximum(np.abs(y), np.abs(ynew)),
                                   threshold)
                err = absh * np.linalg.norm(
                    np.divide(np.matmul(f, E)[:, 0], denom), np.inf)
                if nonNegative and err <= rtol and any(
                    [True for i in idxNonNegative if ynew[i] < 0]):
                    errNN = np.linalg.norm(
                        np.divide(
                            [max(0, -1 * ynew[i]) for i in idxNonNegative],
                            thresholdNonNegative), np.inf)
                    if errNN > rtol:
                        err = errNN
                        NNrejectStep = True

            #Error is outside the tolerance
            if err > rtol:
                nfailed = nfailed + 1
                if absh <= hmin:
                    warnings.warn("ode45: ode45: IntegrationTolNotMet " +
                                  str(t) + " " + str(hmin))
                    return odefinalize(solver_name, printstats,
                                       [nsteps, nfailed, nfevals], nout, tout,
                                       yout, haveEventFcn, teout, yeout, ieout)

                if nofailed:
                    nofailed = False
                    if NNrejectStep:
                        absh = max(hmin, 0.5 * absh)
                    else:
                        absh = max(
                            hmin,
                            absh * max(0.1, 0.8 * math.pow(rtol / err, power)))
                else:
                    absh = max(hmin, 0.5 * absh)
                h = tdir * absh
                done = False
            else:
                #Successful step
                NNreset_f7 = False
                if nonNegative and any(
                    [True for i in idxNonNegative if ynew[i] < 0]):
                    for j in idxNonNegative:
                        ynew[j] = max(ynew[j], 0)

                    if normcontrol:
                        normynew = np.linalg.norm(ynew)
                    NNreset_f7 = True

                break

        nsteps += 1

        if haveEventFcn:
            te, ye, ie, valt, stop = odezero([], eventFcn, eventArgs, valt, t,
                                             np.transpose(np.array([y])), tnew,
                                             np.transpose(np.array([ynew])),
                                             t0, h, f, idxNonNegative)
            if len(te) != 0:
                if len(teout) == 0:
                    teout = np.copy(te)
                else:
                    teout = np.append(teout, te)

                if len(yeout) == 0:
                    yeout = np.copy(ye)
                else:
                    yeout = np.append(yeout, ye, axis=1)

                if len(ieout) == 0:
                    ieout = np.copy(ie)
                else:
                    ieout = np.append(ieout, ie)

                if stop:
                    #Terminal event
                    taux = t + (te[-1] - t) * A
                    _, f[:, 1:7] = ntrp45(taux, t, np.transpose(np.array([y])),
                                          h, f, idxNonNegative)
                    tnew = te[-1]
                    ynew = ye[:, -1]
                    h = tnew - t
                    done = True

        if outputAt == "SolverSteps":
            #Computed points, no refinement
            nout_new = 1
            tout_new = np.array([tnew])
            yout_new = np.transpose(np.array([ynew]))
        elif outputAt == "RefinedSteps":
            #Computed points, with refinement
            tref = t + (tnew - t) * s
            nout_new = refine
            tout_new = tref.copy()
            tout_new = np.append(tout_new, tnew)
            yout_new, _ = ntrp45(tref, t, np.transpose(np.array([y])), h, f,
                                 idxNonNegative)
            yout_new = np.append(yout_new,
                                 np.transpose(np.array([ynew])),
                                 axis=1)
        elif outputAt == "RequestedPoints":
            #Chosen points
            nout_new = 0
            tout_new = np.array([])
            yout_new = np.array([])
            while nex <= ntspan:
                if tdir * (tnew - tspan[nex - 1]) < 0:
                    if haveEventFcn and stop:
                        nout_new = nout_new + 1
                        tout_new = np.append(tout_new, tnew)
                        if len(yout_new) == 0:
                            yout_new = np.transpose(np.array([ynew]))
                        else:
                            yout_new = np.append(yout_new,
                                                 np.transpose(np.array([ynew
                                                                        ])),
                                                 axis=1)
                    break

                nout_new = nout_new + 1
                tout_new = np.append(tout_new, tspan[nex - 1])

                if tspan[nex - 1] == tnew:
                    yout_temp = np.transpose(np.array([ynew]))
                else:
                    yout_temp, _ = ntrp45(tspan[nex - 1], t, y, h, f,
                                          idxNonNegative)

                if len(yout_new) == 0:
                    yout_new = yout_temp
                else:
                    yout_new = np.append(yout_new, yout_temp, axis=1)
                nex = nex + 1

        #Extra memory allocation
        if nout_new > 0:
            oldnout = nout
            nout = nout + nout_new
            if nout > len(tout[0]):
                talloc = np.zeros((1, chunk), dtype=dataType)
                tout = np.array([np.append(tout, talloc)])
                yalloc = np.zeros((neq, chunk), dtype=dataType)
                yout = np.append(yout, yalloc, axis=1)
            for i in range(oldnout, nout):
                tout[0, i] = tout_new[i - oldnout]
                yout[:, i] = yout_new[:, i - oldnout]

        if done:
            break

        #No failures, compute new h
        if nofailed:
            temp = 1.25 * math.pow((err / rtol), power)
            if temp > 0.2:
                absh = absh / temp
            else:
                absh = 5.0 * absh

        #Advance the integration by one step
        t = tnew
        y = ynew.copy()

        if normcontrol:
            normy = normynew

        if NNreset_f7:
            f[:, 6] = feval(odeFcn, tnew, ynew, odeArgs)
            nfevals = nfevals + 1
        f[:, 0] = f[:, 6]

    return odefinalize(solver_name, printstats, [nsteps, nfailed, nfevals],
                       nout, tout, yout, haveEventFcn, teout, yeout, ieout)
def ode45_scipyStep(ode,tspan,y0,scipyStep,options = None, varargin = None) :
        
    solver_name = 'ode45'
    # Check inputs
    
    # Stats
    nsteps  = 0
    nfailed = 0
    nfevals = 0
      
    # Output
    FcnHandlesUsed  = isfunction(ode)
    if not FcnHandlesUsed :
        raise ValueError("ode is not an handle function")
    
    # Handle solver arguments
    neq, tspan, ntspan, NEXT, t0, tfinal, tdir, y0, f0, odeArgs, odeFcn, options, threshold, rtol, normcontrol, normy, hmax, htry, htspan, dataType = odearguments(FcnHandlesUsed, solver_name, ode, tspan, y0, options, varargin)
    nfevals = nfevals + 1

    #Handle the output
    
    refine = np.maximum(1,options.Refine)
    
    if ntspan > 2 :
        outputAt = 1 # output only at tspan points
    elif refine <= 1 :
        outputAt = 2 # computed points, no refinement
    else :
        outputAt = 3 # computed points, with refinement
        S = np.array(range(1,refine))/refine

    # Handle the event function
    haveEventFcn,eventFcn,eventArgs,valt,teout,yeout,ieout = odeevents(FcnHandlesUsed,odeFcn,t0,y0,options,varargin)
    
    # Handle the mass matrix
    Mtype, M, Mfun = odemass(FcnHandlesUsed,odeFcn,t0,y0,options,varargin)
    if Mtype > 0 :
        #check if matrix is singular and raise an arror
        
        # Incorporate the mass matrix into odeFcn and odeArgs.
        odeFcn,odeArgs = odemassexplicit(FcnHandlesUsed,Mtype,odeFcn,odeArgs,Mfun,M)
        f0 = feval(odeFcn,t0,y0,odeArgs)
        nfevals = nfevals + 1
            
    #Non-negative solution components
    idxNonNegative = options.NonNegative
    nonNegative =  len(idxNonNegative) != 0
    #thresholdNonNegative = np.abs(threshold)
    if nonNegative :
        odeFcn,thresholdNonNegative,odeArgs = odenonnegative(odeFcn,y0,threshold,idxNonNegative,odeArgs)
        #odeArgs = (argSup,odeArgs)
        f0 = feval(odeFcn,t0,y0,odeArgs)
        nfevals = nfevals + 1
    
    t = t0
    y = y0
    
    # Allocate memory if we're generating output.
    
    if outputAt == 1 :
        output_y = np.zeros((neq,ntspan),dtype=dataType)
        output_t = np.zeros(ntspan,dtype=dataType)
    else :
        output_y = np.zeros((neq,1),dtype=dataType)
        output_t = np.zeros(1,dtype=dataType)
        
    output_y[:,0] = y0
    output_t[0] = t0
    
    #Initialize method parameters.
    POW = 1/5
    A = np.array([1/5, 3/10, 4/5, 8/9, 1, 1],dtype=dataType)
    B = np.array([
    [1/5, 3/40, 44/45, 19372/6561, 9017/3168, 35/384],
    [0, 9/40, -56/15, -25360/2187, -355/33, 0],
    [0, 0, 32/9, 64448/6561, 46732/5247, 500/1113],
    [0, 0, 0, -212/729, 49/176, 125/192],
    [0, 0, 0, 0, -5103/18656, -2187/6784],
    [0, 0, 0, 0, 0, 11/84],
    [0, 0, 0, 0, 0, 0]
    ],dtype=dataType)
    
    E = np.array([71/57600, 0, -71/16695, 71/1920, -17253/339200, 22/525, -1/40],dtype=dataType)
    f = np.zeros((neq,7),dtype=dataType)
    hmin = 16*np.finfo(float(t)).eps
    
    if htry is None :  # Compute an initial step size h using y'(t).
        absh = np.minimum(hmax, htspan)
        if normcontrol :
            rh = (np.linalg.norm(f0) / np.maximum(normy,threshold)) / (0.8 * rtol**POW)
        else :
            rh = np.linalg.norm(f0 / np.maximum(np.abs(y0),threshold),np.inf) / (0.8 * rtol**POW)
        if absh * rh > 1 :
            absh = 1 / rh
        absh = np.maximum(absh, hmin)
    else :
        absh = np.minimum(hmax, np.maximum(hmin, htry))
    f[:,0] = f0
    #THE MAIN LOOP
    done = False
    stop = False
    
    plop = 0
    while not done :
        
        h = scipyStep[plop]
        plop = plop +1
        absh = h
        # Stretch the step if within 10% of tfinal-t.
        if plop >= len(scipyStep) :
            done = True
   
        # LOOP FOR ADVANCING ONE STEP.
        nofailed = True  # no failed attempts
        
        while True :
                     
            hA = h * A
            hB = h * B

            f[:,1] = feval(odeFcn,t+hA[0],y+np.dot(f,hB[:,0]),odeArgs)
            f[:,2] = feval(odeFcn,t+hA[1],y+np.dot(f,hB[:,1]),odeArgs)
            f[:,3] = feval(odeFcn,t+hA[2],y+np.dot(f,hB[:,2]),odeArgs)
            f[:,4] = feval(odeFcn,t+hA[3],y+np.dot(f,hB[:,3]),odeArgs)
            f[:,5] = feval(odeFcn,t+hA[4],y+np.dot(f,hB[:,4]),odeArgs)
            
            tnew = t + hA[5]
            if done :
                tnew = tfinal  # Hit end point exactly.
            h = tnew - t # Purify h
            
            ynew = y + np.dot(f,hB[:,5])
            f[:,6] = feval(odeFcn,tnew,ynew,odeArgs)
            nfevals = nfevals + 6

            #else : # Successful step
            NNreset_f7 = False
            if nonNegative and np.any(ynew[idxNonNegative]<0) :
                ynew[idxNonNegative] = np.maximum(ynew[idxNonNegative],0)
                if normcontrol :
                    normynew = np.linalg.norm(ynew)
                NNreset_f7 = True
            break
        nsteps = nsteps + 1
             
        if haveEventFcn :
            te,ye,ie,valt,stop=odezero(None,eventFcn,eventArgs,valt,t,y,tnew,ynew,t0,h,f,idxNonNegative)
            
            if len(te) != 0 :
                
                teout=np.append(teout,te)
                if len(yeout) ==0 :
                    yeout=ye
                else:
                    yeout=np.append(yeout,ye,axis=1)
                ieout=np.append(ieout,ie)
                
                if stop :
                    taux = t + (te[-1] - t)*A
                    trash, f[:,1:7]=ntrp45(taux,t,y,None,None,h,f,idxNonNegative)
                    tnew = te[-1]
                    ynew = ye[:,-1]
                    h = tnew - t
                    done = True

        #GERER LES output
        if outputAt == 1 : #Evaluate only at t_span
            
            if tdir == 1 : #tspan is increasing
                while NEXT < ntspan :
                    if tspan[NEXT] == tnew :
                        
                        output_t[NEXT] = tnew     
                        output_y[:,NEXT] = ynew                       
                        NEXT = NEXT+1
                        
                    elif tspan[NEXT] < tnew and t < tspan[NEXT] :
                        
                        first_indice = NEXT
                        NEXT = NEXT+1
                        while tspan[NEXT] < tnew :
                            NEXT = NEXT+1
                        last_indice = NEXT
                        
                        tinterp = tspan[first_indice:last_indice]
                        output_t[first_indice:last_indice] = tinterp
                        
                        yinterp = ntrp45(tinterp,t,y,tnew,ynew,h,f,idxNonNegative,Need_ypinterp = False)
                        output_y[:,first_indice:last_indice] = yinterp
                        
                    elif stop == True :
                        output_t = np.append(output_t,[tnew],axis=0)
                        
                        to_concatenate_y = np.transpose(np.array([ynew]))
                        output_y = np.append(output_y,to_concatenate_y,axis=1)
                        
                        break
                        
                    elif tspan[NEXT] > tnew:
                        break
                    
            elif tdir == -1 : #tspan is decreasing
                while NEXT < ntspan :
                    if tspan[NEXT] == tnew :
                        
                        output_t[NEXT] = tnew
                        output_y[:,NEXT] = ynew
                        NEXT = NEXT+1
                        
                    elif tspan[NEXT] > tnew and t > tspan[NEXT] :
                        
                        first_indice = NEXT
                        NEXT = NEXT+1
                        while tspan[NEXT] > tnew :
                            NEXT = NEXT+1
                        last_indice = NEXT
                        
                        tinterp = tspan[first_indice:last_indice]
                        output_t[first_indice:last_indice] = tinterp
                        
                        yinterp = ntrp45(tinterp,t,y,tnew,ynew,h,f,idxNonNegative,Need_ypinterp = False)
                        output_y[:,first_indice:last_indice] = yinterp
                        
                    elif stop == True : # DEBUG
                        output_t = np.append(output_t,[tnew],axis=0)
                        
                        to_concatenate_y = np.transpose(np.array([ynew]))
                        output_y = np.append(output_y,to_concatenate_y,axis=1)
                        
                        break
                    
                    elif tspan[NEXT] < tnew:
                        break
        
        else : 
            if outputAt == 3 : #Evaluate at solver steps + refined step
                    
                tinterp = t + h*S               
                output_t = np.append(output_t,tinterp,axis=0)
                
                yinterp = ntrp45(tinterp,t,y,tnew,ynew,h,f,idxNonNegative,Need_ypinterp = False)
                output_y = np.append(output_y,yinterp,axis=1)
                
                output_t = np.append(output_t,[tnew],axis=0)
                
                to_concatenate_y = np.transpose(np.array([ynew]))
                
                output_y = np.append(output_y,to_concatenate_y,axis=1)               
                
            else : #Evaluate only at solver steps
                output_t = np.append(output_t,[tnew],axis=0)
                    
                to_concatenate_y = np.transpose(np.array([ynew]))
                output_y = np.append(output_y,to_concatenate_y,axis=1)
                
        
        if done :
            break
        
        # Advance the integration one step.
        t = tnew
        y = ynew
        if normcontrol:
            normy = normynew
            
        if NNreset_f7 :
        # Used f7 for unperturbed solution to interpolate.  
        # Now reset f7 to move along constraint. 
            f[:,6] = feval(odeFcn,tnew,ynew,odeArgs)
            nfevals = nfevals + 1
        f[:,0] = f[:,6]
        
    extdata = Extdata(odeFcn,options,odeArgs)
    stats = Stats(nsteps,nfailed,nfevals)
    oderesult = Oderesult(solver_name,extdata,output_t,output_y,stats,teout,yeout,ieout)
    return oderesult
Exemple #5
0
def ode45(ode, tspan, y0, options=None, varargin=None):

    solver_name = 'ode45'
    # Check inputs

    # Stats
    nsteps = 0
    nfailed = 0
    nfevals = 0

    # Output
    FcnHandlesUsed = isfunction(ode)
    if not FcnHandlesUsed:
        raise ValueError("ode is not an handle function")

    # Handle solver arguments
    neq, tspan, ntspan, NEXT, t0, tfinal, tdir, y0, f0, odeArgs, odeFcn, options, threshold, rtol, normcontrol, normy, hmax, htry, htspan, dataType = odearguments(
        FcnHandlesUsed, solver_name, ode, tspan, y0, options, varargin)
    nfevals = nfevals + 1

    #Handle the output
    refine = np.maximum(1, options.Refine)

    if ntspan > 2:
        outputAt = 1  # output only at tspan points
    elif refine <= 1:
        outputAt = 2  # computed points, no refinement
    else:
        outputAt = 3  # computed points, with refinement
        S = np.array(range(1, refine)) / refine

    # Handle the event function
    haveEventFcn, eventFcn, eventArgs, valt, teout, yeout, ieout = odeevents(
        FcnHandlesUsed, odeFcn, t0, y0, options, varargin)

    # Handle the mass matrix
    Mtype, M, Mfun = odemass(FcnHandlesUsed, odeFcn, t0, y0, options, varargin)
    if Mtype > 0:
        # Incorporate the mass matrix into odeFcn and odeArgs.
        odeFcn, odeArgs = odemassexplicit(FcnHandlesUsed, Mtype, odeFcn,
                                          odeArgs, Mfun, M)
        f0 = feval(odeFcn, t0, y0, odeArgs)
        nfevals = nfevals + 1

    #Non-negative solution components
    nonNegative = len(
        options.NonNegative) != 0  #not isempty(options.NonNegative)
    idxNonNegative = np.array(options.NonNegative)
    if nonNegative:
        odeFcn, thresholdNonNegative, odeArgs = odenonnegative(
            odeFcn, y0, threshold, idxNonNegative, odeArgs)
        f0 = feval(odeFcn, t0, y0, odeArgs)
        nfevals = nfevals + 1

    t = t0
    y = y0

    # Allocate memory for generating output.
    if outputAt == 1:
        output_y = np.zeros((neq, ntspan), dtype=dataType)
        output_t = np.zeros(ntspan, dtype=dataType)
    else:
        output_y = np.zeros((neq, 1), dtype=dataType)
        output_t = np.zeros(1, dtype=dataType)

    output_y[:, 0] = y0
    output_t[0] = t0

    #Initialize method parameters.
    POW = 1 / 5
    A = np.array([1 / 5, 3 / 10, 4 / 5, 8 / 9, 1, 1], dtype=dataType)
    B = np.array(
        [[1 / 5, 3 / 40, 44 / 45, 19372 / 6561, 9017 / 3168, 35 / 384],
         [0, 9 / 40, -56 / 15, -25360 / 2187, -355 / 33, 0],
         [0, 0, 32 / 9, 64448 / 6561, 46732 / 5247, 500 / 1113],
         [0, 0, 0, -212 / 729, 49 / 176, 125 / 192],
         [0, 0, 0, 0, -5103 / 18656, -2187 / 6784], [0, 0, 0, 0, 0, 11 / 84],
         [0, 0, 0, 0, 0, 0]],
        dtype=dataType)

    E = np.array([
        71 / 57600, 0, -71 / 16695, 71 / 1920, -17253 / 339200, 22 / 525,
        -1 / 40
    ],
                 dtype=dataType)
    f = np.zeros((neq, 7), dtype=dataType)
    hmin = 16 * np.finfo(float(t)).eps

    if htry is None:  # Compute an initial step size h using y'(t).
        absh = np.minimum(hmax, htspan)
        if normcontrol:
            rh = (np.linalg.norm(f0) /
                  np.maximum(normy, threshold)) / (0.8 * rtol**POW)
        else:
            rh = np.linalg.norm(f0 / np.maximum(np.abs(y0), threshold),
                                np.inf) / (0.8 * rtol**POW)
        if absh * rh > 1:
            absh = 1 / rh
        absh = np.maximum(absh, hmin)
    else:
        absh = np.minimum(hmax, np.maximum(hmin, htry))
    f[:, 0] = f0
    #THE MAIN LOOP
    done = False
    stop = False
    while not done:
        # By default, hmin is a small number such that t+hmin is only slightly
        # different than t.  It might be 0 if t is 0.
        hmin = 16 * np.finfo(float(t)).eps
        absh = np.minimum(hmax, np.maximum(
            hmin, absh))  # couldn't limit absh until new hmin
        h = tdir * absh

        # Stretch the step if within 10% of tfinal-t.
        if 1.1 * absh >= np.abs(tfinal - t):
            h = tfinal - t
            absh = np.abs(h)
            done = True

        # LOOP FOR ADVANCING ONE STEP.
        nofailed = True  # no failed attempts
        while True:

            hA = h * A
            hB = h * B

            f[:, 1] = feval(odeFcn, t + hA[0], y + np.dot(f, hB[:, 0]),
                            odeArgs)
            f[:, 2] = feval(odeFcn, t + hA[1], y + np.dot(f, hB[:, 1]),
                            odeArgs)
            f[:, 3] = feval(odeFcn, t + hA[2], y + np.dot(f, hB[:, 2]),
                            odeArgs)
            f[:, 4] = feval(odeFcn, t + hA[3], y + np.dot(f, hB[:, 3]),
                            odeArgs)
            f[:, 5] = feval(odeFcn, t + hA[4], y + np.dot(f, hB[:, 4]),
                            odeArgs)

            tnew = t + hA[5]
            if done:
                tnew = tfinal  # Hit end point exactly.
            h = tnew - t  # Purify h

            ynew = y + np.dot(f, hB[:, 5])
            f[:, 6] = feval(odeFcn, tnew, ynew, odeArgs)
            nfevals = nfevals + 6

            #Estimate the error
            NNrejectStep = False
            if normcontrol:
                normynew = np.linalg.norm(ynew)
                errwt = np.maximum(np.maximum(normy, normynew), threshold)
                err = absh * (np.linalg.norm(np.dot(f, E)) / errwt)
                if nonNegative and (err <= rtol) and np.any(
                        ynew[idxNonNegative] < 0):
                    errNN = np.linalg.norm(np.maximum(
                        0, -ynew[idxNonNegative])) / errwt
                    if errNN > rtol:
                        err = errNN
                        NNrejectStep = True
            else:
                err = absh * (np.linalg.norm(
                    np.dot(f, E) /
                    np.maximum(np.maximum(np.abs(y), np.abs(ynew)), threshold),
                    np.inf))
                if nonNegative and (err <= rtol) and np.any(
                        ynew[idxNonNegative] < 0):
                    errNN = np.linalg.norm(
                        np.maximum(0, -ynew[idxNonNegative]) /
                        thresholdNonNegative, np.inf)
                    if errNN > rtol:
                        err = errNN
                        NNrejectStep = True

            # Accept the solution only if the weighted error is no more than the
            # tolerance rtol.  Estimate an h that will yield an error of rtol on
            # the next step or the next try at taking this step, as the case may be,
            # and use 0.8 of this value to avoid failures.
            if err > rtol:  # Failed step
                nfailed = nfailed + 1
                if absh <= hmin:
                    print(
                        "Warning:python:ode45:IntegrationTolNotMet:absh <= hmin "
                    )
                    extdata = Extdata(odeFcn, options, odeArgs)
                    stats = Stats(nsteps, nfailed, nfevals)
                    oderesult = Oderesult(solver_name, extdata, output_t,
                                          output_y, stats, teout, yeout, ieout)
                    return oderesult

                if nofailed:
                    nofailed = False
                    if NNrejectStep:
                        absh = np.maximum(hmin, 0.5 * absh)
                    else:
                        absh = np.maximum(
                            hmin,
                            absh * np.maximum(0.1, 0.8 * (rtol / err)**POW))
                else:
                    absh = np.maximum(hmin, 0.5 * absh)
                h = tdir * absh
                done = False

            else:  # Successful step
                NNreset_f7 = False
                if nonNegative and np.any(ynew[idxNonNegative] < 0):
                    ynew[idxNonNegative] = np.maximum(ynew[idxNonNegative], 0)
                    if normcontrol:
                        normynew = np.linalg.norm(ynew)
                    NNreset_f7 = True
                break
        nsteps = nsteps + 1

        if haveEventFcn:
            te, ye, ie, valt, stop = odezero(None, eventFcn, eventArgs, valt,
                                             t, y, tnew, ynew, t0, h, f,
                                             idxNonNegative)

            if len(te) != 0:

                teout = np.append(teout, te)
                if len(yeout) == 0:
                    yeout = ye
                else:
                    yeout = np.append(yeout, ye, axis=1)
                ieout = np.append(ieout, ie)

                if stop:
                    taux = t + (te[-1] - t) * A
                    trash, f[:, 1:7] = ntrp45(taux, t, y, None, None, h, f,
                                              idxNonNegative)
                    tnew = te[-1]
                    ynew = ye[:, -1]
                    h = tnew - t
                    done = True

        #Manage output
        if outputAt == 1:  #Evaluate only at t_span

            if tdir == 1:  #tspan is increasing
                while NEXT < ntspan:
                    if tspan[NEXT] == tnew:

                        output_t[NEXT] = tnew
                        output_y[:, NEXT] = ynew
                        NEXT = NEXT + 1

                    elif tspan[NEXT] < tnew and t < tspan[NEXT]:

                        first_indice = NEXT
                        NEXT = NEXT + 1
                        while tspan[NEXT] < tnew:
                            NEXT = NEXT + 1
                        last_indice = NEXT

                        tinterp = tspan[first_indice:last_indice]
                        output_t[first_indice:last_indice] = tinterp

                        yinterp = ntrp45(tinterp,
                                         t,
                                         y,
                                         tnew,
                                         ynew,
                                         h,
                                         f,
                                         idxNonNegative,
                                         Need_ypinterp=False)
                        output_y[:, first_indice:last_indice] = yinterp

                    elif stop == True:
                        output_t = np.append(output_t, [tnew], axis=0)

                        to_concatenate_y = np.transpose(np.array([ynew]))
                        output_y = np.append(output_y,
                                             to_concatenate_y,
                                             axis=1)

                        break

                    elif tspan[NEXT] > tnew:
                        break

            elif tdir == -1:  #tspan is decreasing
                while NEXT < ntspan:
                    if tspan[NEXT] == tnew:

                        output_t[NEXT] = tnew
                        output_y[:, NEXT] = ynew
                        NEXT = NEXT + 1

                    elif tspan[NEXT] > tnew and t > tspan[NEXT]:

                        first_indice = NEXT
                        NEXT = NEXT + 1
                        while tspan[NEXT] > tnew:
                            NEXT = NEXT + 1
                        last_indice = NEXT

                        tinterp = tspan[first_indice:last_indice]
                        output_t[first_indice:last_indice] = tinterp

                        yinterp = ntrp45(tinterp,
                                         t,
                                         y,
                                         tnew,
                                         ynew,
                                         h,
                                         f,
                                         idxNonNegative,
                                         Need_ypinterp=False)
                        output_y[:, first_indice:last_indice] = yinterp

                    elif stop == True:  # DEBUG
                        output_t = np.append(output_t, [tnew], axis=0)

                        to_concatenate_y = np.transpose(np.array([ynew]))
                        output_y = np.append(output_y,
                                             to_concatenate_y,
                                             axis=1)

                        break

                    elif tspan[NEXT] < tnew:
                        break

        else:
            if outputAt == 3:  #Evaluate at solver steps + refined step

                tinterp = t + h * S
                output_t = np.append(output_t, tinterp, axis=0)

                yinterp = ntrp45(tinterp,
                                 t,
                                 y,
                                 tnew,
                                 ynew,
                                 h,
                                 f,
                                 idxNonNegative,
                                 Need_ypinterp=False)
                output_y = np.append(output_y, yinterp, axis=1)

                output_t = np.append(output_t, [tnew], axis=0)

                to_concatenate_y = np.transpose(np.array([ynew]))

                output_y = np.append(output_y, to_concatenate_y, axis=1)

            else:  #Evaluate only at solver steps
                output_t = np.append(output_t, [tnew], axis=0)

                to_concatenate_y = np.transpose(np.array([ynew]))
                output_y = np.append(output_y, to_concatenate_y, axis=1)

        if done:
            break
        # If there were no failures compute a new h.
        if nofailed:
            # Note that absh may shrink by 0.8, and that err may be 0.
            temp = 1.25 * (err / rtol)**POW
            if temp > 0.2:
                absh = absh / temp
            else:
                absh = 5.0 * absh
        # Advance the integration one step.
        t = tnew
        y = ynew
        if normcontrol:
            normy = normynew

        if NNreset_f7:
            # Used f7 for unperturbed solution to interpolate.
            # Now reset f7 to move along constraint.
            f[:, 6] = feval(odeFcn, tnew, ynew, odeArgs)
            nfevals = nfevals + 1
        f[:, 0] = f[:, 6]

    extdata = Extdata(odeFcn, options, odeArgs)
    stats = Stats(nsteps, nfailed, nfevals)
    oderesult = Oderesult(solver_name, extdata, output_t, output_y, stats,
                          teout, yeout, ieout)
    return oderesult
 def test_odemass_vec_matrix(self):
     opts = {'Mass':[[1, 2],[2, 3]]}
     massType, massM, massFcn = odemass(self.t0,self.y0_vec,opts,[])
     self.assertEqual(massType,1)
     self.assertEqual(massM,[[1, 2],[2, 3]])
     self.assertEqual(massFcn,None)
 def test_odemass_int_function_state(self):
     opts = {'Mass':self.mass_state_int,'MStateDependence':'weak'}
     massType, massM, massFcn = odemass(self.t0,self.y0_int,opts,[])
     self.assertEqual(massType,3)
     self.assertEqual(massM,[4])
     self.assertEqual(massFcn,self.mass_state_int)
 def test_odemass_int_function_state_extra(self):
     opts = {'Mass':self.mass_state_int}
     massType, massM, massFcn = odemass(self.t0,self.y0_int,opts,[])
     self.assertEqual(massType,3)
     self.assertEqual(massM,[4])
     self.assertEqual(massFcn,self.mass_state_int)
 def test_odemass_int_function_time(self):
     opts = {'Mass':self.mass_time_int,'MStateDependence':'none'}
     massType, massM, massFcn = odemass(self.t0,self.y0_int,opts,[])
     self.assertEqual(massType,2)
     self.assertEqual(massM,[2])
     self.assertEqual(massFcn,self.mass_time_int)
 def test_odemass_int_matrix(self):
     opts = {'Mass':[3]}
     massType, massM, massFcn = odemass(self.t0,self.y0_int,opts,[])
     self.assertEqual(massType,1)
     self.assertEqual(massM,[3])
     self.assertEqual(massFcn,None)
 def test_odemass_int_nomass(self):
     opts = {}
     massType, massM, massFcn = odemass(self.t0,self.y0_int,opts,[])
     self.assertEqual(massType,0)
     self.assertEqual(massM[0,0],1)
     self.assertEqual(massFcn,None)