image thumbnail
from Eckart Inertias by Bryan Wong
A suite of MATLAB codes to calculate effective Eckart inertias for internal rotation

PPfun=ppcreate(varargin)
function PPfun=ppcreate(varargin)
%PPCREATE Create 1-D Piecewise Polynomial Function.
% PPfun = PPCREATE(X,Y,Type,P,Tag) creates a function handle PPfun for a
% 1-D piecewise polynomial described by the data X and Y.
% Type is a character string identifying the type of piecewise
% polynomial desired.
% P is an optional array of parameters required for some Types.
% Tag is an optional character string to store with PPfun for
% identification purposes.
%
% Type        Description
% 'pchip'     same as MATLAB PCHIP function, no P required.
% 'spline'    same as MATLAB SPLINE function, no P required.
% 'notaknot'  same as MATLAB SPLINE function, no P required.
% 'extrap'    same as MATLAB SPLINE function, no P required.
% 'natural'   spline, y''=0 at end points, no P required.
% 'parabolic' spline, first and last polys are parabolic, no P required.
% 'clamped'   spline, P is two element vector of slopes y' at end points.
% 'curvature' spline, P is two element vector of y'' at the end points.
% 'hermite'   spline, P is a vector of slopes at all points in X.
%
% PPfun = PPCREATE(PP,Tag) where PP is the 1-D piecewise polynomial
% structure returned by PP = PCHIP(X,Y) or PP = SPLINE(X,Y) creates a
% function handle PPfun containing PP. Tag is an optional string as
% described above.
%
% PPfun2 = PPCREATE(PPfun,Op,P,Tag) peforms the operation specified by the
% string Op on the piecewise polynomial function handle PPfun, returning a
% new function handle PPfun2. P is an optional parameter. Tag is an
% optional string as described above.
%
% Operation    Description
% diff         differentiate the piecewise polynomial
% int          integrate the piecewise polynomial, P is the integration
%                 constant or initial value of the integral.
% tag          store tag string P with function handle.
% inv          inverse interpolation function handle returned, e.g., PPinv,
%                 interpolates the piecewise polynomial, such that
%                 [Xi,Yi]=PPinv(Yi) finds all points Xi where the piecewise
%                 polynomial has the scalar value Yi. If none are found an
%                 empty array is returned. On output Yi=repmat(Yi,size(Xi)).
%                 PPinv('tag') returns the tag assigned to PPinv.
% cut          cut spline apart, P = [Xmin Xmax] defines the range of the
%                 revised piecewise polynomial.
%
%--------------------------------------------------------------------------
% The created piecewise polynomial function handle PPfun has the following
% features:
% Sytax           Description
%
% PPfun(x)        evaluate the function at the points in x.
%
% PPfun('tag')    return Tag string stored in PPfun.
% PPfun('pp')     return MATLAB-defined piecewise polynomial structure.
% PPfun('breaks') return breakpoints X used to create PPfun.
% PPfun('pieces') return number of piecewise polynomials, length(X)-1.
% PPfun('order')  return order of the piecewise polynomial. (4 = cubic)
% PPfun('plot')   plots the piecewise polynomial over its entire range.
%
% See also: SPLINE, PCHIP, MKPP, UNMKPP, PPVAL.

% D.C. Hanselman, University of Maine, Orono, ME 04469
% MasteringMatlab@yahoo.com
% Mastering MATLAB 7
% 2006-03-23

if nargin<1
   error('At Least One Input is Required.')
end
PPData=struct('pp',[],'tag','');

switch class(varargin{1})
case 'struct'                                                % PPCREATE(PP)
   pp=varargin{1};
   if isstruct(pp) && isfield(pp,'form')...
                   && strcmp(pp.form,'pp') && (pp.dim==1)
      PPData.pp=varargin{1};
      if nargin==2 && ischar(varargin{2})
         PPData.tag=varargin{2};
      end
      PPfun=@(x) PPfunction(PPData,x);      
   else
      error('1-D PP Structure Expected.')
   end
   
