image thumbnail
from Kirchhoff Vortex Contour Dynamics Simulation by Travis Mitchell
Contour dynamics simulation of an elliptical vortex in 2D inviscid and incompressible flow

Kirchhoff_CD_Simulation.m
%  KIRCHHOFF_CD_SIMULATION.M      Kirchhoff Ellipse Contour Dynamics Simulation Program  
%
%  Version: 1.0, 19 March 2008
%  Authors:  Travis B. Mitchell and Louis F. Rossi
%  Email:   travis*b*mitchell=gmail*com, rossi=math*udel*edu
%  Real_email = regexprep(Email,{'=','*'},{'@','.'})
%
%  This code was used in 'The Evolution of Kirchhoff Elliptic Vortices' by T.B. Mitchell and L. F. Rossi,
%  which will appear in Physics of Fluids, April 2008.  A preprint is included in the readme file.
%
%  The routines 1) create a perturbed Kirchhoff vortex, 2) follow its evolution forward in time using a
%  contour dynamics algorithm, then 3) fit the contours to modes and write the results.  
%
%  INITIALIZATION: Kirchhoff ellipse base state is specified (a, b, zeta), then one mode 
%  is applied.  Depending on i_pert_control, seed mode can be specified as per equations 
%  (15a), (15b), or you can specify the amplitude of a purely growing or decaying eigenmode 
%  where the phase will be set as per equation (21).
%
%  EVOLUTION: Most accurate option is to run to desired time, however if contour points get too 
%  close or too far, simulation will hang.  So it is often useful to break the run into smaller
%  steps to monitor things, and the run can later be done in one go once the largest time of 
%  interest is known.  Options are set with i_integrator_control.  
%
%  FITTING:  Two fitting approaches, described in section IIE, are implemented.  The 'Linear'
%  one is faster since it fits to fewer quantities.  The 'Nonlinear' approach can follow the mode
%  evolution to larger amplitude.  Options are set with i_fit_control.

%               SUPPLEMENTAL MATLAB ROUTINES USED
%
%   ControlString.m             v1.0    Generates diagnostic string
%   ScanParameters.m            v1.0    Allows variation of parameter for scan
%   CalcPertAreaChange.m        v1.0    Area diagnostic
%   FitLoveEnd.m                v1.0    Trims tips of large amplitude seed, see IVC of paper
%   dZdt.m                      v1.0    Calculates velocity, implementation of Eq. 28 of paper
%   PiecewiseIntegration.m      v1.0    Breaks simulation run into time steps
%   CalcLinEvolution.m          v1.0    Calculates the predicted linear solution of a perturbed Kirchhoff vortex, see IIC of paper
%   LoadExpData.m               v1.0    Loads external file of experimental data
%   LoveLinearFit.m             v1.0    Fits Love modes, minimizes distance^2 along perpendicular direction, see IIE of paper
%   LoveNonlinearFit.m          v1.0    Fits Love modes, minimizes distance^2 between contour and ellipse 
%   FitString.m                 v1.0    Generates string with results of a fit
%   ReadAndPlotResults.m        v1.0    Sample post-simulation routine to open and plot fit results
%
%               EXTERNAL MATLAB ROUTINES USED
%
%   ellipsefit.m        Ellipse fitter  2005-08-09  D.C. Hanselman
%                       www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=7012
%   curvspace.m         Generates evenly spaced points along curve  22 Mar 2005 Yo Fukushima  
%                       www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=7233
%   intersections.m     Computes intersections where 2 curves meet  v1.10   Douglas M. Schwarz
%                       http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=11837
%   nearestneighbor.m   Calculates nearest point    2006 Richard Brown
%                       www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=12574
%   LoadAscii.m         Loads an ascii file, external routine of unknown origin

%                       BASE STATE PARAMETERS
aspect_ratio            = 3.5;              % convention is a/b: semi-major_axis/semi-minor_axis  
semi_major_axis         = 1.0;              % a=1 is convention used in paper, choice impacts perturbation specifications
zeta                    = 1.;               % Note: neg. vorticity confuses fitting routines b/c CCW ellipse rotation expected
n_ellipse_points        = 500;              % points along circumference

