www.gusucode.com > funfun工具箱matlab源码程序 > funfun/ode23t.m
function varargout = ode23t(ode,tspan,y0,options,varargin) %ODE23T Solve moderately stiff ODEs and DAEs, trapezoidal rule. % [TOUT,YOUT] = ODE23T(ODEFUN,TSPAN,Y0) with TSPAN = [T0 TFINAL] integrates % the system of differential equations y' = f(t,y) from time T0 to TFINAL % with initial conditions Y0. ODEFUN is a function handle. For a scalar T % and a vector Y, ODEFUN(T,Y) must return a column vector corresponding % to f(t,y). Each row in the solution array YOUT corresponds to a time % returned in the column vector TOUT. To obtain solutions at specific % times T0,T1,...,TFINAL (all increasing or all decreasing), use TSPAN = % [T0 T1 ... TFINAL]. % % [TOUT,YOUT] = ODE23T(ODEFUN,TSPAN,Y0,OPTIONS) solves as above with default % integration parameters replaced by values in OPTIONS, an argument created % with the ODESET function. See ODESET for details. Commonly used options % are scalar relative error tolerance 'RelTol' (1e-3 by default) and vector % of absolute error tolerances 'AbsTol' (all components 1e-6 by default). % If certain components of the solution must be non-negative, use % ODESET to set the 'NonNegative' property to the indices of these % components. The 'NonNegative' property is ignored for problems % where there is a mass matrix. % % The Jacobian matrix df/dy is critical to reliability and efficiency. Use % ODESET to set 'Jacobian' to a function handle FJAC if FJAC(T,Y) returns % the Jacobian df/dy or to the matrix df/dy if the Jacobian is constant. % If the 'Jacobian' option is not set (the default), df/dy is approximated % by finite differences. Set 'Vectorized' 'on' if the ODE function is coded % so that ODEFUN(T,[Y1 Y2 ...]) returns [ODEFUN(T,Y1) ODEFUN(T,Y2) ...]. % If df/dy is a sparse matrix, set 'JPattern' to the sparsity pattern of % df/dy, i.e., a sparse matrix S with S(i,j) = 1 if component i of f(t,y) % depends on component j of y, and 0 otherwise. % % ODE23T can solve problems M(t,y)*y' = f(t,y) with mass matrix M(t,y). Use % ODESET to set the 'Mass' property to a function handle MASS if MASS(T,Y) % returns the value of the mass matrix. If the mass matrix is constant, % the matrix can be used as the value of the 'Mass' option. Problems with % state-dependent mass matrices are more difficult. If the mass matrix does % not depend on the state variable Y and the function MASS is to be called % with one input argument T, set 'MStateDependence' to 'none'. If the mass % matrix depends weakly on Y, set 'MStateDependence' to 'weak' (the % default) and otherwise, to 'strong'. In either case the function MASS is % to be called with the two arguments (T,Y). If there are many differential % equations, it is important to exploit sparsity: Return a sparse % M(t,y). Either supply the sparsity pattern of df/dy using the 'JPattern' % property or a sparse df/dy using the Jacobian property. For strongly % state-dependent M(t,y), set 'MvPattern' to a sparse matrix S with S(i,j) % = 1 if for any k, the (i,k) component of M(t,y) depends on component j of % y, and 0 otherwise. % % If the mass matrix is non-singular, the solution of the problem is % straightforward. See examples FEM1ODE, FEM2ODE, BATONODE, or % BURGERSODE. If M(t0,y0) is singular, the problem is a differential- % algebraic equation (DAE). ODE23T solves DAEs of index 1. DAEs have % solutions only when y0 is consistent, i.e., there is a yp0 such that % M(t0,y0)*yp0 = f(t0,y0). Use ODESET to set 'MassSingular' to 'yes', 'no', % or 'maybe'. The default of 'maybe' causes ODE23T to test whether M(t0,y0) % is singular. You can provide yp0 as the value of the 'InitialSlope' % property. The default is the zero vector. If y0 and yp0 are not % consistent, ODE23T treats them as guesses, tries to compute consistent % values close to the guesses, and then goes on to solve the problem. See % examples HB1DAE or AMP1DAE. % % [TOUT,YOUT,TE,YE,IE] = ODE23T(ODEFUN,TSPAN,Y0,OPTIONS) with the 'Events' % property in OPTIONS set to a function handle EVENTS, solves as above % while also finding where functions of (T,Y), called event functions, % are zero. For each function you specify whether the integration is % to terminate at a zero and whether the direction of the zero crossing % matters. These are the three column vectors returned by EVENTS: % [VALUE,ISTERMINAL,DIRECTION] = EVENTS(T,Y). For the I-th event function: % VALUE(I) is the value of the function, ISTERMINAL(I)=1 if the integration % is to terminate at a zero of this event function and 0 otherwise. % DIRECTION(I)=0 if all zeros are to be computed (the default), +1 if only % zeros where the event function is increasing, and -1 if only zeros where % the event function is decreasing. Output TE is a column vector of times % at which events occur. Rows of YE are the corresponding solutions, and % indices in vector IE specify which event occurred. % % SOL = ODE23T(ODEFUN,[T0 TFINAL],Y0...) returns a structure that can be % used with DEVAL to evaluate the solution or its first derivative at % any point between T0 and TFINAL. The steps chosen by ODE23T are returned % in a row vector SOL.x. For each I, the column SOL.y(:,I) contains % the solution at SOL.x(I). If events were detected, SOL.xe is a row vector % of points at which events occurred. Columns of SOL.ye are the corresponding % solutions, and indices in vector SOL.ie specify which event occurred. % % Example % [t,y]=ode23t(@vdp1000,[0 3000],[2 0]); % plot(t,y(:,1)); % solves the system y' = vdp1000(t,y), using the default relative error % tolerance 1e-3 and the default absolute tolerance of 1e-6 for each % component, and plots the first component of the solution. % % See also ODE15S, ODE23S, ODE23TB, ODE45, ODE23, ODE113, ODE15I, % ODESET, ODEPLOT, ODEPHAS2, ODEPHAS3, ODEPRINT, DEVAL, % ODEEXAMPLES, VDPODE, BRUSSODE, HB1DAE, FUNCTION_HANDLE. % ODE23T is an implementation of the trapezoidal rule. A "free" % interpolant is used. % Mark W. Reichelt, Lawrence F. Shampine, Yanyuan Ma, and Jacek Kierzenka % 12-18-97 % Copyright 1984-2011 The MathWorks, Inc. solver_name = 'ode23t'; if nargin < 4 options = []; if nargin < 3 y0 = []; if nargin < 2 tspan = []; if nargin < 1 error(message('MATLAB:ode23t:NotEnoughInputs')); end end end end % Stats nsteps = 0; nfailed = 0; nfevals = 0; npds = 0; ndecomps = 0; nsolves = 0; % Output FcnHandlesUsed = isa(ode,'function_handle'); output_sol = (FcnHandlesUsed && (nargout==1)); % sol = odeXX(...) output_ty = (~output_sol && (nargout > 0)); % [t,y,...] = odeXX(...) % There might be no output requested... sol = []; zdata = []; znewdata = []; if output_sol sol.solver = solver_name; sol.extdata.odefun = ode; sol.extdata.options = options; sol.extdata.varargin = varargin; end % Handle solver arguments [neq, tspan, ntspan, next, t0, tfinal, tdir, y0, f0, odeArgs, odeFcn, ... options, threshold, rtol, normcontrol, normy, hmax, htry, htspan] = ... odearguments(FcnHandlesUsed, solver_name, ode, tspan, y0, options, varargin); nfevals = nfevals + 1; one2neq = (1:neq); % Handle the output if nargout > 0 outputFcn = odeget(options,'OutputFcn',[],'fast'); else outputFcn = odeget(options,'OutputFcn',@odeplot,'fast'); end outputArgs = {}; if isempty(outputFcn) haveOutputFcn = false; else haveOutputFcn = true; outputs = odeget(options,'OutputSel',1:neq,'fast'); if isa(outputFcn,'function_handle') % With MATLAB 6 syntax pass additional input arguments to outputFcn. outputArgs = varargin; end end refine = max(1,odeget(options,'Refine',1,'fast')); if ntspan > 2 outputAt = 'RequestedPoints'; % output only at tspan points elseif refine <= 1 outputAt = 'SolverSteps'; % computed points, no refinement else outputAt = 'RefinedSteps'; % computed points, with refinement S = (1:refine-1) / refine; end printstats = strcmp(odeget(options,'Stats','off','fast'),'on'); % Handle the event function [haveEventFcn,eventFcn,eventArgs,valt,teout,yeout,ieout] = ... odeevents(FcnHandlesUsed,odeFcn,t0,y0,options,varargin); % Handle the mass matrix [Mtype, Mt, Mfun, Margs, dMoptions] = odemass(FcnHandlesUsed,odeFcn,t0,y0,... options,varargin); % Non-negative solution components idxNonNegative = odeget(options,'NonNegative',[],'fast'); nonNegative = ~isempty(idxNonNegative); if nonNegative if Mtype == 0 % Explicit ODE -- modify the derivative function [odeFcn,thresholdNonNegative] = odenonnegative(odeFcn,y0,threshold,idxNonNegative); f0 = feval(odeFcn,t0,y0,odeArgs{:}); nfevals = nfevals + 1; else % Linearly implicit ODE/DAE -- ignore non-negativity constraints warning(message('MATLAB:ode23t:NonNegativeIgnoredForLinearlyImplicitSystems')); nonNegative = false; idxNonNegative = []; end end % Handle the Jacobian [Jconstant,Jac,Jargs,Joptions] = ... odejacobian(FcnHandlesUsed,odeFcn,t0,y0,options,varargin); Janalytic = isempty(Joptions); % if not set via 'options', initialize constant Jacobian here if Jconstant if isempty(Jac) % use odenumjac [Jac,Joptions.fac,nF] = odenumjac(odeFcn, {t0,y0,odeArgs{:}}, f0, Joptions); nfevals = nfevals + nF; npds = npds + 1; elseif ~isa(Jac,'numeric') % not been set via 'options' Jac = feval(Jac,t0,y0,Jargs{:}); % replace by its value npds = npds + 1; end end t = t0; y = y0; yp0_OK = false; DAE = false; RowScale = []; if Mtype > 0 nz = nnz(Mt); if nz == 0 error(message('MATLAB:ode23t:MassMatrixAllZero')) end Msingular = odeget(options,'MassSingular','maybe','fast'); switch Msingular case 'no', DAE = false; case 'yes', DAE = true; case 'maybe', DAE = (eps*nz*condest(Mt) > 1); end if DAE yp0 = odeget(options,'InitialSlope',[],'fast'); if isempty(yp0) yp0_OK = false; yp0 = zeros(neq,1); else yp0 = yp0(:); if length(yp0) ~= neq error(message('MATLAB:ode23t:YoYPoLengthMismatch')); end % Test if (y0,yp0) are consistent enough to accept. yp0_OK = (norm(Mt*yp0 - f0) <= 1e-3*rtol*max(norm(Mt*yp0),norm(f0))); end if ~yp0_OK % Must compute ICs, so classify them. if Mtype >= 3 % state dependent ICtype = 3; else % M, M(t) % Test for a diagonal mass matrix. [r,c] = find(Mt); if isequal(r,c) % diagonal ICtype = 1; elseif ~issparse(Mt) % not diagonal but full ICtype = 2; else % sparse, not diagonal ICtype = 3; end end end end end Mcurrent = true; needNewM = false; Mtnew = Mt; % Initialize method parameters. % The first step is taken at order one with the backward Euler method (BDF1). pow = 1/2; gamma = 1; start = true; % Adjust the warnings. warnoffId = { 'MATLAB:singularMatrix', 'MATLAB:nearlySingularMatrix'}; for i = 1:length(warnoffId) warnstat(i) = warning('query',warnoffId{i}); warnoff(i) = warnstat(i); warnoff(i).state = 'off'; end % Get the initial slope yp. For DAEs the default is to compute % consistent initial conditions. if DAE && ~yp0_OK if ICtype < 3 [y,yp,f0,dfdy,nFE,nPD,Jfac] = daeic12(odeFcn,odeArgs,t,ICtype,Mt,y,yp0,f0,... rtol,Jconstant,Jac,Jargs,Joptions); else [y,yp,f0,dfdy,nFE,nPD,Jfac,dMfac] = daeic3(odeFcn,odeArgs,tspan,htry,Mtype,Mt,Mfun,... Margs,dMoptions,y,yp0,f0,rtol,Jconstant,... Jac,Jargs,Joptions); if ~isempty(dMoptions) dMoptions.fac = dMfac; end end if ~isempty(Joptions) Joptions.fac = Jfac; end nfevals = nfevals + nFE; npds = npds + nPD; if Mtype >= 3 Mt = feval(Mfun,t,y,Margs{:}); Mtnew = Mt; Mcurrent = true; end else if Mtype == 0 yp = f0; elseif DAE && yp0_OK yp = yp0; else if issparse(Mt) [L,U,P,Q,R] = lu(Mt); yp = Q * (U \ (L \ (P * (R \ f0)))); else [L,U,p] = lu(Mt,'vector'); yp = U \ (L \ f0(p)); end ndecomps = ndecomps + 1; nsolves = nsolves + 1; end if Jconstant dfdy = Jac; elseif Janalytic dfdy = feval(Jac,t,y,Jargs{:}); npds = npds + 1; else % Joptions not empty [dfdy,Joptions.fac,nF] = odenumjac(odeFcn, {t,y,odeArgs{:}}, f0, Joptions); nfevals = nfevals + nF; npds = npds + 1; end end Jcurrent = true; needNewJ = false; % hmin is a small number such that t + hmin is clearly different from t in % the working precision, but with this definition, it is 0 if t = 0. hmin = 16*eps*abs(t); if isempty(htry) % Compute an initial step size h using yp = y'(t). if normcontrol wt = max(normy,threshold); rh = 1.43 * (norm(yp) / wt) / rtol^pow; % 1.43 = 1 / 0.7 else wt = max(abs(y),threshold); rh = 1.43 * norm(yp ./ wt,inf) / rtol^pow; end absh = min(hmax, htspan); if absh * rh > 1 absh = 1 / rh; end absh = max(absh, hmin); if ~DAE % The error of BDF1 is 0.5*h^2*y''(t), so we can determine the optimal h. h = tdir * absh; tdel = (t + tdir*min(sqrt(eps)*max(abs(t),abs(t+h)),absh)) - t; f1 = feval(odeFcn,t+tdel,y,odeArgs{:}); nfevals = nfevals + 1; dfdt = (f1 - f0) ./ tdel; DfDt = dfdt + dfdy*yp; if normcontrol if Mtype > 0 if issparse(Mt) rh = 1.43 * sqrt(0.5 * (norm(U \ (L \ (P * ( R \ DfDt)))) / wt) / rtol); else rh = 1.43 * sqrt(0.5 * (norm(U \ (L \ DfDt(p))) / wt) / rtol); end else rh = 1.43 * sqrt(0.5 * (norm(DfDt) / wt) / rtol); end else if Mtype > 0 if issparse(Mt) rh = 1.43*sqrt(0.5*norm( (Q * (U \ (L \ (P * (R \ DfDt))))) ./ wt,inf) / rtol); else rh = 1.43*sqrt(0.5*norm( (U \ (L \ DfDt(p))) ./ wt,inf) / rtol); end else rh = 1.43 * sqrt(0.5 * norm( DfDt ./ wt,inf) / rtol); end end absh = min(hmax, htspan); if absh * rh > 1 absh = 1 / rh; end absh = max(absh, hmin); end else absh = min(hmax, max(hmin, htry)); end h = tdir * absh; % Allocate memory if we're generating output. nout = 0; tout = []; yout = []; if nargout > 0 if output_sol chunk = min(max(100,50*refine), refine+floor((2^11)/neq)); tout = zeros(1,chunk); yout = zeros(neq,chunk); zdata = zeros(neq,chunk); znewdata = zeros(neq,chunk); else if ntspan > 2 % output only at tspan points tout = zeros(1,ntspan); yout = zeros(neq,ntspan); else % alloc in chunks chunk = min(max(100,50*refine), refine+floor((2^13)/neq)); tout = zeros(1,chunk); yout = zeros(neq,chunk); end end nout = 1; tout(nout) = t; yout(:,nout) = y; end % Initialize the output function. if haveOutputFcn feval(outputFcn,[t tfinal],y(outputs),'init',outputArgs{:}); end % THE MAIN LOOP z = h * yp; % z is the scaled derivative. if Mtype == 4 [dMzdy,dMoptions.fac] = odenumjac(@odemxv, {Mfun,t,y,z,Margs{:}}, Mt*z, ... dMoptions); end needNewLU = true; % Initialize LU. done = false; while ~done hmin = 16*eps*abs(t); abshlast = absh; absh = min(hmax, max(hmin, absh)); h = tdir * absh; % Stretch the step if within 10% of tfinal-t. if 1.1*absh >= abs(tfinal - t) h = tfinal - t; absh = abs(h); done = true; end if absh ~= abshlast z = (absh / abshlast) * z; needNewLU = true; end % LOOP FOR ADVANCING ONE STEP. nofailed = true; % no failed attempts itfailed = 0; while true % Evaluate the formula. if normcontrol wt = max(normy,threshold); else wt = max(abs(y),threshold); end if needNewJ if Janalytic dfdy = feval(Jac,t,y,Jargs{:}); else % Joptions not empty f0 = feval(odeFcn,t,y,odeArgs{:}); [dfdy,Joptions.fac,nF] = odenumjac(odeFcn, {t,y,odeArgs{:}}, f0, Joptions); nfevals = nfevals + nF + 1; end npds = npds + 1; Jcurrent = true; needNewJ = false; needNewLU = true; end if needNewM Mt = feval(Mfun,t,y,Margs{:}); Mcurrent = true; if Mtype == 4 [dMzdy,dMoptions.fac] = odenumjac(@odemxv, {Mfun,t,y,z,Margs{:}}, Mt*z, ... dMoptions); end needNewM = false; needNewLU = true; end if needNewLU Miter = Mt - (gamma*h)*dfdy; if Mtype == 4 Miter = Miter + dMzdy; end % Use explicit scaling of the equations when solving DAEs. if DAE RowScale = 1 ./ max(abs(Miter),[],2); Miter = sparse(one2neq,one2neq,RowScale) * Miter; end if issparse(Miter) [L,U,P,Q,R] = lu(Miter); else [L,U,P] = lu(Miter,'vector'); Q = []; R = []; end ndecomps = ndecomps + 1; rate = []; needNewLU = false; end % Predict ynew, znew at tnew. tnew = t + h; if done tnew = tfinal; % Hit end point exactly. end h = tnew - t; % Purify h. if start % Backward Euler (BDF1) % Use linear interpolating polynomial. znew = z; ynew = y + z; else % Trapezoidal rule (TR) % Use quadratic interpolating polynomial. a1 = ((tnew - t_2)*(tnew - t_1))/((t - t_2)*(t - t_1)); a2 = ((tnew - t_2)*(tnew - t))/((t_1 - t_2)*(t_1 - t)); a3 = ((tnew - t_1)*(tnew - t))/((t_2 - t_1)*(t_2 - t)); ynew = a1*y + a2*y_1 + a3*y_2; znew = 2*(ynew - y) - z; end % Mtnew is required in the RHS function evaluation. if Mtype == 2 % state-independent if FcnHandlesUsed Mtnew = feval(Mfun,tnew,Margs{:}); % mass(t,p1,p2...) else Mtnew = feval(Mfun,tnew,ynew,Margs{:}); % mass(t,y,'mass',p1,p2...) end end [ynew,znew,iter,itfail,rate] = ... itsolve(Mtnew,tnew,ynew,znew,gamma,h,L,U,P,Q,R,odeFcn,odeArgs,rtol,... wt,rate,Mtype,Mfun,Margs,DAE,RowScale,warnstat,warnoff,warnoffId); nfevals = nfevals + iter; nsolves = nsolves + iter; if itfail nofailed = false; nfailed = nfailed + 1; itfailed = itfailed + 1; if Jcurrent && Mcurrent if absh <= hmin warning(message('MATLAB:ode23t:IntegrationTolNotMet', sprintf( '%e', t ), sprintf( '%e', hmin ))); solver_output = odefinalize(solver_name, sol,... outputFcn, outputArgs,... printstats, [nsteps, nfailed, nfevals,... npds, ndecomps, nsolves],... nout, tout, yout,... haveEventFcn, teout, yeout, ieout,... {zdata,znewdata,idxNonNegative}); if nargout > 0 varargout = solver_output; end return; elseif itfailed == 3 && ~start start = true; % Need to restart. pow = 1/2; % Revert to BDF1. gamma = 1; % The local truncation error of BDF1 is (1/2)*y''*h^2. % Approximate by differentiating a quadratic interpolant. d1 = y/((t-t_1)*(t-t_2)); d2 = y_1/((t_1-t)*(t_1-t_2)); d3 = y_2/((t_2-t)*(t_2-t_1)); est = d1 + d2 + d3; err = norm(est ./ wt,inf)*absh^2; q = sqrt(err / rtol); ratio = hmax / absh; if 0.7 < q*ratio ratio = 0.7/q; end abshlast = absh; absh = max(ratio * absh, hmin); if absh >= abs(tfinal-t) % Step size could have increased. absh = abs(tfinal-t); done = true; else done = false; end h = tdir * absh; z = (absh / abshlast) * z; % Rescale z because of new h. needNewLU = true; else abshlast = absh; absh = max(0.3 * absh, hmin); h = tdir * absh; z = (absh / abshlast) * z; % Rescale z because of new h. needNewLU = true; done = false; end else needNewJ = ~Jcurrent; needNewM = ~Mcurrent; end else % Estimate the local truncation error. if normcontrol normynew = norm(ynew); wt = max(wt, normynew); else wt = max(wt, abs(ynew)); end if start % The local truncation error is (1/2)*y''*h^2. % Approximate by differencing the scaled derivative. err = norm(0.5*(znew - z) ./ wt,inf); else % The local truncation error is -(1/12)*y'''*h^3. % Approximate by differentiating a cubic interpolant. c1 = ynew/((tnew-t_2)*(tnew-t_1)*(tnew-t)); c2 = y/((t-tnew)*(t-t_2)*(t-t_1)); c3 = y_1/((t_1-tnew)*(t_1-t)*(t_1-t_2)); c4 = y_2/((t_2-t)*(t_2-t_1)*(t_2-tnew)); est = (c1 + c2 + c3 + c4)/2; err = norm(est ./ wt,inf)*absh^3; end NNrejectStep = false; if nonNegative && (err <= rtol) && any(ynew(idxNonNegative)<0) if normcontrol errNN = norm( max(0,-ynew(idxNonNegative)) / wt ); else errNN = norm( max(0,-ynew(idxNonNegative)) ./ thresholdNonNegative, inf); end if errNN > rtol err = errNN; NNrejectStep = true; end end if err > rtol % Failed step nfailed = nfailed + 1; if absh <= hmin warning(message('MATLAB:ode23t:IntegrationTolNotMet', sprintf( '%e', t ), sprintf( '%e', hmin ))); solver_output = odefinalize(solver_name, sol,... outputFcn, outputArgs,... printstats, [nsteps, nfailed, nfevals,... npds, ndecomps, nsolves],... nout, tout, yout,... haveEventFcn, teout, yeout, ieout,... {zdata,znewdata,idxNonNegative}); if nargout > 0 varargout = solver_output; end return; end nofailed = false; abshlast = absh; if NNrejectStep absh = max(hmin, 0.5*absh); else absh = max(abshlast * max(0.1, 0.7*(rtol/err)^pow), hmin); end h = tdir * absh; z = (absh / abshlast) * z; needNewLU = true; done = false; else % Successful step break; end end end % while true nsteps = nsteps + 1; NNreset_znew = false; if nonNegative && any(ynew(idxNonNegative) < 0) NNidx = idxNonNegative(ynew(idxNonNegative) < 0); % logical indexing ynew(NNidx) = 0; if normcontrol normynew = norm(ynew); end NNreset_znew = true; end if haveEventFcn [te,ye,ie,valt,stop] = odezero(@ntrp23t,eventFcn,eventArgs,valt,... t,y,tnew,ynew,t0,h,z,znew,idxNonNegative); if ~isempty(te) if output_sol || (nargout > 2) teout = [teout, te]; yeout = [yeout, ye]; ieout = [ieout, ie]; end if stop % Stop on a terminal event. % Adjust the interpolation data to [t te(end)]. tzc = te(end); hzc = tzc - t; [~,ypaux] = ntrp23t(tzc,t,y,[],ynew,h,z,znew,idxNonNegative); z = hzc/h * z; % scaled derivative at t znew = hzc*ypaux; % scaled derivative at tzc tnew = te(end); ynew = ye(:,end); h = tnew - t; done = true; end end end if output_sol nout = nout + 1; if nout > length(tout) tout = [tout, zeros(1,chunk)]; % requires chunk >= refine yout = [yout, zeros(neq,chunk)]; zdata = [zdata, zeros(neq,chunk)]; znewdata = [znewdata, zeros(neq,chunk)]; end tout(nout) = tnew; yout(:,nout) = ynew; zdata(:,nout) = z; znewdata(:,nout) = znew; end if output_ty || haveOutputFcn switch outputAt case 'SolverSteps' % computed points, no refinement nout_new = 1; tout_new = tnew; yout_new = ynew; case 'RefinedSteps' % computed points, with refinement tref = t + (tnew-t)*S; nout_new = refine; tout_new = [tref, tnew]; yout_new = [ntrp23t(tref,t,y,[],ynew,h,z,znew,idxNonNegative), ynew]; case 'RequestedPoints' % output only at tspan points nout_new = 0; tout_new = []; yout_new = []; while next <= ntspan if tdir * (tnew - tspan(next)) < 0 if haveEventFcn && stop % output tstop,ystop nout_new = nout_new + 1; tout_new = [tout_new, tnew]; yout_new = [yout_new, ynew]; end break; end nout_new = nout_new + 1; tout_new = [tout_new, tspan(next)]; if tspan(next) == tnew yout_new = [yout_new, ynew]; else yout_new = [yout_new, ntrp23t(tspan(next),t,y,[],ynew,h,z,znew,... idxNonNegative)]; end next = next + 1; end end if nout_new > 0 if output_ty oldnout = nout; nout = nout + nout_new; if nout > length(tout) tout = [tout, zeros(1,chunk)]; % requires chunk >= refine yout = [yout, zeros(neq,chunk)]; end idx = oldnout+1:nout; tout(idx) = tout_new; yout(:,idx) = yout_new; end if haveOutputFcn stop = feval(outputFcn,tout_new,yout_new(outputs,:),'',outputArgs{:}); if stop done = true; end end end end if done break end % Advance the integration one step. if NNreset_znew % Used znew for unperturbed solution to interpolate. In perturbing ynew, % defined NNidx. Use now to reset znew to move along constraint. znew(NNidx) = 0; end if start % Generate "previously computed" solution by interpolation and % put the stored solutions in order, though this is not necessary. t_1 = t + 0.5*h; y_1 = ntrp23t(t_1,t,y,[],ynew,h,z,znew,idxNonNegative); t_2 = t; y_2 = y; t = tnew; y = ynew; if normcontrol normy = normynew; end z = znew; Jcurrent = Jconstant; switch Mtype case {0,1} Mcurrent = true; % Constant mass matrix I or M. case 2 % M(t) has already been evaluated at tnew in Mtnew. Mt = Mtnew; Mcurrent = true; case {3,4} % state dependent % M(t,y) has not yet been evaluated at the accepted ynew. Mcurrent = false; end % Change from BDF1 to TR. Doubling the step size is reasonable % with a raise in order and does not change the LU decomposition. start = false; pow = 1/3; gamma = 0.5; absh = 2 * absh; z = 2 * z; else t_2 = t_1; y_2 = y_1; t_1 = t; y_1 = y; t = tnew; y = ynew; if normcontrol normy = normynew; end z = znew; Jcurrent = Jconstant; switch Mtype case {0,1} Mcurrent = true; % Constant mass matrix I or M. case 2 % M(t) has already been evaluated at tnew in Mtnew. Mt = Mtnew; Mcurrent = true; case {3,4} % state dependent % M(t,y) has not yet been evaluated at the accepted ynew. Mcurrent = false; end if nofailed q = (err / rtol)^pow; ratio = hmax / absh; if 0.7 < q*ratio ratio = 0.7/q; end ratio = min(5, max(0.2, ratio)); if abs(ratio - 1) > 0.2 absh = ratio * absh; needNewLU = true; z = ratio * z; end end end end % while ~done solver_output = odefinalize(solver_name, sol,... outputFcn, outputArgs,... printstats, [nsteps, nfailed, nfevals,... npds, ndecomps, nsolves],... nout, tout, yout,... haveEventFcn, teout, yeout, ieout,... {zdata,znewdata,idxNonNegative}); if nargout > 0 varargout = solver_output; end % -------------------------------------------------------------------------- function [y,z,iter,itfail,rate] = ... itsolve(M,t,y,z,gamma,h,L,U,P,Q,R,odeFcn,odeArgs,rtol,wt,rate,... Mtype,Mfun,Margs,DAE,RowScale,warnstat,warnoff,warnoffId) % Solve the nonlinear equation M*z = h*f(t,v+gamma*z) and y = v+gamma*z. The % value v is incorporated in the predicted y and is not needed because the y % is corrected using corrections to z. The argument t is constant during the % iteration. The function f(t,y) is given by feval(odeFcn,t,y,odeArgs{:}). % Similarly, if M is state-dependent, it is given by feval(Mfun,t,y,Margs{:}). % L,U,P,Q,R is the lu decomposition of the matrix M-gamma*h*dfdy, where dfdy is % an approximate Jacobian of f. For full matrices, P is the permutation vector % and Q and R are left empty. A simplified Newton (chord) iteration is used, % so dfdy and the decomposition are held constant. z is computed to an accuracy % of kappa*rtol. The rate of convergence of the iteration is estimated. If the % iteration succeeds, itfail is set false and the estimated rate is returned % for use on a subsequent step. rate can be used as long as neither h nor % dfdy changes. maxiter = 5; kappa = 0.5; itfail = 0; minnrm = 100 * eps * norm(y ./ wt,inf); for iter = 1:maxiter if Mtype >= 3 % state dependent M = feval(Mfun,t,y,Margs{:}); end rhs = h * feval(odeFcn,t,y,odeArgs{:}) - M * z; if DAE % Account for row scaling. rhs = RowScale .* rhs; end [lastmsg,lastid] = lastwarn(''); warning(warnoff); if isempty(R) % Miter was full, P is the permutation vector del = U \ (L \ rhs(P)); else del = Q * (U \ (L \ (P * (R \ rhs)))); end warning(warnstat); % If no new warnings or a muted warning, restore previous lastwarn. [msg,msgid] = lastwarn; if isempty(msg) || any(strcmp(msgid,warnoffId)) lastwarn(lastmsg,lastid); end z = z + del; y = y + gamma*del; newnrm = norm(del ./ max(wt,abs(y)),inf); if newnrm <= minnrm break; elseif iter == 1 if ~isempty(rate) errit = newnrm * rate / (1 - rate) ; if errit <= 0.1*kappa*rtol break; end else rate = 0; end elseif newnrm > 0.9*oldnrm itfail = 1; break; else rate = max(0.9*rate, newnrm / oldnrm); errit = newnrm * rate / (1 - rate); if errit <= kappa*rtol break; elseif iter == maxiter itfail = 1; break; elseif kappa*rtol < errit*rate^(maxiter-iter) itfail = 1; break; end end oldnrm = newnrm; end