case 'function_handle'                               % PPCREATE(PPfun,Op,P)
   PPfun=varargin{1};
   pp=PPfun('pp');
   if nargin<2
      error('At Least Two Input Arguments Required.')
   elseif ~ischar(varargin{2})
      error('Second Input Must be an Operation String.')
   end
   switch lower(varargin{2}(1:min(3,length(varargin{2}))))
      
   case 'tag'                                        % tag tag tag
      if nargin~=3 || ~ischar(varargin{3})
         error('Tag String Expected.')
      end
      PPData.tag=varargin{3};
      PPData.pp=pp;
      PPfun=@(x) PPfunction(PPData,x);
      
   case 'dif'                                        % differentiate
      if pp.order==0
         error('Piecewise Polynomial Cannot be Differentiated.')
      end
      pp.coefs=repmat(pp.order-1:-1:1,pp.pieces,1).*pp.coefs(:,1:end-1);
      pp.order=pp.order-1;
      PPData.pp=pp;
      if nargin==3 && ischar(varargin{3})
         PPData.tag=varargin{3};
      else
         PPData.tag='Derivative';
      end
      PPfun=@(x) PPfunction(PPData,x);
      
   case 'int'                                        % integrate
      if nargin==2 || ischar(varargin{3}) || isempty(varargin{3})
         c=0;
      elseif isnumeric(varargin{3}) 
         c=varargin{3}(1);
      else
         error('Numeric Integration Constant Expected.')
      end
      sf=repmat(pp.order:-1:1,pp.pieces,1);
      ico=[pp.coefs./sf zeros(pp.pieces,1)];
      pp.order=pp.order+1;
      dx=diff(pp.breaks(:));
      tmp=ico(:,1);
      for i=2:pp.order % make integral cumulative
         tmp=dx.*tmp + ico(:,i);
      end
      ico(:,end)=cumsum([c;tmp(1:end-1)]);
      pp.coefs=ico;
      PPData.pp=pp;
      if ischar(varargin{nargin})
         PPData.tag=varargin{nargin};
      else
         PPData.tag='Integral';
      end
      PPfun=@(x) PPfunction(PPData,x);
   case 'cut'                                      % cut cut cut
      if nargin<3 || ~isnumeric(varargin{3}) || numel(varargin{3})~=2
         error('P = [Xmin Xmax] Argument Expected.')
      end
      xmin=min(varargin{3});
      xmax=max(varargin{3});
      imin=find(xmin<pp.breaks,1);
      if isempty(imin)
         imin=1;
      else
         imin=max(imin-1,1);
      end
      imax=find(xmax<pp.breaks,1);
      if isempty(imax)
         imax=length(pp.breaks);
      end
      pp.breaks=pp.breaks(imin:imax);
      pp.coefs=pp.coefs(imin:imax-1,:);
      pp.pieces=length(pp.breaks)-1;
      if ischar(varargin{nargin})
         PPData.tag=varargin{nargin};
      else
         PPData.tag='Cut';
      end
      PPData.pp=pp;
      PPfun=@(x) PPfunction(PPData,x);

   case 'inv'                                    % inverse function handle
      PPData.pp=pp;
      if nargin==3 && ischar(varargin{3})
         PPData.tag=varargin{3};
      else
         PPData.tag='InverseInterpolation';
      end
      PPfun=@(y) PPInverse(PPData,y);

   otherwise
      error('Unknown Operation Selected.')
   end
   