%                       PERTURBATION PARAMETERS
m_1                     = 3;                % This specifies the m-number of the seeded perturbation
m_2                     = 4;                % Fit control.  If m_2=0, will fit to only the m_1 mode
m_3                     = 0;                % Fit control.  If m_3=0, will fit to only m_1 and m_2 modes
                                            % Nonlinear fit only: if m_3<0, allow a and b to vary 
n_vary                  = 1 ;               % Can loop to scan over quantities: e.g. perturbation phase, aspect ratio

i_pert_control          = 1;                % 0 to not apply any perturbation
                                            % 1 to specify in form of Eq. 15b (alpha_m, beta_m)
                                            % 2 to specify in form of Eq. 16 (alpha_m, beta_m, linearized)
                                            % 3 to specify in form of Eq. 15a (A_m, B_m)
                                            % CASE 1 and 2 parameters
    alpha_m                 = 2.e-3;        % mode amplitude, 1E-5 value gives good agreement with linear theory
    beta_m                  = 0.0;          % specified mode angle in radians
    i_beta_control          = 0;            % 0 to use specified beta_m 
                                            % 1 to use angle of growing unstable eigenfunction 
                                            % 2 to use angle of decaying unstable eigenfunction 
                                            % CASE 3 parameters
    A_m                     = 5.e-3;        % mode amplitude
    B_m                     = 0.0;          % mode amplitude

i_pert_end_control          = 0;            % 0 to do nothing
                                            % 1 to trim end of perturbed initial condition (see section IVC of paper)    
    
%                       ITEGRATOR CONTROL PARAMETERS
i_integrator_control        = 1;            % 0 to not run - use when integration has been done before and wish to fit
                                            % 1 to run in one time block - most accurate, but will hang if contour too perturbed 
                                            % 2 to break run into time steps - useful if hanging occurs
                                            % 3 to create 'fake data' based on linear theory predictions
                                            % 4 to read in 'real data' from experiments, a and b are fitted in this case
                                            % GENERAL PARAMETERS                                
    sim_end_time            = 10.0;         % simulation run time
    frame_steps             = 0.5;          % gives time steps at which simulation values are written
    RelTolValue             = 1.E-8;        % Integrator tolerance
    AbsTolValue             = 1.E-12;       % Numerical noise is not reduced below these value
                                            % CASE 2 PARAMETERS            
    n_pre_evolve            = 0.0;          % Set to non-zero to integrate that amount of time prior to beginning segmented integration
    sim_steps               = 1.;           % simulation time steps for segmented integration
    safe_max_del_r_mult     = 10.;          % when inter-particle spacing increases by this, halt simulation (filamentation)
    safe_max_min_r_mult     = 1.20;         % when min r increases by this, halt (fissioning)
    safe_max_max_r          = 1.40;         % when max r reaches this, halt simulation (filamentation)
    safe_min_r              = .002;         % when particle radius below this, halt (fissioning)
    safe_mult_time          = 10;           % halt if time required for step too large
    i_resample              = 0;            % equalizes spacing between nodes
    t_resample_start        = 10.0;         % time to begin resampling node spacing
    t_resample              = 2.0;          % resample every t_resample times, once begun

                                            % CASE 4 PARAMETERS
    exp_scale_factor        = 455.;         % When fitting experimental data, set to normalize major axis near to 1 
    exp_head                = 'I:\Andor\2006\1024\FZ\FZ_';      % where contours.dat and times.dat files can be read
    exp_T_R                 = 10.9;         % experimental value of rotation period

%                       FITTING CONTROL PARAMETERS
i_fit_control           = 1;                % 0 to do no fitting
                                            % 1 for linear fitting to 1, 2 or 3 modes
                                            % 2 for nonlinear fitting to 2 modes

i_fit_range_control     = 1;                % 0 to fit one point at n_fit_param
                                            % 1 to fit everything
                                            % 2 to begin from end minus n_fit_param
                                            % 3 start at beginning, do n_fit_param fits 
                                        
    n_fit_param             = 1;            % quantity used to specify fit range
    i_fit_step              = 1;            % steps between fits

