%  DYNAMIC SIMULATION

%  FILENAME: DYNSIM.M
%  DATE: August 24, 1993
%  VERSION: 01
%  COPYRIGHT 1993 M.Gretzinger & T.Marlin
%            McMaster University   

% This m-file is called from STABLE.M. It sets up a menu to enable the user
%       to specify the end time for integration and magnitude of the
%       time step and to execute the dynamic simulation of the system, 
%       using the parameters specified by the process and tuning data.

while 1
  clc
  disp('*************************************************************')
  disp('*                 DYNAMIC SIMULATION                        *')
  disp('*                                                           *')
  disp('*************************************************************')
  disp('')
  disp('SELECT THE APPROPRIATE MENU ITEM')
  disp('')
  disp('MODIFY...')
  disp('                                      PRESENT VALUES')
  fprintf('1) End time for integration           %4.2f\n',tend)
  fprintf('2) Time step                          %4.3f\n',deltat)
  if change == 's'
      disp('3) Select Set Point or Disturbance    s')
  else
      disp('3) Select Set Point or Disturbance    d')
  end

  disp('4) Execute dynamic simulation')
  disp('5) Return to main menu')
  disp('')
        
  m7 = input('Enter the desired selection:   ');
  
  if isempty(m7), break, end
      
  m7 = round(m7);
  if (m7<1) | (m7>4)
      disp('')
      disp('Not a valid selection')
      disp('')
      disp('Press ENTER to continue')
      pause;
  end    
  
  if m7 == 1
      tend = input('Enter value for end time of integration:  ') ;
      if isempty(tend)
          tend = 0 ;
      end
  
  elseif m7 == 2
      deltat = input('Enter value for time step:  ') ;
      if isempty(deltat)
          deltat = 0 ;
      end

  elseif m7 == 3
      disp('For unit change in set point, enter the letter:    s')
      disp('For unit change in disturbance, enter the letter:  d')
      disp('')
      junkchange = input('Selection:  ','s') ;
      if junkchange ~= 'd' & junkchange ~= 's'
         junkchange = change;
         disp ('illegal entry, please reenter')
         disp ('press enter to continue')
         pause
      end
      if isempty(junkchange)
          junkchange = 'd' ;
      end
      change = junkchange;

                
  elseif m7 == 4      % execute dynamic simulation
      
      gamma = round(theta/deltat); 
      if (tend/deltat+gamma+2) > 7000
          deltat = tend/(7000-gamma) ;
          clc
          disp('Your choice of tend and deltat may lead to errors by')
          disp('surpassing matrix size and memory size limits. ')
          fprintf('To avoid this, deltat has been adjusted to %4.2f\n',deltat)
          disp('')
          disp('Press any key to continue')
          pause
      end

      clc
      if change == 'd'
        a=input('Are disturbance dynamics same as process dynamics?  [y/n]  ','s');
        if a ~= 'n' & a ~= 'y'
           a = 'y';
        end

        if a == 'y'
            thetad = theta; taulead_d = taulead; 
            taud1 = tau1; taud2 = tau2; taud3 = tau3; xid = xi;
        elseif a == 'n'
            thetad = 0; taulead_d = 0; 
            taud1 = taudist;  taud2 = 0; taud3 = 0; xid = 0;
        end
      elseif change == 's'
        thetad=0;  taulead_d=0;  taud1=0;  taud2=0; taud3=0; xid=0;
      end

      clc
      disp('Creating plot.....')
      disp('Press ENTER after viewing plot')

      % Initialize variables

      gamma = round(theta/deltat);        % round off to integer
      n = gamma + 2 + 1;
      t = 0;
      time = t  ;

      cv1 = zeros(1,tend/deltat+gamma+2) ;
      cv2 = cv1;  cv3 = cv2;  cv4 = cv3;  cv4b = cv4;  cv5 = cv4b;
      z = cv5;  e = z;  sp = e;  mv = e ;
      dv1 = cv1;  dv2 = cv2;  dv3 =cv3;  dv4 = cv4;  dv4b = cv4b;  dv5 = cv5;
      zd = z;  dist = sp;  cv = cv1;
      
      % If block to calculate values needed for second order 
      % process equations
      if tau3 ~= 0
          a2 = 2*xi/tau3 ;
          a1 = 1/tau3^2 ;
          a0 = a1 ;
          
          % When damping factor is >= 1, this block calculate tau4 and
          % tau5, which are then used as 2 lags in the process.
          if xi >= 1
             s = roots([tau3^2  2*xi*tau3  1]) ;
             tau4 = -1/s(1) ;
             tau5 = -1/s(2) ;
          end
      end

      % While loop which calculates the process, disturbance and controller
      % equations numerically, one element at a time, from time = 0 to 
      % time = tend, in increments of deltat.
      
      while t < tend

         % if block to determine value of set point or disturbance. 
         % At 5% of tend, a unit step in either the set point or the
         % disturbance occurs, as specified by the user.
         
         if t < .05*tend
             sp(n) = 0. ;  dist(n) = 0;
         elseif t >= .05*tend 
             if change == 'd'
                 dist(n) = 1. ;  sp(n) = 0. ;
             elseif change == 's'
                 dist(n) = 0. ;  sp(n) = 1. ;
             end
         end

         %PROCESS EQUATIONS
         %  GAIN
         cv1(n) = kp*mv(n-1) ;
         
         %  DEADTIME
         cv2(n) = cv1(n-gamma) ;

         %  LEAD/LAG
         if (tau1 == 0) & (taulead == 0)
            cv3(n) = cv2(n) ;
         else
            cv3(n) = (tau1*cv3(n-1)/deltat + (taulead/deltat+1)*cv2(n)...
               - taulead*cv2(n-1)/deltat) / (tau1/deltat+1) ;
         end

         %  LAG
         if tau2 == 0
            cv4(n) = cv3(n);
         else
            cv4(n) = (1-exp(-deltat/tau2))*cv3(n)... 
                      + (exp(-deltat/tau2))*cv4(n-1) ;
         end
      
         %  SECOND ORDER
         if tau3 == 0
            cv5(n) = cv4(n);
         else
            if xi < 1
               cv5(n) = cv5(n-1) + deltat*z(n-1) ;
               z(n) = z(n-1) + deltat*(-a2*z(n-1) - a1*cv5(n-1) + a0*cv4(n-1));

            elseif xi >= 1    % use tau4 and tau5 calculated above (line 77-83)
                              % and solve as 2 more lags

               cv4b(n) = (1-exp(-deltat/tau4))*cv4(n)... 
                         + (exp(-deltat/tau4))*cv4b(n-1) ;
               cv5(n) = (1-exp(-deltat/tau5))*cv4b(n) ...
                        + (exp(-deltat/tau5))*cv5(n-1) ;
            end
         end


         %  DISTURBANCE EQUATIONS


         %  GAIN
         dv1(n) = kpd*dist(n) ;
         
         %  DEADTIME
         dv2(n) = dv1(n-gamma) ;

         %  LEAD/LAG
         if (taud1 == 0) & (taulead_d == 0)
            dv3(n) = dv2(n) ;
         else
            dv3(n) = (taud1*dv3(n-1)/deltat + (taulead_d/deltat+1)*dv2(n)...
               - taulead_d*dv2(n-1)/deltat) / (taud1/deltat+1) ;
         end

         %  LAG
         if taud2 == 0
            dv4(n) = dv3(n);
         else
            dv4(n) = (1-exp(-deltat/taud2))*dv3(n)... 
                      + (exp(-deltat/taud2))*dv4(n-1) ;
         end
      
         %  SECOND ORDER
         if taud3 == 0
            dv5(n) = dv4(n);
         else
            if xid < 1
               dv5(n) = dv5(n-1) + deltat*zd(n-1) ;
               zd(n) = zd(n-1) + deltat*(-a2*zd(n-1) - a1*dv5(n-1) + a0*dv4(n-1));

            elseif xid >= 1    % use tau4 and tau5 calculated above (line 77-83)
                              % and solve as 2 more lags

               dv4b(n) = (1-exp(-deltat/tau4))*dv4(n)... 
                         + (exp(-deltat/tau4))*dv4b(n-1) ;
               dv5(n) = (1-exp(-deltat/tau5))*dv4b(n) ...
                        + (exp(-deltat/tau5))*dv5(n-1) ;
            end
         end

         cv(n) = cv5(n) + dv5(n) ;   % Add output from process and disturbance
                                     % to send to controller

         %  CONTROLLER EQUATIONS
         
         if kc == 0
            mv(n) = mv(n-1) ;
         else
            e(n) = sp(n) - cv(n) ;
            % If block to ensure no division by zero errors when no integral
            % mode is present in controller
            if ti == 0
                deltamv = kc*(e(n) - e(n-1) - td*(cv(n) - 2*cv(n-1) ...
                              + cv(n-2))/deltat) ;
            else   
                deltamv = kc*(e(n) - e(n-1) + deltat*e(n)/ti ...
                              - td*(cv(n) - 2*cv(n-1) + cv(n-2))/deltat) ;
            end
            mv(n) = mv(n-1) + deltamv ;
         end

        % Update t and n. Store next t value in time, the vector of times
        % at which each calculation is carried out.
         t = t + deltat ;
         time = [time t] ;
         n = n+1 ;

         
      end       % while t < tend

  % Calculate IAE (Integral of the Absolute value of the Error)
  IAE = deltat * sum(abs(e)) ;
 
  % Plot set point (dashed green line) and controlled variable (solid red
  % line) versus time.
  clf ; axis('auto')
  subplot(211),plot(time,sp(1,round(theta/deltat+2):length(sp)),'--g', time,cv(1,round(theta/deltat+2):length(cv)))
  title(['DYNAMIC SIMULATION   (IAE = ',num2str(IAE),')'])
  xlabel('Time')
  ylabel('Controlled Variable')

  % Plot manipulated variable versus time
  subplot(212),plot(time,mv(1,round(theta/deltat+2):length(mv)))
  xlabel('Time')
  ylabel('Manipulated Variable')
  
  figure(1)
  pause
  close all

  clear cv1 cv2  cv3 cv4 cv4b cv5 z e sp mv dv1 dv2 dv3 dv4 dv4b dv5 
  clear zd dist cv time
  
  else
      break
  end        % if m7
  
end    % while 1 (line 12)