case {'single' 'double'}                                % PPCREATE(X,Y,...)
   x=varargin{1};
   if nargin<2
      error('At Least Two Numeric Vectors X and Y Are Required.')
   end
   y=varargin{2};
   if ~isreal(x)
      error('X Must Contain Real Values.')
   end
   [x,idx]=sort(x(:)); % put x in increasing order
   nx=length(x);
   if nx~=numel(y);
      error('X and Y Must Contain the Same Number of Elements.')
   end
   y=reshape(y(idx),size(x));
   if nx<4
      error('At Least 4 Pairs of Data Points are Required.')
   end
   n=nx-1;        % number of pieces
   H=diff(x);     % differences in x
   if any(H==0)
      error('X Values Must be Distinct.')
   end
   D=diff(y)./H;  % slopes
   pptypes={'pchip';'spline';'notaknot';'extrap';...
      'natural';'parabolic';'clamped';'curvature';'hermite'};
   if nargin==2
      pptype='spline';
   elseif ischar(varargin{3}) && ~isempty(varargin{3}) && ...
         any(strncmpi(varargin{3},pptypes,2))
      pptype=pptypes{strncmpi(varargin{3},pptypes,2)};
   else
      error('Valid Character String Type Expected.')
   end
   if nargin>3 && ischar(varargin{end})
      PPData.tag=varargin{end};
   else % default tag is selected Type
      PPData.tag=pptype;
   end
   if any(strncmpi(pptype,{'c','h'},1)) % P required
      if nargin<4 || ischar(varargin{4})
         error('Parameter Vector P Required.')
      end
      P=varargin{4};
   end
   pp=struct('form','pp','breaks',x.',...     % create default PP structure
             'coefs',zeros(n,4),'pieces',n,'order',4,'dim',1);
   pptype=lower(pptype(1:2));
          
   if pptype(1)=='h'                          % 'hermite' requested
      if nargin<4 || ischar(varargin{4}) || ...
            numel(varargin{4})~=nx || ~isnumeric(varargin{4})
         error('Vector of Slopes the Same Size as X Required.')
      end
      dy=varargin{4}(:);
      pp.coefs(:,4)=y(1:end-1); % compute coefficients
      pp.coefs(:,3)=dy(1:end-1);
      pp.coefs(:,2)=(3*D - (2*dy(1:end-1)+dy(2:end)))./H;
      pp.coefs(:,1)=(-2*D + (dy(2:end)+dy(1:end-1)))./(H.*H);
      PPData.pp=pp;
      
      PPfun=@(x) PPfunction(PPData,x);
      
   elseif strcmp(pptype,'pc')                % MATLAB pchip
      PPData.pp=pchip(x,y);
      
   else                                      % spline construction
      
      V=[0;6*diff(D);0];              % right side vector
      B=[0;2*(H(1:n-1)+H(2:n));0];    % diagonal elements
      A=[0;H(2:n)];                   % subdiagonal elements
      C=A;                            % superdiagonal elements
      switch pptype
      case {'sp' 'no' 'ex'}  % MATLAB spline
         B(1)=H(2);
         B(2)=B(2)+H(1)+H(1)*H(1)/H(2);
         B(n)=B(n)+H(n)+H(n)*H(n)/H(n-1);
         B(nx)=H(n-1);
         C(1)=-H(1)-H(2);
         C(2)=C(2)-H(1)*H(1)/H(2);
         C(n)=0;
         A(n)=-H(n-1)-H(n);
         A(n-1)=A(n-1)-H(n)*H(n)/H(n-1);
         V(1)=0;
         V(nx)=0;   
      case 'na' % natural spline
         B(1)=1;
         B(nx)=1;
         C(n)=0;
         A(n)=0;
         V(1)=0;
         V(nx)=0;
      case 'pa' % parabolic spline
         B(1)=1;
         B(2)=B(2)+H(1);
         B(n)=B(n)+H(n);
         B(nx)=-1;
         C(1)=-1;
         C(n)=0;
         A(n)=1;
         V(1)=0;
         V(nx)=0;
      case 'cl' % clamped spline
         B(1)=1;
         B(2)=B(2)-H(1)/2;
         B(n)=B(n)-H(n)/2;
         B(nx)=1;
         C(1)=0.5;
         C(n)=0;
         A(n)=0.5;
         V(1)=3*(D(1)-P(1))/H(1);
         V(2)=V(2)-3*(D(1)-P(1));
         V(n)=V(n)-3*(P(2)-D(n));
         V(nx)=3*(P(2)-D(n))/H(n);
      case 'cu' % curvature specified spline
         B(1)=1;
         B(nx)=1;
         C(n)=0;
         A(n)=0;
         V(1)=P(1);
         V(2)=V(2)-H(1)*P(1);
         V(n)=V(n)-H(n)*P(2);
         V(nx)=P(2);
      end
      i=[2:nx 1:nx  1:n ];                          % row indices for A;B;C
      j=[1:n  1:nx  2:nx];                       % column indices for A;B;C
      ABC=sparse(i,j,[A;B;C],nx,nx,3*(nx+1));        % create sparse matrix

      switch pptype
      case {'sp' 'no' 'ex'}                        % poke in extra elements
         ABC(1,3)=H(1);
         ABC(nx,n-1)=H(n);
      end
      sflag=spparms('autommd');
      spparms('autommd',0);   % no reordering required
      m=ABC\V;                % find solution
      spparms('autommd',sflag);

      pp.coefs(:,4)=y(1:n); 
      pp.coefs(:,3)=D-H.*(2*m(1:n)+m(2:nx))/6;
      pp.coefs(:,2)=m(1:n)/2;
      pp.coefs(:,1)=diff(m)./(6*H);
      PPData.pp=pp;
      
      PPfun=@(x) PPfunction(PPData,x);
   end