%                       OUTPUT OF RESULTS
i_write_fit_output      = 1;                % 1 to write mode fit output
file_head               = '0319\FZ_';       % directory to write fit results to
drive_string            = 'I:\Andor\2008\';

%                       DIAGNOSTICS 
i_calc_pert_area_change = 0;            % 1 to calculate and display change in area from perturbation
i_plot_final_state      = 0;            % 1 to see end state after integration
i_plot_perp_matrix      = 0;            % 1 to see perp matrix, if nonlinear fit instead serves as flag to print int_out_vec
i_plot_fit              = 1;            % 1 to plot fit at each time
i_plot_amp_phase        = 0;            % 1 to plot amp and phase versus time for entire run
i_plot_safe_calc        = 1;            % 1 to plot after each step if doing piecewise integration
  
%       INITIALIZE OUTPUT MATRIX
n_time_points_sim       = double(int32(sim_end_time / frame_steps + 1));  
if i_integrator_control  > 0                                            % re-initialize if doing new simulation run     
    if i_integrator_control == 4                                        % with experimental data, open to determine length
        fid = fopen([exp_head,'contours.dat'], 'r'); 
        x = fscanf(fid, '%f ', [3000  inf]);  
        fclose(fid);
        n_time_points_sim = size(x,2) / 2 ;
    end
    out_matrix                  = zeros(14,n_vary,n_time_points_sim);
%  t,   t/T_R,  a,  b,  phi,    alpha_1,    beta_1, alpha_2,    beta_2, alpha_3,    beta_3, min_r,  max_r,  chi     out_matrix
%  1,   2,      3,  4,  5,      6,          7,      8,          9,      10,         11,     12,     13,     14    
end

%       START OF SIMULATION      
warning('off','MATLAB:divideByZero')
ControlString(i_integrator_control, i_pert_control, i_beta_control, i_pert_end_control, i_fit_control, m_2, m_3)      % Print run information
s = linspace(0,2*pi,n_ellipse_points+1);
s = s(1:n_ellipse_points);

for i_vary = 1:1:n_vary                 %  FOR LOOP OVER VARIED QUANTITIES
     
    %    *******************  CALCULATE UNPERTURBED ELLIPSE PROPERTIES   **********************************  
    
    dummy = ScanParameters(n_vary);             % can use n_vary loop to vary a parameter for a scan, not used       
    if n_vary == 1                              
        aspect_ratio = aspect_ratio;
    else
        aspect_ratio = aspect_ratio %+ (n_vary - 1) * 0.1;   for example
    end
    
    a = semi_major_axis;
    b = semi_major_axis / aspect_ratio;
    c = (a^2 - b^2)^0.5;
    omega           = zeta * a*b/(a+b)^2;                       % Eq. 12, Kirchhoff expression for ellipse rotation frequency
    T_R             = 2 * pi / omega;            
    u_0 = acosh(a/c);                                           % Use (u,v) for cylindrical elliptical coordinate, rather than (psi,eta)
    h_0_squared = 1. ./ (b^2*cos(s).^2 + a^2.*sin(s).^2);       % This quantity is 1 / Jacobian, see Eq. 14, also Love (1893)

    gamma = aspect_ratio;                                           % Linear theory predictions, see Love (1893) and Guo et al. (2004)
    mu_m_plus = zeta / 2 * ( ((gamma - 1)/(gamma + 1))^m_1 + (2 * m_1 * gamma / (gamma + 1)^2 - 1) );   % Eq. 19a
    mu_m_neg  = zeta / 2 * ( ((gamma - 1)/(gamma + 1))^m_1 - (2 * m_1 * gamma / (gamma + 1)^2 - 1) );   % Eq. 19b
    lambda_m = sqrt( abs(mu_m_plus * mu_m_neg));                    % growth rate / oscillation frequency, real, equivalent to Eq. 17
    beta_m_plus = (-1) * atan(sqrt(mu_m_neg / mu_m_plus)) / m_1;    % growing eigenfunction phase, Eq. 21a
    beta_m_neg  = atan(sqrt(mu_m_neg / mu_m_plus)) / m_1;           % decaying eigenfunction phase, Eq. 21b
                 