otherwise
	 error('Unknown First Argument.')      
end
%--------------------------------------------------------------------------
function [xi,yi]=PPInverse(PPData,y)
% function called for inverse interpolation

if ischar(y) && strcmpi(y,'tag')
   xi=PPData.tag;
   return
end
if ~isnumeric(y) || numel(y)~=1
   error('Scalar Numerical Value Expected.')
end
pp=PPData.pp;
dxbr=diff(pp.breaks);
tol=100*eps;
xi=[];
pp.coefs(:,end)=pp.coefs(:,end)-y;  % shift all polys by y
for k=1:pp.pieces
   p=pp.coefs(k,:);     % k-th poly to investigate
   inz=find(p);         % index of nonzero elements of poly
   fnz=inz(1);          % first nonzero element
   lnz=inz(end);        % last nonzero element

   p=p(fnz+1:lnz)/p(fnz);        % strip leading,trailing zeros, make monic
   r=zeros(1,pp.order-lnz);      % roots at zero

   if (lnz-fnz)>1                   % add nonzero roots
      a=diag(ones(1,lnz-fnz-1),-1); % form companion matrix
      a(1,:)=-p;
      r = [r eig(a).'];             % find eignevalues to get roots
   end

   i=find(abs(imag(r))<tol & ... % find real roots with
          real(r)>=0 & ...       % nonnegative real parts that are
          real(r)<dxbr(k));      % less than the next breakpoint
   if ~isempty(i)
      xi=[xi; pp.breaks(k)+r(i)];
   end
end
if nargout==2
   yi=repmat(y,size(xi));
end
%--------------------------------------------------------------------------
function y=PPfunction(PPData,xx)
% function called when PPfun(x) is called.
pp=PPData.pp;
if ischar(xx)                                              % PPfun('string')
   switch lower(xx(1:2))
      case 'ta'
         y=PPData.tag;
      case 'pp'
         y=pp;
      case 'br'
         y=pp.breaks;
      case 'pi'
         y=pp.pieces;
      case 'or'
         y=pp.order;
      case 'pl'
         x=reshape(pp.breaks,1,[]);
         xlen=length(x);
         n=max(2,fix(500/xlen));
         xi=repmat(x(1:end-1),n,1);
         d=repmat((0:n-1)'/n,1,xlen-1);
         dx=repmat(diff(x),n,1);
         xi=xi+d.*dx;
         xi=[xi(:); x(xlen)];
         plot(xi,ppval(pp,xi))
      otherwise
         error('Invalid String Input.')
   end
elseif isnumeric(xx)                                             % PPfun(x)
   if numel(xx)==1   % scalar input
      idx=find(xx>=pp.breaks);
      if isempty(idx)               % extrapolate if necessary
         idx=1;
      elseif idx(end)>pp.pieces
         idx=pp.pieces;
      end
      xs=xx-pp.breaks(idx(end));    % local coordinates
      c=pp.coefs(idx(end),:);       % local polynomial
      if pp.order==4                % quick eval for cubic spline
         y=((c(1)*xs + c(2))*xs + c(3))*xs +c(4);
      else
         y=c(1);
         for i=2:pp.order           % apply Horner's method
            y=xs.*y+c(i);
         end
      end
   else              % array input
      xbr=pp.breaks;
      c=pp.coefs.';
      [rx,cx] = size(xx);
      xs=xx(:).';
      tosort=false;
      if any(diff(xs)<0)
         tosort=true;
         [xs,ix]=sort(xs);
      end
      % for each data point, compute its breakpoint interval
      [idx,idx]=histc(xs,xbr);
      idx(xs<xbr(1)|~isfinite(xs))=1; % extrapolate using first
      idx(xs>=xbr(end))=pp.pieces;    % and last polynomial

      xs=xs-xbr(idx);                 % local coordinates
      if pp.order==4                  % quick eval for cubic spline
         y=((c(1,idx).*xs + c(2,idx)).*xs + c(3,idx)).*xs +c(4,idx);
      else
         y=c(1,idx);
         for i=2:pp.order             % apply Horner's method
            y=xs.*y+c(i,idx);
         end
      end
      if tosort
         y(ix)=y;
      end
      y=reshape(y,rx,cx);
   end
else
   error('Numeric or String Input Expected.')
end
%--------------------------------------------------------------------------

Contact us at files@mathworks.com