%   **************  CREATE PERTURBED INITIAL CONDITION, LOAD INTO Z0_START   *************************** 
    switch i_pert_control                                            
        case 0                                                      % no pert, but add noise to avoid fitting singularity problem
            u_pert      = rand(size(s)) * 1.E-12; alpha_m_0 = 0; beta_m_0 = 0;                        
            Z0_Love     = c * cosh(u_0 + u_pert) .* cos(s) + i * c * sinh(u_0 + u_pert ) .* sin(s);
            
        case 1                                                      % Eq. 15b form of perturbation
            switch i_beta_control
                case 0
                    u_pert  = alpha_m * cos(m_1*(s-beta_m)) .* h_0_squared; beta_m_0 = beta_m;
                case 1
                    u_pert  = alpha_m * cos(m_1*(s-beta_m_plus)) .* h_0_squared; beta_m_0 = beta_m_plus;
                case 2
                    u_pert  = alpha_m * cos(m_1*(s-beta_m_neg)) .* h_0_squared; beta_m_0 = beta_m_neg;
            end
            alpha_m_0       = alpha_m;
            Z0_Love         = c * cosh(u_0 + u_pert) .* cos(s) + i * c * sinh(u_0 + u_pert ) .* sin(s);
            
        case 2                                                      % Eq. 16 form of perturbation (linearized)
            switch i_beta_control
                case 0
                    Z0_Love     = a*cos(s) + b*i*sin(s) + alpha_m*(b*cos(s) + a*i*sin(s)).* cos(m_1*(s-beta_m)) .* h_0_squared; beta_m_0 = beta_m;
                case 1
                    Z0_Love     = a*cos(s) + b*i*sin(s) + alpha_m*(b*cos(s) + a*i*sin(s)).* cos(m_1*(s-beta_m_plus)) .* h_0_squared; beta_m_0 = beta_m_plus;
                case 2
                    Z0_Love     = a*cos(s) + b*i*sin(s) + alpha_m*(b*cos(s) + a*i*sin(s)).* cos(m_1*(s-beta_m_neg)) .* h_0_squared; beta_m_0 = beta_m_neg;
            end
            alpha_m_0       = alpha_m;
            
        case 3                                                      % Eq. 15a form of perturbation
            u_pert = (A_m * cos(m_1*(s)) + B_m * sin(m_1*(s))) .* h_0_squared;
            alpha_m_0 = sqrt(A_m^2 + B_m^2);  beta_m_0 = atan(B_m / A_m) / m_1;            
            Z0_Love = c * cosh(u_0 + u_pert) .* cos(s) + i * c * sinh(u_0 + u_pert ) .* sin(s);            
    end

    Z0_Love = Z0_Love.';
    
    if i_pert_end_control == 0                                  % if desired, trim end of perturbation as per section IVC of paper
        Z0_start = Z0_Love;
    else                                                            
        Z0_Love_trim =FitLoveEnd(Z0_Love, m_1, pi/8, pi/4, 0);      
        Z0_start = Z0_Love_trim;
    end
    
    if i_calc_pert_area_change == 1; del_area = CalcPertAreaChange(a,b,s,Z0_start); end;        % print area diagnostic

%   ********************************  PERFORM INTEGRATION    ***************************   
    options = odeset('RelTol',RelTolValue,'AbsTol',AbsTolValue);
    switch i_integrator_control                                            
        case 0                                                      % do no integration
        case 1
            int_st = 'Single step integration.';
            disp(['Begin integration with end time: ', num2str(sim_end_time)]);  tic                                                      
            [T Y_Love]              = ode113(@(t,Z) dZdt(Z,s,zeta),[0:frame_steps:sim_end_time],Z0_start,options); toc
        
        case 2                                  % integrate in steps, determine safe halt time due to filamentation/fusion      
            if n_pre_evolve ~= 0
                disp(['Begin pre_evolution integration with end time: ', num2str(n_pre_evolve)]); tic
                [T Y_Love] = ode113(@(t,Z) dZdt(Z,s,zeta),[0:frame_steps:n_pre_evolve],Z0_start,options); toc
                Z0_start = Y_Love(end,:);
            end       
            in_vec = [RelTolValue, AbsTolValue, a, b, zeta, m_1, alpha_m_0, beta_m_0, sim_end_time, sim_steps, frame_steps];
            in_vec = [in_vec, safe_max_del_r_mult, safe_max_min_r_mult, safe_max_max_r, safe_mult_time, safe_min_r];
            in_vec = [in_vec, i_resample, t_resample, t_resample_start, i_plot_safe_calc ];
            in_vec = [in_vec, n_ellipse_points, n_time_points_sim, Z0_start'];
            out_vec = PiecewiseIntegration(in_vec); 
            T = out_vec(:,1) + n_pre_evolve;
            Y_Love = out_vec(:,2:size(out_vec,2));
            
        case 3                                                          % create 'fake data' using predictions of linear theory
            switch i_pert_control
                case 0                                                  % no pert, make non-zero to avoid fitting singularity
                    A_m = 1.E-13; B_m = 1.E-13;
                case 1                                                  % convert alpha_m, beta_m to A_m and B_m
                    switch i_beta_control
                        case 0
                            beta_m_0 = beta_m;
                        case 1
                            beta_m_0 = beta_m_plus;
                        case 2
                            beta_m_0 = beta_m_neg;
                    end
                    A_m = sqrt( alpha_m^2 / ( 1 + tan(m_1 * beta_m_0)^2));
                    B_m = A_m * tan(m_1 * beta_m_0);
                case 2
                    disp('change i_pert control (linearized perturbation not a valid input for linear theory solution)')
                case 3                                                                              % use the A_m and B_m specified                    
            end
            in_vec = [lambda_m, mu_m_plus, mu_m_neg, m_1, sim_end_time, frame_steps, a, b, zeta, n_ellipse_points, A_m, B_m];   
            out_vec = CalcLinEvolution(in_vec);            
            T = out_vec(:,1);                                                                                               
            Y_Love = out_vec(:,2:size(out_vec,2));
            
        case 4                                                          % load data from experiment
            S1.exp_head          = exp_head;
            S1.exp_scale_factor  = exp_scale_factor;
            S1.n_ellipse_points  = n_ellipse_points;
            tic; out_vec = LoadExpData(S1); toc;
            T = out_vec(:,1); 
            Y_Love = out_vec(:,2:size(out_vec,2));

    end                                 % i_integrator_control switch
    
    if i_plot_final_state; plot(real(Y_Love(end,:)),imag(Y_Love(end,:))); axis equal; end;         
    
    n_time_points_fit       = length(T);        % may be different than n_time_points_sim
    out_matrix(1,i_vary,1:length(T)) = T; 
    if i_integrator_control ~= 4;               % time_vec is time normalized by rotation period, length may be less than n_time_points_sim
        out_matrix(2,i_vary,1:length(T)) = T / T_R; 
    else;  
        out_matrix(2,i_vary,1:length(T)) = T / exp_T_R;                 % with experiment, rotation period separately measured
    end 
         
%  ********************************  FITTING   ******************************  
    if i_fit_control > 0 
        switch i_fit_range_control                                          % set range of fit
            case 0
                i_fit_start = n_time_points_fit - n_fit_param;
                i_fit_end   = i_fit_start;
            case 1
                i_fit_start = 1;
                size_vec = size(Y_Love);
                i_fit_end = size_vec(1);
            case 2
                i_fit_start = n_time_points_fit - n_fit_param;
                i_fit_end = n_time_points_fit;
            case 3
                i_fit_start = 1;
                i_fit_end = n_fit_param;
        end

        for i_fit = i_fit_start:i_fit_step:i_fit_end                        % loop over fitting of each point

            x_Love = real(Y_Love(i_fit,:).');                               % load input vector for fit
            y_Love = imag(Y_Love(i_fit,:).');            
            fit_in_vec(1)               = b;
            fit_in_vec(2)               = m_1;
            fit_in_vec(3)               = m_2;
            fit_in_vec(4)               = m_3;
            fit_in_vec(5)               = a;
            fit_in_vec(6)               = n_ellipse_points;
            fit_in_vec(8)               = i_plot_perp_matrix;
            fit_in_vec(10)              = i_plot_fit;  
            if i_fit == i_fit_start 
                fit_in_vec(7)           = 1;          % i_force_perp_matrix_creation = 1
                fit_in_vec(9)           = 0;          % i_fit_analysis_prev_use = 0
            else
                fit_in_vec(7)           = 0;
                fit_in_vec(9)           = 1;        
            end

            switch i_fit_control               % perform selected type of fitting
                case 0                                                                      % do nothing
                case 1                                                                      % do linear fit, up to 3 modes
                    int_out_vec             =   LoveLinearFit(x_Love,y_Love, fit_in_vec);
                case 2                                                                      % do nonlinear fit, up to 2 modes            
                    int_out_vec             =   LoveNonLinearFit(x_Love,y_Love, fit_in_vec);
            end         

%  ********************************  LOAD AND DISPLAY FIT OUTPUT   ******************************              
            
    %  t,   t/T_R,  a,  b,  phi,    alpha_1,    beta_1, alpha_2,    beta_2, alpha_3,    beta_3, min_r,  max_r,  chi             out_matrix
    %  1,   2,      3,  4,  5,      6,          7,      8,          9,      10,         11,     12,     13,     14 
            out_matrix(6,i_vary,i_fit)          = int_out_vec(1);                             % load results into out_matrix
            out_matrix(7,i_vary,i_fit)          = int_out_vec(2);
            out_matrix(8,i_vary,i_fit)          = int_out_vec(3);
            out_matrix(9,i_vary,i_fit)          = int_out_vec(4);
            out_matrix(5,i_vary,i_fit)          = int_out_vec(7);
            out_matrix(12,i_vary,i_fit)         = int_out_vec(8);
            out_matrix(13,i_vary,i_fit)         = int_out_vec(9);
            out_matrix(14,i_vary,i_fit)         = int_out_vec(10);
            if m_3 >= 0 
                out_matrix(10,i_vary,i_fit)         = int_out_vec(5);
                out_matrix(11,i_vary,i_fit)         = int_out_vec(6);
                out_matrix(4,i_vary,i_fit)          = b;
                out_matrix(3,i_vary,i_fit)          = a;      
            else
                out_matrix(3,i_vary,i_fit)          = int_out_vec(5);       % nonlinear fitter, a and b can be fit parameters
                out_matrix(4,i_vary,i_fit)          = int_out_vec(6);    
            end
            
           if i_fit == i_fit_start                                          % keep track of ellipse rotations, assume CCW rotation (positive zeta)
                n_half_rotations = 0;
            else
                if out_matrix(5,i_vary,i_fit) + n_half_rotations * pi < out_matrix(5,i_vary,i_fit - 1)
                    n_half_rotations = n_half_rotations + 1;                    % increment half rotation counter
                end
                out_matrix(5,i_vary,i_fit) = out_matrix(5,i_vary,i_fit) + n_half_rotations * pi;
           end
            
            FitString(n_vary, i_fit, i_vary, T, out_matrix, m_1, m_2, m_3, i_fit_control, i_integrator_control)  % print fit results

        end     % i_fits    loop over fit 
    end         % i_fit_control > 1 end    
end         % i_vary loop (not usually used)

%  ********************************  WRITE FIT RESULTS   ****************************** 
if i_write_fit_output == 1 
    
    for i_write = 1: n_vary
        if i_write < 10  
            file_ext = ['_0',num2str(i_write),'.dat'];
        else
            file_ext = ['_',num2str(i_write),'.dat'];
        end
        write_vec = [alpha_m_0, beta_m_0, a, b, zeta, m_1, m_2, m_3]; 
        dir_string = [drive_string,file_head];
        fileout = [dir_string,'out_vector',file_ext];
        save(fileout, 'write_vec', '-ascii',  '-tabs');
        out_matrix_b = (squeeze(out_matrix(:,i_write,:)))';
        dir_string = [drive_string,file_head];
        fileout = [dir_string,'out_matrix',file_ext];
        save(fileout, 'out_matrix_b', '-ascii',  '-tabs');
    end
end

Contact us at files@mathworks.com