function aps_powerlaw(start_step,end_step,save_path)
% aps_powerlaw(start_step,end_step)
% Program to compute the powerlaw tropsoheric delays.
%   STEP 1 = Estimate powerlaw delay and maximum height from sounding data
%            (when this is available), or use defaults or user set values
%   STEP 2 = Rotate dataset to minimize interpolation effects, Powerlaw scaling and interpolate to regular grid
%   STEP 3 = 1D/2D bandfiltering in the frequency domain and interpolate
%            back to local grid
%   STEP 4 = Spatial local estimation of the powerlaw scaling coefficient,
%            estimate final values from reliable frequency band, extrapolate
%            to all point locations and compute powerlaw tropospheric delay.
%
%   To change the processing parameters use setparm_aps and getparm_aps
%
% **** when using this estimation method please cite:
%       D.P.S. Bekaert, A.J. Hooper and T.J. Wright, A spatially-variable power-law tropospheric correction
% 	  	 technique for InSAR data, JGR, doi:10.1029/2014JB011558 *****
%
%
%     Copyright (C) 2015  Bekaert David - University of Leeds
%     Email: eedpsb@leeds.ac.uk or davidbekaert.com
%
%     This program is free software; you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation; either version 2 of the License, or
%     (at your option) any later version.
%
%     This program is distributed in the hope that it will be useful,
%     but WITHOUT ANY WARRANTY; without even the implied warranty of
%     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%     GNU General Public License for more details.
%
%     You should have received a copy of the GNU General Public License along
%     with this program; if not, write to the Free Software Foundation, Inc.,
%     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%
% By David Bekaert - University of Leeds
%
% modifications:
% 04/2013   DB:     Incorportate a parm_aps list with all the processing
%                   options and variables.
% 05/2013   DB:     Include powerlaw coefficient sensitivity analysis
% 05/2013   DB:     Allow for an interferogram based estimation
% 12/2013   DB:     Put the flag of ifg correction on prior to powerlaw computation
% 02/2014   DB:     Integrate the plotting with the running of the sounding data
% 02/2014   DB:     Include a mountain ridge code, user interaction needed
% 07/2014   DB:     Have the option to plot the ridges
% 07/2014   DB:     Include a cropping option
% 11/2014   DB:     Fix bug and include paper reference
% 03/2015   DB:     Ask for the reference technique in step 5
% 01/2016   DB:     Fix ll.lonlat, ph_uw, hgt for non-stamps data, replace
%                   save calls with aps_save, in case no -append flag is included
% 02/2016   DB:     Expand option 5 to plot with the unwrapped phase too.

if nargin<3
   save_path = [pwd filesep];
end

currdir = pwd;		% current working directory
plot_flag = 0;
hydro =1;
wet=1;

fprintf(['\n********************************************************************************************************** \n','D.P.S. Bekaert, A.J. Hooper and T.J. Wright,\n', 'A spatially-variable power-law tropospheric correction technique for InSAR data, JGR,  doi:10.1029/2014JB011558\n', '**********************************************************************************************************\n\n'])

%% PART 1: Define the powerlaw coefficents, from the user or by using sounding data.
sounding_data = getparm_aps('sounding_data',1);
stamps_processed = getparm_aps('stamps_processed',1);
powerlaw_ridge_constraint = getparm_aps('powerlaw_ridge_constraint',1);

if strcmp(stamps_processed,'y')
    load psver
else
    psver = 2;
end

% file names of the output data
apsname = [save_path filesep 'tca' num2str(psver) '.mat'];
apssbname = [save_path filesep 'tca_sb' num2str(psver) '.mat'];
apsbandsname = [save_path filesep 'tca_bands' num2str(psver) '.mat'];
apsbandssbname = [save_path filesep 'tca_bands_sb' num2str(psver) '.mat'];

% saving part of the data in a subfolder
save_path = [save_path filesep 'aps_p'];
if exist(save_path,'dir')~=7
    mkdir(save_path);
end

if start_step==0 && strcmp(powerlaw_ridge_constraint,'y')

    mountain_ridge=[];
    if exist('tca_support.mat')==2
        load('tca_support.mat','powerlaw_ridges')
        if exist('powerlaw_ridges','var')==1
            mountain_ridge = powerlaw_ridges.mountain_ridge;
        end
    end
    if isempty(mountain_ridge)==1
        str_repr='y';
        str_vis = 'n';
    end

    % data has been computed before - reporcess? - visualize?
    if length(mountain_ridge)>0
        str_repr = '';
        while strcmpi(str_repr,'y')~=1 && strcmpi(str_repr,'n')~=1
            str_repr = input(['Ridges have been defined before, reprocess the data? [y/n] \n'],'s');
        end

        % no reprocessing, do you want to visualize it?
        if strcmpi(str_repr,'n')
            str_vis = '';
            while strcmpi(str_vis,'y')~=1 && strcmpi(str_vis,'n')~=1
                str_vis = input(['Do you want to visualize the data? [y/n] \n'],'s');
            end
        else
            str_vis = 'n';
        end

    end

    if strcmpi(str_repr,'y')
        [mountain_ridge] = aps_powerlaw_watershed;
    end
    if strcmpi(str_vis,'y')
        aps_support_plot(1);
    end
end

if start_step==1 && strcmp(sounding_data,'y')
    fprintf('\n\nStep 1: Powerlaw coefficients from the sounding data\n')

    % getting the variables from the parm_aps file
    look_angle = getparm_aps('look_angle',1);
    lambda = getparm_aps('lambda',1);
    sounding_h0= getparm_aps('sounding_h0',1);
    sounding_h_alpha_thres= getparm_aps('sounding_h_alpha_thres',1);
    sounding_start_date= getparm_aps('sounding_start_date',1);
    sounding_end_date= getparm_aps('sounding_end_date',1);
    sounding_time_stamp= getparm_aps('sounding_time_stamp',1);
    sounding_dir= getparm_aps('sounding_dir',1);
    sounding_error_promp= getparm_aps('sounding_error_promp',1);
    sounding_sensitivity= getparm_aps('sounding_sensitivity',1);
    n_months = getparm_aps('sounding_months',1);
    time_stamp = getparm_aps('sounding_time_stamp',1);
    sounding_ifg_dates = getparm_aps('sounding_ifg_dates',1);

    %%% check if the file is already exisiting - ask if reprocessing is
    %%% needed, if not load exisitng file and plot results
    time_stamp_str = [];
    for k=1:size(time_stamp,1)
        if k>1
            time_stamp_str = [time_stamp_str '_' time_stamp(k,:)];
        else
            time_stamp_str = [time_stamp(k,:)];
        end
    end

    if strcmp(sounding_sensitivity,'y')
        fprintf('Sensitivity analyses of sounding data \n\n')

        % check first if the output file is already exisiting otherwize
        % ask if it needs to be reprocessed or visualised.

        if strcmp(sounding_ifg_dates,'y')
            fprintf('Coefficient estimation for each interferogram \n\n')
            if hydro==1 && wet==0
                save_name = [sounding_dir filesep 'Powerlaw' filesep 'Powerlaw_sensitivity_hydro_SAR_dates_1month_' time_stamp_str 'Hr.mat'  ];
            elseif hydro==0 && wet==1
                save_name = [sounding_dir filesep 'Powerlaw' filesep 'Powerlaw_sensitivity_wet_SAR_dates_1month_' time_stamp_str 'Hr.mat'  ];
            else
                save_name = [sounding_dir filesep 'Powerlaw' filesep 'Powerlaw_sensitivity_SAR_dates_1month_' time_stamp_str 'Hr.mat'  ];
            end
            n_months =1;
        else
            fprintf('Coefficient estimation based on average \n\n')
            % putting the variables in the right set-up
            start_year = str2num(sounding_start_date(1:4));
            end_year = str2num(sounding_end_date(1:4));
            start_str = sounding_start_date(5:6);
            end_str = sounding_end_date(5:6);

            % the file name to be loaded
            if hydro==1 && wet==0
                save_name = [sounding_dir filesep 'Powerlaw' filesep 'Powerlaw_sensitivity_hydro_' num2str(n_months) 'month_' time_stamp_str 'Hr_' num2str(start_year) start_str '_' num2str(end_year) end_str '.mat'  ];
            elseif hydro==0 && wet==1
                save_name = [sounding_dir filesep 'Powerlaw' filesep 'Powerlaw_sensitivity_wet_' num2str(n_months) 'month_' time_stamp_str 'Hr_' num2str(start_year) start_str '_' num2str(end_year) end_str '.mat'  ];
            else
                save_name = [sounding_dir filesep 'Powerlaw' filesep 'Powerlaw_sensitivity_' num2str(n_months) 'month_' time_stamp_str 'Hr_' num2str(start_year) start_str '_' num2str(end_year) end_str '.mat'  ];
            end
        end

        if exist(save_name,'file')==2
            str = '';
            while strcmpi(str,'y')~=1 && strcmpi(str,'n')~=1
                str = input(['This file already exist, do you want to re-process the data? [y/n] \n'],'s');
            end

            if strcmpi(str,'y')
                sounding_powerlaw_sens(hydro,wet)
            else
                sounding_powerlaw_sens_display
            end
        else
            % estimate the coefficients for individual interferograms
            sounding_powerlaw_sens(hydro,wet)
        end

    else
        fprintf('Based on specified sounding period \n\n')
        % When having sounding data estimate the powerlaw and b coefficient
        [alpha_log_all,alpha,h0,n_soundings] = sounding;

        % setting the estimated parameters in the parameter file
        fprintf('Updating the parm_aps list \n')
        setparm_aps('powerlaw_h0',h0)
        setparm_aps('powerlaw_alpha',alpha)

        % saving the data
        save([save_path filesep 'aps_p_step1.mat'],'alpha','h0','look_angle','lambda','sounding_h0','sounding_h_alpha_thres','sounding_start_date','sounding_end_date','sounding_time_stamp')

    end
end
if start_step==1 && strcmp(sounding_data,'n')
     fprintf('\n\n Step 1: powerlaw coefficents set by user \n\n')

     % as set by the user
     h0 = getparm_aps('powerlaw_h0');
     alpha = getparm_aps('powerlaw_alpha');
end

%% PART 2: Rotate the dataset to get mimumal white space around it
% Scaling and interpolation of the data to a regular grid
if start_step<=2 && end_step >=2
    fprintf('\n\nStep 2: Rotate the dataset \n')

    % getting the variables from the parm_aps file
    heading = getparm_aps('heading',1);
    crop_flag =  getparm_aps('crop_flag',1);

    % loading the lonlat information
    ll_matfile = getparm_aps('ll_matfile',1);
    ll = load(ll_matfile);
    ll = ll.lonlat;

    % retrieving the coefficients
    h0 = getparm_aps('powerlaw_h0',1);
    alpha = getparm_aps('powerlaw_alpha',1);
    getparm_aps('powerlaw_xy_res',1);
    phuw_matfile = getparm_aps('phuw_matfile',1);
    hgt_matfile = getparm_aps('hgt_matfile',1);
    DEM_corr = getparm_aps('powerlaw_DEM_corr',1);
    bperp_matfile = getparm_aps('bperp_matfile',1);

    % rotating the data
    [xy_rotatelocal_full,xy_local_full,local_origin_full] = ll2rotatelocal(ll,heading);

    % cropping if needed
    crop = [];
    if strcmpi(crop_flag,'y')
        if exist('area_ex.mat','file')==2
            crop = load('area_ex.mat');
        elseif exist('../area_ex.mat','file')==2
            crop = load('../area_ex.mat');
        else
            crop = [];
            fprintf(['There is no area_ex.mat file found, no data is cropped... \n'])
        end
    end
    if ~isempty(crop)
        ix_remove =  inpolygon(ll(:,1),ll(:,2),crop.lonlat(:,1),crop.lonlat(:,2));
    else
        ix_remove = [];
    end
    ll(ix_remove,:) =[];

    % get the InSAR convexhull -- not needed at this stage!
    ix_convexhull = convhull(ll(:,1),ll(:,2));
    InSAR_convexhull = ll(ix_convexhull,:);
    if exist('tca_support.mat','file')==2
      save('tca_support.mat','-append','InSAR_convexhull')
    else
      save('tca_support.mat','InSAR_convexhull')
    end
    clear InSAR_convexhull

    % rotating the data using all data including the region cropped.
    % This will be used in the last stage of the power-law code.
    [xy_rotatelocal,xy_local,local_origin] = ll2rotatelocal(ll,heading);

    fprintf('        Scaling and interpolation to a regular grid \n\n')
    % loading the unwrapped interferograms
    ph = load(phuw_matfile);
    ph = ph.ph_uw;

    % keep track of those pixels which are NaN
    ix_phase_nan = isnan(ph);

    % remove crop if needed
    ph(ix_remove,:)=[];

    % loading the topography information
    hgt = load(hgt_matfile);
    hgt = hgt.hgt;
    % remove crop if needed
    hgt(ix_remove,:)=[];

    % geting the number of interferograms and points from the phase matrix
    n_interferograms= size(ph,2);
    n_points = size(ph,1);

    % Scaling of the topography according to the powerlaw
    % ifg based powerlaw correction?
    if length(alpha)==n_interferograms
        hgt_max = max(hgt);
        hgt_scaled=repmat(hgt,1,size(ph,2));
        ifg_based_correction='y';

        for k=1:length(alpha)
            if h0(k)*1000-hgt_max<=0
                error('myApp:argChk', ['Power law is not valid above h0 heights,... \nAbort,... \n'])
            end
            hgt_scaled(:,k)=(h0(k)*1000-hgt_scaled(:,k)).^(alpha(k));
        end
    else
        % Scaling of the topography according to the powerlaw
        ifg_based_correction='n';
        ix = find(h0*1000-hgt < 0);
        if isempty(ix)~=1
            error('myApp:argChk', ['Power law is not valid above h0 heights,... \nAbort,... \n'])
        end
        hgt_scaled = (h0*1000-hgt).^(alpha);
    end

    % define a new dataset were the height is actually the first column and
    % the interferograms are in the other columns
    % optional remove an estimate for the DEM errors
    if strcmp(DEM_corr,'y') && n_interferograms>5;
       % these are erros that scale with perpendicular baseline
       % loading the perpendicualr baseline information
       bperp = load(bperp_matfile);
        if strcmp(stamps_processed,'y')
          bperp = bperp.bperp;
       end
       % checking the size of bperp
       if size(bperp,2) > 1
          bperp = bperp';
          if size(bperp,2) >1
              error('myApp:argChk', ['bperp is not a vector,... \nAbort,... \n'])
          end
       end
       % estimating the correlated errors
       DEM_corr_e = lscov(bperp,ph')';

       % removing DEM correlated errors
       ph_temp = ph-repmat(bperp',n_points,1).*repmat(DEM_corr_e,1,n_interferograms);
       dataset = [hgt_scaled ph_temp];
       clear ph_temp A
    else
        if strcmp(DEM_corr,'y') && n_interferograms<=5
            fprintf('Not enough interferograms to make a reliable estimate for the DEM error \n')
            DEM_corr = 'n';
        end
        dataset = [hgt_scaled ph];
        DEM_corr_e = zeros([n_points 1]);
    end

    %% do an initial reference correction for each interferogram.
    % This is to scope for regions that are small and which can have bandfiltering
    % arctifacts introduced. What happens is that each interferogram is
    % shifted such that the phase is zero at h0. Note that this is a long
    % wavelength shift that will be filtered out latter on. But in case of
    % irregularities it will reduce introduced arctifacts. To cope with
    % spatial varying signals this offset estimation is done over multiple
    % windows, but the constant offset is the same for all windows.
    % get the position of some subwindows

    [iterate] = window_generation(xy_rotatelocal,1);

    counter=0;
    for k=1:length(iterate.window_ix)
        counter = counter + size(iterate.window_ix{k},1);
    end

    for k=1:n_interferograms
        % separate between ifg based correction or using a single alpha and h0
        if length(alpha)==n_interferograms
            height_data = dataset(:,k);
            scaling = 1./nanmean(abs(dataset(:,k)));
            phase_data = dataset(:,n_interferograms+k);
        else
            scaling = 1./nanmean(abs(dataset(:,1)));
            height_data = dataset(:,1);
            phase_data = dataset(:,1+k);
        end

        % estimating the reference, forced to be the same for all windows
        A_temp = zeros([counter length(iterate.window_ix)]);
        data_temp =  zeros([counter size(phase_data,2)]);
        counter =1;
        for kk=1:length(iterate.window_ix)
            A_temp(counter:counter+size(iterate.window_ix{kk},1)-1,kk) = [height_data(iterate.window_ix{kk},1)]*scaling;
            data_temp(counter:counter+size(iterate.window_ix{kk},1)-1,:) = [phase_data(iterate.window_ix{kk},1)];
            counter =  counter + size(iterate.window_ix{kk},1);

        end
        A_temp = [A_temp ones([size(data_temp,1) 1])];

        % allowing for NaN in interferogram {DB}
%        ix = find(isnan(data_temp(:,1))~=1);
        ix = ~((isnan(data_temp(:,1)) + sum(isnan(A_temp),2))>=1);
        coeff = lscov(A_temp(ix,:),data_temp(ix,1));

        % plot the interferogram before and after the reference shift.
        if plot_flag ==1
           figure('name',['Correction of the reference for ifg ', num2str(k)])
           subplot(2,1,1)
           plot(height_data,phase_data,'k.')
           if length(alpha)==n_interferograms
                temp =getparm_aps('powerlaw_alpha');
                xlabel(['(h_0-h)^{' num2str(temp(k)) '}'])
           else
                xlabel(['(h_0-h)^{' num2str(getparm_aps('powerlaw_alpha')) '}'])
           end
           ylabel('Phase')
        end

       if plot_flag ==1
           for kk=1:length(iterate.window_ix)
                y_temp = [[height_data(iterate.window_ix{kk},1)]*scaling ones(size([height_data(iterate.window_ix{kk},1)]*scaling))]*[coeff(kk) coeff(end)]';
                hold on
                plot([height_data(iterate.window_ix{kk},1)],y_temp,'r-')
           end
           title('power-law plot with ifg reference to be the mean')
       end

       % correct the data for the reference
       data_temp = data_temp - coeff(end);
       % separate between ifg based correction or using a single alpha and h0
       if length(alpha)==n_interferograms
           dataset(:,n_interferograms+k) = dataset(:,n_interferograms+k) - coeff(end);
       else
           dataset(:,k+1) = dataset(:,k+1) - coeff(end);
       end

       % plot the corrected result
       if plot_flag ==1
           subplot(2,1,2)
           if length(alpha)==n_interferograms
               plot(dataset(:,k),dataset(:,n_interferograms+k),'k.')
               temp =getparm_aps('powerlaw_alpha');
               xlabel(['(h_0-h)^{' num2str(temp(k)) '}'])
           else
              plot(dataset(:,1),dataset(:,k+1),'k.')
              xlabel(['(h_0-h)^{' num2str(getparm_aps('powerlaw_alpha')) '}'])
           end
           ylabel('Phase shifted to h0 reference')

           % plot line on top
           coeff = lscov(A_temp,data_temp(:,1));
           for kk=1:length(iterate.window_ix)
                if length(alpha)==n_interferograms
                    y_temp = [[dataset(iterate.window_ix{kk},k)]*scaling ones(size([dataset(iterate.window_ix{kk},k)]*scaling))]*[coeff(kk) coeff(end)]';
                else
                    y_temp = [[dataset(iterate.window_ix{kk},1)]*scaling ones(size([dataset(iterate.window_ix{kk},1)]*scaling))]*[coeff(kk) coeff(end)]';
                end
                hold on
                plot([dataset(iterate.window_ix{kk},k)],y_temp,'r-')

           end
           title('power law plot with h0 as reference')
       end
    end

    %% Extrapolate such the convexhull contains the full grid
    xy_res = getparm_aps('powerlaw_xy_res');
    [xy_local_box,dataset_box] = extrapolate_local_new(xy_rotatelocal,dataset, xy_res(1), xy_res(2));

    % Interpolate to a regular grid
    [X_regular,Y_regular,Z_regular] = interpolate_regular(xy_local_box,dataset_box, xy_res(1), xy_res(2));

    % Saving the results of this step
    aps_save([save_path filesep 'aps_p_step2.mat'],xy_rotatelocal,xy_rotatelocal_full,ifg_based_correction,xy_local,xy_local_full,heading,ll,local_origin,local_origin_full,X_regular,Y_regular,Z_regular,xy_rotatelocal,xy_res,DEM_corr,DEM_corr_e,ix_remove,ix_phase_nan)

end

%% PART 3: Bandfiltering and interpolation back to a local grid
if start_step<=3 && end_step >=3
    fprintf('Step 3: Bandfiltering of the regular grid and converting back to a local grid \n\n')
    % Loading the data from the previous step
    if start_step==3
        load([save_path filesep 'aps_p_step2.mat'])
    end

     % loading the lonlat information
    ll_matfile = getparm_aps('ll_matfile');
    temp = load(ll_matfile);
    if strcmp(stamps_processed,'y')
        temp = temp.lonlat;
    end

    n_points_original = size(temp,1);
    clear temp;

    % getting the extend of the spatial bandfilters
    spatial_bands = getparm_aps('powerlaw_spatial_bands');

    % Bandfiltering
    bandfiltering(Z_regular,xy_res(1),xy_res(2),spatial_bands,save_path,ifg_based_correction)

    % Interpolate back to local grid
    n_datasets = size(Z_regular,3);
    % calling the function for each bandfiltered dataset
    fprintf(['*Interpolate the band filtered data to the local grid: \n'])
    if strcmp(ifg_based_correction,'y')
        h_ifg_number = n_datasets/2;
    else
        h_ifg_number = 1;
    end
    for k=1:n_datasets

        % the file names, vary depending if the correction is varying for each interferogram
        if k<=h_ifg_number && h_ifg_number~=1
            % thse are interferograms
            load_name = ['bandfilter_regular_hgt_ifg_' num2str(k) '.mat'];
            save_name = ['bandfilter_local_hgt_ifg' num2str(k) '.mat'];
        elseif k<=h_ifg_number && h_ifg_number==1
            % these are the heights
            load_name = 'bandfilter_regular_hgt.mat';
            save_name = 'bandfilter_local_hgt.mat';
        else
            % thse are interferograms
             load_name = ['bandfilter_regular_ifg_' num2str(k-h_ifg_number) '.mat'];
             save_name = ['bandfilter_local_ifg_' num2str(k-h_ifg_number) '.mat'];
        end

        dataset_band = load([save_path filesep load_name]);

        % Storing the information what type of filtering was done for which band
        dimension_filter = dataset_band.dimension_filter;      % 1 for 1D, 2 for 2D
        % loading the data for each dataset
        dataset_band = dataset_band.data_band_out;

        % computing the bandfiltered data at the local grid
        % note that this is xy_rotatelocal and does not include the cropped
        % region!
        [z_local_out_temp] = interpolate2local(X_regular,Y_regular,dataset_band,xy_rotatelocal);
        % in case data has been cropped then path the cropped region with NaNs
        z_local_out = NaN([n_points_original size(z_local_out_temp,2)]);

        if isempty(ix_remove)
            z_local_out = z_local_out_temp;
        else
            z_local_out(~ix_remove,:) = z_local_out_temp;
        end

        % Check in case some of the orginal phase values were nan.
        % if so set them to nan here too.
        if k<=h_ifg_number && h_ifg_number~=1
            % these are interferograms heights not the phase. No need to
            % modify this here.
        elseif k<=h_ifg_number && h_ifg_number==1
            % these are the heights, nothing needs to be done as the
            % heights remain the same for each interferogram.
        else
            % these are interferograms
            fprintf('Allowing for NaN''s in the interferograms \n')
            z_local_out(ix_phase_nan(:,k-h_ifg_number),:)=NaN;
        end

        % saving the data for each dataset
        aps_save([save_path filesep save_name],z_local_out,spatial_bands,dimension_filter);
        % outputting the progress to the user
        fprintf(['Progress: ' num2str(k) '/' num2str(n_datasets) ' done \n'])
    end

    % Saving the results of this step
    aps_save([save_path filesep 'aps_p_step3.mat'],ifg_based_correction,n_datasets,local_origin,local_origin_full,xy_rotatelocal,xy_rotatelocal_full,DEM_corr,DEM_corr_e,dimension_filter,ix_remove,ix_phase_nan)
end

%% PART 4: Estimating the scaling coefficient of the powerlaw for the local dataset
% and computing the corresponding troposheric delay
if start_step<=4 && end_step >=4

    fprintf('\n\nStep 4: Estimating scaling coefficient of the powerlaw locally \n')
    fprintf('and estimating the tropospheric interferometric phase delay \n\n')

    % Loading the data from the previous step
    if start_step==4
        load([save_path filesep 'aps_p_step3.mat'])
    end

    % loading information from the parm file
    hgt_matfile = getparm_aps('hgt_matfile');
    powerlaw_all_bands = getparm_aps('powerlaw_all_bands');
    hgt = load(hgt_matfile);
    hgt = hgt.hgt;

    % checking if its an interferogram based correction or not:
    if strcmp(ifg_based_correction,'y')
        n_interferograms = n_datasets/2;
    else
        n_interferograms = n_datasets-1;
    end

    % initialization
    n_points = size(hgt,1);
    ph_tropo_powerlaw = NaN([n_points n_interferograms]);
    K_tropo_powerlaw = NaN([n_points n_interferograms]);
    perc_cons_K_powerlaw= NaN([ n_interferograms 1]);
    if strcmp(powerlaw_all_bands,'y')
        ph_tropo_powerlaw_bands = NaN([n_points n_interferograms size(getparm_aps('powerlaw_spatial_bands'),1)]);
        K_tropo_powerlaw_bands = NaN([n_points n_interferograms size(getparm_aps('powerlaw_spatial_bands'),1)]);
    end

    % Linear estimation for multiple windows based on bandfitlered data
    % final slope value is estimated for reliable bands only
    iterate = [];
    % getting the patch information from the parm_aps file

    % processing only those images that are not dropped in case of stamps
    ix_ifgs = 1:n_interferograms;
    if strcmp(stamps_processed,'y')
       ix_drop_ifgs = getparm('drop_ifg');
    else
        ix_drop_ifgs = [];
    end
    ix_ifgs(ix_drop_ifgs)=[];

    for k=1:length(ix_ifgs)

        ix_interferogram = ix_ifgs(k);

        % the file names, vary depending if the correction is varying for each interferogram
        if strcmp(ifg_based_correction,'y') && n_interferograms~=1
            % the first dataset was set the be the topography
            topo_data = load([save_path filesep 'bandfilter_local_hgt_ifg' num2str(ix_interferogram) '.mat']);
            topo_data = topo_data.z_local_out;
            % the other dataset were the phase
            phase_data = load([save_path filesep 'bandfilter_local_ifg_' num2str(ix_interferogram) '.mat']);
            phase_data = phase_data.z_local_out;
            ifg_number=k;
        else
            % the first dataset was set the be the topography
            topo_data = load([save_path filesep 'bandfilter_local_hgt.mat']);
            topo_data = topo_data.z_local_out;
            % the other dataset were the phase
            phase_data = load([save_path filesep 'bandfilter_local_ifg_' num2str(ix_interferogram) '.mat']);
            phase_data = phase_data.z_local_out;
            ifg_number=1;
        end

        % keep the figures of the outlier rejection as validation
        if k==0
            save_path_outlier_windows = [save_path filesep 'ifg_' num2str(ix_interferogram) '_outlier_window'];
        else
            save_path_outlier_windows=[];
        end

        if k==10000
            debug_fig_linear=1;
        else
            debug_fig_linear=0;
        end

        [ph_tropo_powerlaw_temp,ph_tropo_powerlaw_band_temp,iterate,K_temp,K_band,perc_cons_K] = aps_powerlaw_linear_local(xy_rotatelocal_full,local_origin_full,topo_data,phase_data,iterate,dimension_filter,save_path_outlier_windows,ifg_number,ix_phase_nan(:,ix_interferogram));

        % storing of the data
        % Computation of the tropospheric delay for all ifgs
        ph_tropo_powerlaw(:,ix_interferogram) = ph_tropo_powerlaw_temp;
        K_tropo_powerlaw(:,ix_interferogram) = K_temp;
        perc_cons_K_powerlaw(ix_interferogram,1) = perc_cons_K;
        if strcmp(powerlaw_all_bands,'y')
            % keeping all bands with in the columns the ifgs and the third
            % dimention the bands
            ph_tropo_powerlaw_bands(:,ix_interferogram,:) = ph_tropo_powerlaw_band_temp;
            K_tropo_powerlaw_bands(:,ix_interferogram,:)=K_band;

            % keeping the interferogram and the respective bands.
            eval(['ph_tropo_powerlaw_ifg_' num2str(ix_interferogram) '= ph_tropo_powerlaw_band_temp;']);
            eval(['K_tropo_powerlaw_ifg_' num2str(ix_interferogram) '= K_band;']);

            clear ph_tropo_powerlaw_band_temp
            % saving the data from this step
            if strcmp(stamps_processed,'y')
                % This is StaMPS
                if strcmp(getparm('small_baseline_flag'),'y')
                    if exist(apsbandssbname,'file')==2
                        save(apsbandssbname,'-append',['ph_tropo_powerlaw_ifg_' num2str(ix_interferogram)],['K_tropo_powerlaw_ifg_' num2str(ix_interferogram)])
                    else
                        save(apsbandssbname,['ph_tropo_powerlaw_ifg_' num2str(ix_interferogram)],['K_tropo_powerlaw_ifg_' num2str(ix_interferogram)])
                    end
                else
                    if exist(apsbandsname,'file')==2
                        save(apsbandsname,'-append',['ph_tropo_powerlaw_ifg_' num2str(ix_interferogram)],['K_tropo_powerlaw_ifg_' num2str(ix_interferogram)])
                    else
                        save(apsbandsname,['ph_tropo_powerlaw_ifg_' num2str(ix_interferogram)],['K_tropo_powerlaw_ifg_' num2str(ix_interferogram)])
                    end
                end
            else
                % This is not StaMPS
                if exist(apsbandsname,'file')==2
                    save(apsbandsname,'-append',['ph_tropo_powerlaw_ifg_' num2str(ix_interferogram)],['K_tropo_powerlaw_ifg_' num2str(ix_interferogram)])
                else
                    save(apsbandsname,['ph_tropo_powerlaw_ifg_' num2str(ix_interferogram)],['K_tropo_powerlaw_ifg_' num2str(ix_interferogram)])
                end
            end
            eval(['clear ph_tropo_powerlaw_ifg_' num2str(ix_interferogram) ' K_tropo_powerlaw_ifg_' num2str(ix_interferogram)  ';']);
        end

        % give output to the screen
        fprintf(['Progress ifgs: ' num2str(k) '/' num2str(length(ix_ifgs)) ' done \n'])
    end
    % saving the data from this step
    aps_save([save_path filesep 'aps_p_step4.mat'],ph_tropo_powerlaw,DEM_corr,DEM_corr_e,iterate,K_tropo_powerlaw)

    % saving the data into the final variable togehter were other
    % correction values are saved.
    ph_tropo_powerlaw_or= ph_tropo_powerlaw;
    K_tropo_powerlaw_or=K_tropo_powerlaw;
    if strcmp(stamps_processed,'y')
        % This is StaMPS
        if strcmp(getparm('small_baseline_flag'),'y')
            if exist(apssbname,'file')==2
                save(apssbname,'-append','ph_tropo_powerlaw','K_tropo_powerlaw','perc_cons_K_powerlaw')
            else
                save(apssbname,'ph_tropo_powerlaw','K_tropo_powerlaw','perc_cons_K_powerlaw')
            end

            % saving the band information
            if strcmpi(powerlaw_all_bands,'y')
                if exist(apsbandssbname,'file')==2
                    save(apsbandssbname,'-append','ph_tropo_powerlaw_or','K_tropo_powerlaw_or','ph_tropo_powerlaw_bands','K_tropo_powerlaw_bands','perc_cons_K_powerlaw')
                else
                    save(apsbandssbname,'ph_tropo_powerlaw_or','K_tropo_powerlaw_or','ph_tropo_powerlaw_bands','K_tropo_powerlaw_bands','perc_cons_K_powerlaw')
                end
            end
        else
            % this is single master
            if exist(apsname,'file')==2
                save(apsname,'-append','ph_tropo_powerlaw','K_tropo_powerlaw','perc_cons_K_powerlaw')
            else
                save(apsname,'ph_tropo_powerlaw','K_tropo_powerlaw','perc_cons_K_powerlaw')
            end

            % saving the band information
            if strcmpi(powerlaw_all_bands,'y')
                if exist(apsbandsname,'file')==2
                    save(apsbandsname,'-append','ph_tropo_powerlaw_or','K_tropo_powerlaw_or','ph_tropo_powerlaw_bands','K_tropo_powerlaw_bands','perc_cons_K_powerlaw')
                else
                    save(apsbandsname,'ph_tropo_powerlaw_or','K_tropo_powerlaw_or','ph_tropo_powerlaw_bands','K_tropo_powerlaw_bands','perc_cons_K_powerlaw')
                end
            end
        end
    else
        % This is not StaMPS
        if exist(apsname,'file')==2
            save(apsname,'-append','ph_tropo_powerlaw','K_tropo_powerlaw','perc_cons_K_powerlaw')
        else
            save(apsname,'ph_tropo_powerlaw','K_tropo_powerlaw','perc_cons_K_powerlaw')
        end

         % saving the band information
        if strcmpi(powerlaw_all_bands,'y')
            if exist(apsbandsname,'file')==2
                save(apsbandsname,'-append','ph_tropo_powerlaw_or','K_tropo_powerlaw_or','ph_tropo_powerlaw_bands','K_tropo_powerlaw_bands','perc_cons_K_powerlaw')
            else
                save(apsbandsname,'ph_tropo_powerlaw_or','K_tropo_powerlaw_or','ph_tropo_powerlaw_bands','K_tropo_powerlaw_bands','perc_cons_K_powerlaw')
            end
        end
    end
    setparm_aps('powerlaw_kept',[0]);

end

if start_step<=5 && end_step >=5 &&  exist('aps_RMSE_comparison')==2
    fprintf('\n\nStep 5: Powerlaw band comparison with reference\n')

    if strcmpi(getparm_aps('stamps_processed'),'y')
        % check which technique it needs to be compared with
        technique_number = -10;
        while isnumeric(technique_number)~=1 | (technique_number<1 | technique_number>7)
            technique_number = input(['*Select the technique number you want to compare with: \n---- MERIS (1), MODIS (2), MODIS Recal (3), ERA-I (4), WRF (5), u(sb) (6), u(sb)-d (7) : \n'],'s');
            technique_number = floor(str2num(technique_number));
        end
        % the technique component as a string
        if technique_number==1
            comp_str = 'a_mi';
        elseif technique_number==2
            comp_str = 'a_MI';
        elseif technique_number==3
            comp_str = 'a_RMI';
        elseif technique_number==4
            comp_str = 'a_e';
        elseif technique_number==5
            comp_str = 'a_w';
        elseif technique_number==6
            if strcmpi(getparm('small_baseline_flag'),'y')
                comp_str = 'usb';
            else
                comp_str = 'u';
            end
        elseif technique_number==7
            if strcmpi(getparm('small_baseline_flag'),'y')
                comp_str = 'usb-d';
            else
                comp_str = 'u-d';
            end
        end

        % check if we should add the hydrostatic delays too in case this is
        % possible
        if technique_number==1 | technique_number==2 | technique_number==3
            hydro_comp = inf;
            while isnumeric(hydro_comp)~=1 | (hydro_comp<0 | hydro_comp>2)
                hydro_comp = input(['*Do you want to add a hydrostatic component:\n---- None (0), ERA-I hydr (1), WRF hydro (2): \n'],'s');
                hydro_comp = floor(str2num(hydro_comp));
            end
        else
            hydro_comp = 0;
        end
        % the hydrostatic component as a string
        hydro_str = '';
        if hydro_comp==1
            hydro_str = '+a_eh';
        elseif hydro_comp==2
            hydro_str = '+a_wh';
        end

        % The reference technique is combination of both
        tech_ref = [comp_str hydro_str];
        clear hydro_str comp_str technique_number hydro_comp

        % running the RMSE script
        aps_RMSE_comparison(tech_ref,'a_pbands')
        str = '';
        while strcmpi(str,'y')~=1 && strcmpi(str,'n')~=1
            str = input(['Do you want to update ''powerlaw_kept'' to a different band? [y/n] \n'],'s');
        end
        if strcmpi(str,'y')
            bandnumber = -10;
            while (bandnumber<0 | bandnumber>size(getparm_aps('powerlaw_spatial_bands'),1))
                bandnumber = input(['Which the band? (row number of the band to keep, or 0 for all bands combined) \n'],'s');
                bandnumber = str2num(bandnumber);
                if isempty(bandnumber)
                    bandnumber = -10;
                end
            end
            setparm_aps('powerlaw_kept',[bandnumber]);
        end
    else
       fprintf('This option uses the StaMPS ps_plot function, and therefore only works if your data is stamps processed \n')
    end

end


function [biomassValues, targetLowerBounds, targetUpperBounds, plottedReactions] = multiProductionEnvelope(model, deletions, biomassRxn, geneDelFlag, nPts, plotAllFlag, plotTools)
% Calculates the byproduct secretion envelopes for
% every product (excreted metabolites with 1 or more Carbons)
%
% USAGE:
%
%    [biomassValues, targetValues] = multiProductionEnvelope(model, deletions, biomassRxn, geneDelFlag, nPts, plotTools)
%
% INPUT:
%    model:            COBRA model structure
%
% OPTIONAL INPUT:
%    deletions:        List of reaction or gene deletions (empty if wild type)
%                      (Default = {})
%    biomassRxn:       Biomass `rxn` name (Default = whatever is defined in model)
%    geneDelFlag:      Perform gene and not reaction deletions (Default = false)
%    nPts:             Number of points in the plot (Default = 20)
%    plotTools:        boolean (default = false) - add tools for editing the figure and its properties
%    plotAllFlag:      plot all envelopes, even ones that are not growth
%                      coupled (Default = false)
%    plotTools:        boolean (default = false) - add tools for editing the figure and its properties
%
% OUTPUT:
%    biomassValues:         Biomass values for plotting
%    targetUpperBounds:     Target upper bounds for plotting (
%                           biomassvalues x reactions)
%    targetLowerBounds:     Target lower bounds for plotting (
%                           biomassvalues x reactions)
%    plottedReactions:      Reactions that led to relevant side product
%
% .. Author: - Jeff Orth 8/16/07

if (nargin < 2)
    deletions = {};
end
if (nargin < 3)
    % Biomass flux
    biomassRxn = model.rxns(model.c==1);
end
if (nargin < 4)
    % Gene or rxn deletions
    geneDelFlag = false;
end
if (nargin < 5)
    nPts = 20;
end
if ~exist('plotTools','var')
    plotTools = false;
end

if ~exist('plotAllFlag','var')
    plotAllFlag = false;
end

% Create model with deletions
if (length(deletions) > 0)
    if (geneDelFlag)
        model = deleteModelGenes(model,deletions);
    else
        model = changeRxnBounds(model,deletions,zeros(size(deletions)),'b');
    end
end

%get all C exchange reactions
excRxns = model.rxns(findExcRxns(model,false,false));
CRxns = findCarbonRxns(model,1);
CExcRxns = intersect(excRxns,CRxns);
substrateIDs = find(model.lb(findRxnIDs(model,CExcRxns))<0);
%remove the substrate reactions
for i = 1:length(substrateIDs)
    j = substrateIDs(i);
    if j == 1
        CExcRxns = CExcRxns(2:length(CExcRxns));
    elseif j == length(CExcRxns)
        CExcRxns = CExcRxns(1:length(CExcRxns)-1);
    else
        CExcRxns = cat(1,CExcRxns(1:j-i),CExcRxns(j-i+2:length(CExcRxns)));
    end
end

% Run FBA to get upper bound for biomass
model = changeObjective(model,biomassRxn,1);
solMax = optimizeCbModel(model,'max');
solMin = optimizeCbModel(model,'min');

% Create biomass range vector
biomassValues = linspace(solMin.f,solMax.f,nPts);

plottedReactions = {};
targetUpperBounds = zeros(0,0);
targetLowerBounds = zeros(0,0);
% Max/min for target production
for i = 1:length(CExcRxns)
    model = changeObjective(model,CExcRxns(i),1);
    model2 = changeRxnBounds(model,biomassRxn,max(biomassValues),'b');
    fbasol2 = optimizeCbModel(model2,'max');
    maxRate = fbasol2.f; %find max production at max growth rate
    if (plotAllFlag)||(maxRate > getCobraSolverParams('LP','feasTol')) %Change to plot everything that is above the detection limit
        plottedReactions = [plottedReactions,CExcRxns(i)];
        targetUpperBounds(end+1,1) = 0;
        targetLowerBounds(end+1,1) = 0;
        for j = 1:length(biomassValues)
            model = changeRxnBounds(model,biomassRxn,biomassValues(j),'b');
            sol = optimizeCbModel(model,'max');
            if (sol.stat == 1)
                targetUpperBounds(end,j) = sol.f;
            elseif (sol.stat == 2)
                targetUpperBounds(end,j) = Inf;
            else
                targetUpperBounds(end,j) = NaN;
            end
            sol = optimizeCbModel(model,'min');
            if (sol.stat == 1)
                targetLowerBounds(end,j) = sol.f;
            elseif (sol.stat == 2)
                  targetUpperBounds(end,j) = Inf;
            else
                targetLowerBounds(end,j) = NaN;
            end
        end
    end
end

% Plot results
colors = {'b','g','r','c','m','y','k'};
for i = 1:length(plottedReactions)
    plot([biomassValues fliplr(biomassValues)],[targetUpperBounds(i,:) fliplr(targetLowerBounds(i,:))],colors{mod(i-1,length(colors))+1},'LineWidth',2)
    axis tight;
    hold on;
end
hold off;
legend(plottedReactions);
legend off;
ylabel('Production Rate (mmol/gDW h)');
xlabel('Growth Rate (1/h)');
if plotTools
    plottools, plotbrowser('on'), figurepalette('hide'), propertyeditor('off');
end
biomassValues = biomassValues';
targetLowerBounds = targetLowerBounds';
targetUpperBounds = targetUpperBounds';


%{
 * Copyright (C) 2013-2020, The Regents of The University of Michigan.
 * All rights reserved.
 * This software was developed in the Biped Lab (https://www.biped.solutions/)
 * under the direction of Jessy Grizzle, grizzle@umich.edu. This software may
 * be available under alternative licensing terms; contact the address above.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * The views and conclusions contained in the software and documentation are those
 * of the authors and should not be interpreted as representing official policies,
 * either expressed or implied, of the Regents of The University of Michigan.
 *
 * AUTHOR: Bruce JK Huang (bjhuang[at]umich.edu)
 * WEBSITE: https://www.brucerobot.com/
%}

function cost = verifyCornerAccuracy(X_verification, Y_verification, P)
    C_X_transformed = P * X_verification;
    C_X_transformed = C_X_transformed ./ C_X_transformed(3,:);
    cost = (norm(C_X_transformed(1:2,:) - Y_verification(1:2,:), 'fro'))^2;
end


function [wtRes, delRes] = simpleOptKnock(model, targetRxn, deletions, geneDelFlag, minGrowth, doubleDelFlag)
% Simple `OptKnock` is used to check all one gene or reaction deletions for
% growth-coupled metabolite production
%
% USAGE:
%
%    [wtRes, delRes] = simpleOptKnock(model, targetRxn, deletions, geneDelFlag, minGrowth, doubleDelFlag)
%
% INPUTS:
%    model:            COBRA model structure
%    targetRxn:        Target metabolite production reaction
%
% OPTIONAL INPUTS:
%    deletions:        Set of gene or reaction deletions to consider for `KO`
%                      (Default = all reactions)
%    geneDelFlag:      Gene deletion flag (Default = false)
%    minGrowth:        Minimum `KO` growth rate (Default = 0.05)
%    doubleDelFlag:    Double deletions (Default = false)
%
% OUTPUTS:
%    wtRes:            Wild type results
%    delRes:           Deletion strain results
%
% .. Author: - Markus Herrgard 2/14/07
%
% The results structures have fields:
%
%    * `growth` - Growth rate of strain
%    * `minProd` - Minimum prod rate of target metabolite
%    * `maxProd` - Maximum prod rate of target metabolite

if (nargin < 3)
    deletions = model.rxns;
end
if (nargin < 4)
    geneDelFlag = false;
end
if (nargin < 5)
    minGrowth = 0.05;
end
if (nargin < 6)
    doubleDelFlag = false;
end

tol = 1e-7;

%rxnGeneMat is a required field for this function, so if it does not exist,
%build it.
if ~isfield(model,'rxnGeneMat')
    model = buildRxnGeneMat(model);
end

% Number of deletions
nDel = length(deletions);

% Wild type values
solWT = optimizeCbModel(model);
grRounded = floor(solWT.f/tol)*tol;
modelWT = changeRxnBounds(model,model.rxns(model.c==1),grRounded,'l');
modelWT = changeObjective(modelWT,targetRxn,1);
solMax = optimizeCbModel(modelWT);
solMin = optimizeCbModel(modelWT);

wtRes.growth = solWT.f;
wtRes.maxProd = solMax.f;
wtRes.minProd = solMin.f;

if (doubleDelFlag)
    growthRate = sparse(nDel,nDel);
    maxProd = sparse(nDel,nDel);
    minProd = sparse(nDel,nDel);
else
    growthRate = zeros(nDel,1);
    maxProd = zeros(nDel,1);
    minProd = zeros(nDel,1);
end

if (~doubleDelFlag)
    showprogress(0,'Simple OptKnock in progress ...');
end
t0 = cputime;
delCounter = 0;
for i = 1:nDel
    if (~doubleDelFlag)
        if mod(i,10) == 0
            showprogress(i/nDel);
        end
    end
    if (geneDelFlag)
        % Gene deletion
        modelKO = deleteModelGenes(model,deletions{i});
    else
        % Reaction deletion
        modelKO = changeRxnBounds(model,deletions{i},0,'b');
    end
    % Calculate optimal growth rate
    solKO = optimizeCbModel(modelKO);
    %fprintf('Single %s %f\n',deletions{i},solKO.f);
    growthRate(i,1) = solKO.f;
    if (solKO.f > minGrowth && solKO.stat == 1)
        % Max & min production of the metabolite at the optimal growth rate
        grRounded = floor(solKO.f/tol)*tol;
        modelKO = changeRxnBounds(modelKO,modelKO.rxns(modelKO.c==1),grRounded,'l');
        modelKO = changeObjective(modelKO,targetRxn,1);
        solMax = optimizeCbModel(modelKO,'max');
        solMin = optimizeCbModel(modelKO,'min');
        if (~doubleDelFlag)
            maxProd(i,1) = solMax.f;
            minProd(i,1) = solMin.f;
        %fprintf('%f %f\n',solMax.f,solMin.f);
        else
            maxProd(i,i) = solMax.f;
            minProd(i,i) = solMin.f;
            for j = i+1:nDel
                delCounter = delCounter+1;
                if mod(j,50) == 0
                    fComp = delCounter/(nDel*(nDel-1)/2);
                    fprintf('%d\t%f\t%f\n',delCounter,100*fComp,(cputime-t0)/60);
                end
                if (geneDelFlag)
                    % Gene deletion
                    modelKO2 = deleteModelGenes(model,deletions{i});
                    modelKO2 = deleteModelGenes(modelKO2,deletions{j});
                else
                    modelKO2 = changeRxnBounds(model,deletions{i},0,'b');
                    modelKO2 = changeRxnBounds(modelKO2,deletions{j},0,'b');
                end
                % Calculate optimal growth rate
                solKO2 = optimizeCbModel(modelKO2);
                growthRate(i,j) = solKO2.f;
                %fprintf('Double %s %s %f\n',deletions{i},deletions{j},solKO2.f);
                if (solKO2.f > minGrowth && solKO2.stat == 1)
                    grRounded2 = floor(solKO2.f/tol)*tol;
                    modelKO2 = changeRxnBounds(modelKO2,modelKO2.rxns(modelKO2.c==1),grRounded2,'l');
                    modelKO2 = changeObjective(modelKO2,targetRxn,1);
                    solMax2 = optimizeCbModel(modelKO2,'max');
                    solMin2 = optimizeCbModel(modelKO2,'min');
                    if (solMin2.f > 0)
                        fprintf('%s %s %f %f %f\n',deletions{i},deletions{j},solKO2.f,solMax2.f,solMin2.f);
                    end
                    maxProd(i,j) = solMax2.f;
                    minProd(i,j) = solMin2.f;
                end
            end
        end
    end
end

% Store results
delRes.maxProd = maxProd;
delRes.minProd = minProd;
delRes.growth = growthRate;


function [suppressionIndex, pos_Vol, neg_Vol, pos_Vol_pRF, neg_Vol_pRF] = rmGetDoGSuppressionIndex(model,varargin)
%[suppressionIndex, pos_Vol, neg_Vol, pos_Vol_pRF, neg_Vol_pRF] = rmGetDoGSuppressionIndex(model,ROIcoords)
%
% rmGetDoGSuppressionIndex - calculates the suppression Index from the DoG model
% Calculates both the volume of the positive gaussian and the negative gaussian that make the
% pRF, it only takes into account the part of the gaussians that fall inside the stimulus window.
% The suppression index is the ratio of the negative and the positive
% volumes. Higher values indicate more suppression. (JOV Zuiderbaan et al.
% 2012)
%
% !! Needs at least the size of the stimulus (sts) as an input in varargin !!
% either by its direct value or by the params of the model:
% [suppressionIndex, pos_Vol, neg_Vol, pos_Vol_pRF, neg_Vol_pRF] = rmGetDoGSuppressionIndex(model,'sts',6.25)
% [suppressionIndex, pos_Vol, neg_Vol, pos_Vol_pRF, neg_Vol_pRF] = rmGetDoGSuppressionIndex(model,'rm',vw.rm)
%
% suppressionIndex          : ratio of the volumes of the negative and the positive gaussian that fall inside the stimuluswindow
% pos_Vol                   : volume of the positive gaussian that falls inside the stimuluswindow
% neg_Vol                   : volume of the negative gaussian that falls inside the stimuluswindow
% pos_Vol_pRF               : volume of the positive part of the pRF that falls inside the stimuluswindow
% neg_Vol_pRF               : volume of the negative part of the pRF that falls inside the stimuluswindow
%
% WZ  02/12: Wrote it

% set default parameters
params.sampleRate = 0.125;      % samplerate of the grid to make the gaussians
params.SI_Centered = false;     % by default the pRF has its original position on the grid, you can make it centered on (0,0)
                                % by giving this parameter the value true, using varargin

if nargin > 1,
    addArg = varargin;
    if numel(addArg) == 1,
        addArg=addArg{1};
    end;
else
    addArg = [];
end;

% parse command line inputs:
params = rmProcessVarargin(params,addArg);

% the stimulus size needs to be given
if ~isfield(params,'stimSize') || isempty(params.stimSize),
    disp('ERROR: Need size of the stimulus');
    error('Need size of the stimulus');
end

% make the stimuluswindow
mygridx = (-params.stimSize:params.sampleRate:params.stimSize);
[X2, Y2] = meshgrid(mygridx,mygridx);
Y2 = flipud(Y2);
X2 = X2(:);
Y2 = Y2(:);

ecc = sqrt(X2.^2 + Y2.^2);
keep = ecc > -params.stimSize & ecc < params.stimSize;
X = X2(keep);
Y = Y2(keep);

gridsize = params.sampleRate.^2;

sigma = model.sigma.major;
sigma2 = model.sigma2.major;
beta1 = model.beta(1,:,1);
beta2 = model.beta(1,:,2);
x = model.x0;
y = model.y0;

% if ROI indices are given, take only the data for that ROI
if isfield(params,'ROIindex') && ~isempty(params.ROIindex)
    sigma = sigma(params.ROIindex);
    sigma2 = sigma2(params.ROIindex);
    beta1 = beta1(params.ROIindex);
    beta2 = beta2(params.ROIindex);
    x = x(params.ROIindex);
    y = y(params.ROIindex);
end

suppressionIndex = zeros(1,numel(sigma2));
pos_Vol = zeros(1,numel(sigma2));
neg_Vol = zeros(1,numel(sigma2));
pos_Vol_pRF = zeros(1,numel(sigma2));
neg_Vol_pRF = zeros(1,numel(sigma2));

for k =1:numel(sigma2)
    if ~params.SI_Centered
        rfpositive = rfGaussian2d(X,Y,sigma(k),sigma(k),0,x(k),y(k));   % original position of the pRF in the stimulus window
        rfnegative = rfGaussian2d(X,Y,sigma2(k),sigma2(k),0,x(k),y(k));
    else
        rfpositive = rfGaussian2d(X,Y,sigma(k),sigma(k),0,0,0);         % center the pRF at (0,0)
        rfnegative = rfGaussian2d(X,Y,sigma2(k),sigma2(k),0,0,0);
    end
    rfposGaus = beta1(k).*rfpositive;
    rfnegGaus = beta2(k).*rfnegative;
    rftotal = rfposGaus + rfnegGaus;

    % calculate the volume of both gaussians that make the pRF
    pos_Vol(k) = (sum(rfposGaus)).*gridsize;
    neg_Vol(k) = -(sum(rfnegGaus)).*gridsize;
    ipos = rftotal > 0;
    ineg = rftotal < 0;
    % calculate the volume of the positive and negative part of the pRF
    pos_Vol_pRF(k) = (sum(rftotal(ipos))).*gridsize;
    neg_Vol_pRF(k) = -(sum(rftotal(ineg))).*gridsize;

    if beta2(k)>= 0;
        suppressionIndex(k) = 0;                % if beta2 is bigger or equal to zero, there is no suppression and the suppression index will be 0
    else
        suppressionIndex(k) = neg_Vol(k)./pos_Vol(k);
    end

end

function params = rmProcessVarargin(params,vararg)
if ~exist('vararg','var') || isempty(vararg), return; end
for n=1:2:numel(vararg),
    data = vararg{n+1};
    fprintf(1,' %s,',vararg{n});
    switch lower(vararg{n}),
        case {'ri','roi','roiindex', 'roi_index','roi index'}
            params.ROIindex = data;

        case {'sts','stimsize','size of the stimulus'}
            params.stimSize = data;

        case {'rm','rm params','params struct of the retinotopic model'}    %vw.rm
            params.stimSize = data.retinotopyParams.stim.stimSize;

        case {'sr','samplerate','samplerate of the grid'}
            params.sampleRate = data;

        case {'sic','si_centered','si centered','suppression index centered'}
            params.SI_Centered = logical(data);

        otherwise,
            fprintf(1,'[%s]:IGNORING unknown parameter: %s\n',...
                mfilename,vararg{n});
    end;
end;


function [y]=funcrs2(tt,fun,eps,y,nswp)
%Cross approximation of a function of a TT-tensor, Method 2
%   [Y]=FUNCRS2(TT,FUN,EPS,Y,NSWP)
%   Computes approximation to the function FUN(TT) with accuracy EPS
%   Auxiliary parameters:  Y (initial approximation), NSWP
%   (number of sweeps in the method
%   Much faster then usual cross by vectorized computation of subtensors
%
%
% TT-Toolbox 2.2, 2009-2012
%
%This is TT Toolbox, written by Ivan Oseledets et al.
%Institute of Numerical Mathematics, Moscow, Russia
%webpage: http://spring.inm.ras.ru/osel
%
%For all questions, bugs and suggestions please mail
%ivan.oseledets@gmail.com
%---------------------------

%PARAMETERS SECTION
rmin=1;
verb=true;
kick_rank=5;
if (~isempty(y))
   yold=y;
end
y1=y;
%For this procedure we need only local (!) indices, since it
%is more or less equivalent to orthogonalization;
d=tt.d;
ps=tt.ps;
core0=tt.core;
n=tt.n;
r=tt.r;

ry=y.r;
psy=y.ps;
cry=y.core;
phi=cell(d+1,1);
phi{d+1}=1;
phi{1}=1;
phx=cell(d+1,1);
phx{d+1}=1; %For storing submatrices in U & V
phx{1}=1;
%Warmup procedure: orthogonalize from right-to-left & maxvol
   cry_old=cry; %This is for checking the accuracy
   psy=cumsum([1;n.*ry(1:d).*ry(2:d+1)]);
   pos1=psy(d+1);
   %The first is the the o
   for i=d-1:-1:1
      %Do right-to-left SVD + maxvol (i.e., no fun() is employed, just the
      %current approximation)
      cr=cry(pos1-ry(i+1)*n(i+1)*ry(i+2):pos1-1);
      cr2=cry_old(psy(i):psy(i+1)-1);
      cr2=reshape(cr2,[ry(i)*n(i),ry(i+1)]);
      cr=reshape(cr,[ry(i+1),n(i+1)*ry(i+2)]);
      %cr=cr.';
      %[cr,rm]=qr(cr,0);
      %rm=2*eye(ry(i+1));
      %cr=cr/2;
      [u,s,v]=svd(cr,'econ'); s=diag(s);
      ry(i+1) = my_chop2(s,norm(s)*eps/sqrt(d-1));
      %cr = u * s * v', I think we should leave v orthogonal
      u=u(:,1:ry(i+1)); v=v(:,1:ry(i+1));
      s=s(1:ry(i+1)); u=u*diag(s);
      cr=conj(v); %cr is n(i+1),ry(i+2),ry(i+1) ---  No. it is conj(v)
      rm=u.'; %This is discussable --- maybe u' (or u.')?
      ry(i+1)=size(cr,2);
      %Maxvol should be computed in a different matrix
      cr0=reshape(cr,[n(i+1),ry(i+2),ry(i+1)]);
      cr0=permute(cr0,[3,1,2]);
      cr0=reshape(cr0,[ry(i+1)*n(i+1),ry(i+2)]);
      cr0=cr0*phx{i+2}.';
      cr0=reshape(cr0,[ry(i+1),n(i+1)*ry(i+2)]);
      cr0=cr0.';
      ind=maxvol2(cr0);
      r1=cr0(ind,:);
      phx{i+1}=r1;

      cr=cr.';

      cry(pos1-ry(i+1)*n(i+1)*ry(i+2):pos1-1)=cr(:);
      pos1=pos1-ry(i+1)*n(i+1)*ry(i+2);
      cr2=cr2*(rm).';
      cry(pos1-ry(i)*n(i)*ry(i+1):pos1-1)=cr2(:);
      %Take phi matrix; convolve from right with current cors of V
      cr0=core0(ps(i+1):ps(i+2)-1);
      cr0=reshape(cr0,[r(i+1)*n(i+1),r(i+2)]);
      cr0=cr0*phi{i+2}; %cr0 is now r(i)*n(i)*ry(i+1);
      cr0=reshape(cr0,[r(i+1),n(i+1)*ry(i+2)]);
      phi{i+1}=cr0(:,ind);
   end
   pos1=pos1-ry(1)*n(1)*ry(2);
   psy=cumsum([1;n.*ry(1:d).*ry(2:d+1)]);
   cry=cry(pos1:numel(cry));
   y.core=cry;
   y.r=ry;
   y.ps=psy;

   swp=1;
yold=[];
not_converged = true;
while ( swp < nswp && not_converged )
    max_er=0;
cry_old=cry; %This is for checking the accuracy
psy=cumsum([1;n.*ry(1:d).*ry(2:d+1)]);
pos1=1;
 for i=1:d-1
     %fprintf('i=%d \n',i);
     %We care for two cores, with number i & number i+1, and use
     %psi(i) and psi(i+2) as a basis; also we will need to recompute
     %psi(i+1)
     ps1=phi{i}; ps2=phi{i+2}; px1=phx{i}; px2=phx{i+2};
     %Compute (!) superblock and function (!) of it
     cr1=core0(ps(i):ps(i+1)-1);
     cr2=core0(ps(i+1):ps(i+2)-1);
     cr1=reshape(cr1,[r(i),n(i)*r(i+1)]);
     cr1=ps1*cr1;
     cr2=reshape(cr2,[r(i+1)*n(i+1),r(i+2)]);
     cr2=cr2*ps2;
     cr1=reshape(cr1,[ry(i)*n(i),r(i+1)]);
     cr2=reshape(cr2,[r(i+1),n(i+1)*ry(i+2)]);
     cr=cr1*cr2;
     %cr=reshape(cr,[ry(i)*n(i)*n(i+1),ry(i+2)]);
     cr=fun(cr); %Elements are evaluated here!
     cr=reshape(cr,[ry(i)*n(i)*n(i+1),ry(i+2)]);
     cr = cr/(px2.');
     cr=reshape(cr,[ry(i),n(i)*n(i+1)*ry(i+2)]);
     cr=px1 \cr;
     cr=reshape(cr,[ry(i)*n(i),n(i+1)*ry(i+2)]);
     %Check for local approximation of cr for the error
     cry1=cry(pos1:pos1+ry(i)*n(i)*ry(i+1)-1);
     %cry2=cry(pos1+ry(i)*n(i)*ry(i+1):pos1+ry(i)*n(i)*ry(i+1)+ry(i+1)*n(i+1)*ry(i+2)-1);
     %if ( swp == 2 )
     % if ( i == 5 )
     %   keyboard;
     % end
     %end
     cry2=cry_old(psy(i+1):psy(i+2)-1);

     cry1=reshape(cry1,[ry(i)*n(i),ry(i+1)]);
     cry2=reshape(cry2,[ry(i+1),n(i+1)*ry(i+2)]);
     appr=cry1*cry2;
     er=norm(appr-cr,'fro')/norm(cr,'fro');
     %keyboard;
     max_er=max(er,max_er);

     %Compute SVD of cr

     [u,s,v]=svd(cr,'econ');
     s=diag(s);
     r2=my_chop2(s,eps*norm(s)/sqrt(d-1));
     s=s(1:r2); u=u(:,1:r2); v=conj(v(:,1:r2));
     v=v*diag(s);

     %Kick rank of u
     ur=randn(size(u,1),kick_rank);
     %Orthogonalize ur to u by Golub-Kahan reorth <- it sucks!!
     [u,rv]=qr([u,ur], 0);
%      u=reort(u,ur);
%      radd=size(u,2)-r2;
%      if ( radd > 0 )
       vr=zeros(size(v,1),kick_rank);
       v=[v,vr];
       v = v*(rv.');
%      end
     r2=size(u,2);

     %ind=maxvol2(u);
     %r1=u(ind,:);
     %u=u/r1; v=v*r1'; v=v.';
     v=v.';
     ry(i+1)=r2;
     cry(pos1:pos1+ry(i)*n(i)*ry(i+1)-1)=u(:);
     pos1=pos1+ry(i)*n(i)*ry(i+1);
     cry(pos1:pos1+ry(i+1)*n(i+1)*ry(i+2)-1)=v(:);

     %Compute new maxvol and (new) submatrices
     u=reshape(u,[ry(i),n(i)*ry(i+1)]);
     u=px1*u;
     u=reshape(u,[ry(i)*n(i),ry(i+1)]);
     ind=maxvol2(u);
     phx{i+1}=u(ind,:);
     %Now we have to: cr1 with phi from the left
     phi{i+1}=cr1(ind,:); %phi{i+1}=phi{i+1};

 end
  %Truncate local memory
  cry=cry(1:pos1+ry(d)*n(d)*ry(d+1)-1);
  psy=cumsum([1;n.*ry(1:d).*ry(2:d+1)]);
y.core=cry;
y.r=ry;
y.ps=psy;
cry_old=cry;
%m=numel(cry);
%cry=[zeros(size(cry)),cry];
%pos1=pos1+m;
%cry2=cry_old(psy(i+1):psy(i+2)-1);
cry=cry(psy(d):psy(d+1)-1); %Start--only two cores
 for i=d-1:-1:1
     %fprintf('i=%d \n',i);
     %We care for two cores, with number i & number i+1, and use
     %psi(i) and psi(i+2) as a basis; also we will need to recompute
     %psi(i+1)
     ps1=phi{i}; ps2=phi{i+2}; px1=phx{i}; px2=phx{i+2};
     %Take current core; convolve it with
     %core=core(ps
     %Compute (!) superblock and function (!) of it
     cr1=core0(ps(i):ps(i+1)-1);
     cr2=core0(ps(i+1):ps(i+2)-1);
     cr1=reshape(cr1,[r(i),n(i)*r(i+1)]);
     cr1=ps1*cr1;
     cr2=reshape(cr2,[r(i+1)*n(i+1),r(i+2)]);
     cr2=cr2*ps2;
     cr1=reshape(cr1,[ry(i)*n(i),r(i+1)]);
     cr2=reshape(cr2,[r(i+1),n(i+1)*ry(i+2)]);
     cr=cr1*cr2;
     cr=reshape(cr,[ry(i)*n(i),n(i+1)*ry(i+2)]);
     cr=fun(cr); %Elements are evaluated here!
     cr=reshape(cr,[ry(i)*n(i)*n(i+1),ry(i+2)]);
     cr = cr/(px2.');
     cr=reshape(cr,[ry(i),n(i)*n(i+1)*ry(i+2)]);
     cr=px1 \cr;
     cr=reshape(cr,[ry(i)*n(i),n(i+1)*ry(i+2)]);
     %Check for local approximation of cr for the error
     %cry1=cry(pos1-ry(i)*n(i)*ry(i+1):pos1-1);
     cry1=cry_old(psy(i):psy(i+1)-1);
     %cry2=cry(ry(:ry(i+1)*n(i+1)*ry(i+2));
     %cry1=cry(1:ry(i)*n(i)*ry(i+1));
     %cry2=cry(ry(i)*n(i)*ry(i+1)+1:ry(i)*n(i)*ry(i+1)+ry(i+1)*n(i+1)*ry(i+2));
     cry2=cry(1:ry(i+1)*n(i+1)*ry(i+2));
     cry(1:ry(i+1)*n(i+1)*ry(i+2))=[];
     %cry2=cry_old(psy(i+1):psy(i+2)-1);
     %cry(1:(ry(i+1)*n(i+1)*ry(i+2)))=[]; %Delete both of first cores
     %cry(1:ry(i)*n(i)*ry(i+1)+ry(i+1)*n(i+1)*ry(i+2))=[];
     cry1=reshape(cry1,[ry(i)*n(i),ry(i+1)]);
     cry2=reshape(cry2,[ry(i+1),n(i+1)*ry(i+2)]);
     appr=cry1*cry2;
     er=norm(appr-cr,'fro')/norm(cr,'fro');
     %er
          max_er=max(er,max_er);

     %Compute SVD of cr
     [u,s,v]=svd(cr,'econ');
     s=diag(s);
     r2=my_chop2(s,eps*norm(s)/sqrt(d-1));
     s=s(1:r2); u=u(:,1:r2); v=v(:,1:r2);

     v = conj(v); % <- bug was here

     %Make it standard
     u=u*diag(s);

     %Kick rank

      vr=randn(size(v,1),kick_rank);
      [v,rv]=qr([v,vr], 0);
%       v=reort(v,vr);
%       radd=size(v,2)-r2;
%       if ( radd > 0 )
         ur=zeros(size(u,1),kick_rank);
         u=[u,ur];
         u = u*(rv.');
%       end
      r2=size(v,2);

     v=v.';v0=v;
     %Compute new phi;
     ry(i+1)=r2;
     u=u(:); u=u.'; v=v(:); v=v.';
     cry=[u,v,cry];
     %keyboard;
     %We need new memory for
     %cry(pos1:pos1+ry(i+1)*n(i+1)*ry(i+2)-1)=v(:); %Here memory has to be (?) enlarged
     %if ( pos1 <= ry(i)*n(i)*ry(i+1) ) %Have to enlarge memory
     %  cry=cry(pos1:numel(cry));
     %  cry=[u(:)',cry];
     %  pos1=ry(i)*n(i)*ry(i+1)+1;
     %else
     %   pos1=pos1-ry(i)*n(i)*ry(i+1);
      %  cry(pos1:pos1+ry(i)*n(i)*ry(i+1)-1)=u(:);
     %end
     %Compute new maxvol and (new) submatrices
     v0=reshape(v0,[ry(i+1)*n(i+1),ry(i+2)]);
     v0=v0*px2.';
     v0=reshape(v0,[ry(i+1),n(i+1)*ry(i+2)]);
     v0=v0.';
     ind=maxvol2(v0);
     phx{i+1}=v0(ind,:);
     %Now we have to: cr1 with phi from the left
     phi{i+1}=cr2(:,ind);

 end

  psy=cumsum([1;n.*ry(1:d).*ry(2:d+1)]);

y.core=cry;
y.r=ry;
y.ps=psy;
if ( isempty(yold) )
 yold=y;
 er_nrm=1;
else

   er_nrm=norm(yold-y)/norm(y);
   yold=y;
end
 fprintf('sweep=%d, er=%3.2e er_nrm=%3.2e \n',swp,max_er,er_nrm);

swp=swp+1;
end
  psy=cumsum([1;n.*ry(1:d).*ry(2:d+1)]);

y.core=cry;
y.r=ry;
y.ps=psy;

end


function N1 = plus(N1, N2)
%PLUS   Plus for CHEBOP2.
%
% N = PLUS(N1, N2) is the same as N = N1 + N2.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

% Check domains are the same.
if ( all( N1.domain ~= N1.domain ) )
    error('CHEBFUN:CHEBOP2:plus:domain', ...
        'Domains of operators must be identical.');
end

% Plus the coefficient matrices together.
A = N1.coeffs;
B = N2.coeffs;

% PLUS the coefficient cellarrays or matrices together:
if ( iscell(A) && iscell(B) )
    C = cell(max(size(A, 1), size(B, 1)), max(size(A, 2), size(B, 2)));
    if size(B, 1) < size(C, 1)
        B{size(C, 1),1} = [];
    end
    if ( size(B, 2) < size(C, 2) )
        B{1,size(C, 2)} = [];
    end
    if ( size(A, 1) < size(C, 1) )
        A{size(C, 1),1} = [];
    end
    if ( size(A, 2) < size(C, 2) )
        A{1,size(C, 2)} = [];
    end
    for jj = 1:size(C, 1)
        for kk = 1:size(C, 2)
            if ( isempty(B{jj,kk}) )
                C{jj,kk} = A{jj,kk};
            elseif ( isempty(A{jj,kk}) )
                C{jj,kk} = B{jj,kk};
            else
                C{jj,kk} = A{jj,kk} + B{jj,kk};
            end
        end
    end

elseif ( iscell(A) )
    BB = cell(size(B, 2), size(B, 1));
    for jj = 1:size(B, 1)
        for kk = 1:size(B, 2)
            BB(kk,jj) = {B(jj,kk)};
        end
    end
    N2.coeffs = BB;
    N1 = plus(N1, N2);
    return

elseif ( iscell(B) )
    AA = cell(size(A, 2), size(A, 1));
    for jj = 1:size(A, 1)
        for kk = 1:size(A, 2)
            AA(kk,jj) = {A(jj,kk)};
        end
    end
    N1.coeffs = AA;
    N1 = plus(N1, N2);
    return

else
    C = zeros(max(size(A, 1), size(B, 1)), max(size(A, 2), size(B, 2)));
    if size(B, 1) < size(C, 1)
        B(size(B, 1)+1:size(C, 1),:) = 0;
    end
    if size(B, 2) < size(C, 2)
        B(:,size(B, 2)+1:size(C, 2)) = 0;
    end
    if size(A, 1) < size(C, 1)
        A(size(A, 1)+1:size(C, 1),:) = 0;
    end
    if size(A, 2) < size(C, 2)
        A(:,size(A, 2)+1:size(C, 2)) = 0;
    end
    C = A + B;
end

% Loop over to remove empty cells (replace with zeros).
for jj = 1:size(C, 1)
    for kk = 1:size(C, 2)
        if ( isempty(C{jj,kk}) )
            C{jj,kk} = 0;
        end
    end
end

% Do not add boundary conditions together. Ignore them.
if ( ~isempty(N1.lbc) || ~isempty(N1.rbc) ||...
        ~isempty(N1.ubc) || ~isempty(N1.dbc) )
    warning('CHEBFUN:CHEBOP2:plus:ignoredBCs', 'BCs were ignored.');
    N1.lbc =[];
    N1.rbc =[];
    N1.ubc =[];
    N1.dbc =[];
end
if ( ~isempty(N2.lbc) || ~isempty(N2.rbc) ||...
        ~isempty(N2.ubc) || ~isempty(N2.dbc) )
    warning('CHEBFUN:CHEBOP2:plus:ignoredBCs', 'BCs were ignored.');
    N2.lbc =[];
    N2.rbc =[];
    N2.ubc =[];
    N2.dbc =[];
end

% Assign to output:
N1.coeffs = C;
N1.xorder = max(N1.xorder, N2.xorder);
N1.yorder = max(N1.yorder, N2.yorder);
% Plus the ops together:
op1 = N1.op;
op2 = N2.op;
N1.op = @(u) op1(u) + op2(u);

end


function [d pred f]=astar_search(A,s,h,varargin)
% ASTAR_SEARCH Perform a heuristically guided (A*) search on the graph.
%
% [d pred rank]=astar_search(A,s,h,optionsu) returns the distance map,
% search tree and f-value of each node in an astar_search.
% The search begins at vertex s.  The heuristic h guides the search,
% h(v) should be small close to a goal and large far from a goal.  The
% heuristic h can either be a vector with an entry for each vertex in the
% graph or a function which maps vertices to values.
%
% This method works on non-negatively weighted directed graphs.
% The runtime is O((E+V)log(V)).
%
% ... = astar_search(A,u,...) takes a set of
% key-value pairs or an options structure.  See set_matlab_bgl_options
% for the standard options.
%   options.visitor: a visitor to use with the A* search (see Note)
%   options.inf: the value to use for unreachable vertices
%       [double > 0 | {Inf}]
%   options.target: a special vertex that will stop the search when hit
%       [{'none'} | any vertex number besides the u]; this is ignored if
%       a visitor is set.
%   options.edge_weight: a double array over the edges with an edge
%       weight for each node, see EDGE_INDEX and EXAMPLES/REWEIGHTED_GRAPHS
%       for information on how to use this option correctly
%       [{'matrix'} | length(nnz(A)) double vector]
%
% Note: You can specify a visitor for this algorithm.  The visitor has the
% following optional functions.
%    vis.initialize_vertex(u)
%    vis.discover_vertex(u)
%    vis.examine_vertex(u)
%    vis.examine_edge(ei,u,v)
%    vis.edge_relaxed(ei,u,v)
%    vis.edge_not_relaxed(ei,u,v)
%    vis.black_target(ei,u,v)
%    vis.finish_vertex(u)
% Each visitor parameter should be a function pointer, which returns 0
% if the search should stop.  (If the function does not return anything,
% the algorithm continues.)
%
% Example:
%   load graphs/bgl_cities.mat
%   goal = 11; % Binghamton
%   start = 9; % Buffalo
%   % Use the euclidean distance to the goal as the heuristic
%   h = @(u) norm(xy(u,:) - xy(goal,:));
%   % Setup a routine to stop when we find the goal
%   ev = @(u) (u ~= goal);
%   [d pred f] = astar_search(A, start, h, ...
%       struct('visitor', struct('examine_vertex', ev)));

% David Gleich
% Copyright, Stanford University, 2006-2008

%% History
%  2007-04-20: Added edge weight option
%  2007-07-12: Fixed edge_weight documentation.
%  2008-10-07: Changed options parsing
%%

[trans check full2sparse] = get_matlab_bgl_options(varargin{:});
if full2sparse && ~issparse(A), A = sparse(A); end

options = struct('inf', Inf, 'edge_weight', 'matrix', 'target', 'none');
options = merge_options(options,varargin{:});

edge_weight_opt = 'matrix';

if strcmp(options.edge_weight, 'matrix')
    % do nothing if we are using the matrix weights
else
    edge_weight_opt = options.edge_weight;
end

if strcmp(options.target,'none')
    target = 0; % a flag used to denote "no target" to the mex
elseif isa(options.target, 'double')
    target = options.target;
else
    error('matlab_bgl:invalidParameter', ...
        'options.target is not ''none'' or a vertex number.');
end

if check, check_matlab_bgl(A,struct()); end
if trans, A = A'; end

function hi=vec2func(u)
    hi = h(u);
end

if isa(h,'function_handle')
    hfunc = h;
else
    hfunc = @vec2func;
end

if isfield(options,'visitor')
    [d pred f] = astar_search_mex(A,s,target,hfunc,options.inf,edge_weight_opt,options.visitor);
else
    [d pred f] = astar_search_mex(A,s,target,h,options.inf,edge_weight_opt);
end

% end the main function
end


function cS = corundum

cs_Crn = crystalSymmetry('-32/m',[1 1 2.729]);
N = Miller({0,0,1},{1,0,2},{1,1,3},{2,-1,-1},{1,1,-1},{7,-14,-3},cs_Crn);
dist = [0.5, 1.49, 2.35, 1.53, 9.95, 9.95];
cS = crystalShape(N./dist);


classdef dm_converter_mpc2_legacy < mp.dm_converter_mpc2
%MP.DM_CONVERTER_MPC2_LEGACY  MATPOWER data model converter for MATPOWER case v2.

%   MATPOWER
%   Copyright (c) 2021-2022, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%
%   This file is part of MATPOWER.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://matpower.org for more info.

%     properties
%     end     %% properties

    methods
        function dm = legacy_user_mod_inputs(obj, dm, mpopt, dc)
            %% pre-process input related to user vars, constraints, costs

            %% create (read-only) copies of individual fields for convenience
            mpc = dm.source;
            [~, bus, gen, ~, ~, A, l, u, mpopt, ...
                N, fparm, H, Cw, z0, zl, zu, ~] = opf_args(mpc, mpopt);

            %% data dimensions
            nb   = size(bus, 1);    %% number of buses
            ng   = size(gen, 1);    %% number of dispatchable injections
            nlin = size(A, 1);      %% number of linear user constraints
            nw = size(N, 1);        %% number of general cost vars, w
            if dc
                %% reduce A and/or N from AC dimensions to DC dimensions, if needed
                if nlin || nw
                    acc = [nb+(1:nb) 2*nb+ng+(1:ng)];   %% Vm and Qg columns
                    if nlin && size(A, 2) >= 2*nb + 2*ng
                        %% make sure there aren't any constraints on Vm or Qg
                        if any(any(A(:, acc)))
                            error('mp.dm_converter_mpc2_legacy/legacy_user_mod_inputs: attempting to solve DC OPF with user constraints on Vm or Qg');
                        end
                        A(:, acc) = [];         %% delete Vm and Qg columns
                    end
                    if nw && size(N, 2) >= 2*nb + 2*ng
                        %% make sure there aren't any costs on Vm or Qg
                        if any(any(N(:, acc)))
                            [ii, jj] = find(N(:, acc));
                            ii = unique(ii);    %% indices of w with potential non-zero cost terms from Vm or Qg
                            if any(Cw(ii)) || (~isempty(H) && any(any(H(:, ii))))
                                error('mp.dm_converter_mpc2_legacy/legacy_user_mod_inputs: attempting to solve DC OPF with user costs on Vm or Qg');
                            end
                        end
                        N(:, acc) = [];         %% delete Vm and Qg columns
                    end
                end
                nv = 0;
                nq = 0;
            else
                nv = nb;
                nq = ng;
            end

            %% get number of user vars, check consistency
            nx = nb+nv + ng+nq;  %% number of standard OPF control variables
            if nlin
                nz = size(A, 2) - nx;   %% number of user z variables
                if nz < 0
                    error('mp.dm_converter_mpc2_legacy/legacy_user_mod_inputs: user supplied A matrix must have at least %d columns.', nx);
                end
            else
                nz = 0;               %% number of user z variables
                if nw                 %% still need to check number of columns of N
                    if size(mpc.N, 2) ~= nx;
                        error('mp.dm_converter_mpc2_legacy/legacy_user_mod_inputs: user supplied N matrix must have %d columns.', nx);
                    end
                end
            end

            %% package up parameters
            z = struct( 'nz', nz, ...           %% num user variables
                        'z0', z0, ...
                        'zl', zl, ...
                        'zu', zu    );
            lin = struct(   'nlin', nlin, ...   %% num user linear constraints
                            'A', A, ...
                            'l', l, ...
                            'u', u  );
            cost =  struct( 'nw', nw, ...       %% num user cost rows
                            'N', N, ...
                            'Cw', Cw    );
            nlc = obj.legacy_user_nln_constraints(dm, mpopt);

            if ~isempty(H)
                cost.H = H;
            end
            if ~isempty(fparm)
                cost.dd = fparm(:, 1);
                cost.rh = fparm(:, 2);
                cost.kk = fparm(:, 3);
                cost.mm = fparm(:, 4);
            end
            dm.userdata.legacy_opf_user_mods = struct( ...
                'z', z, ...
                'lin', lin, ...
                'nlc', {nlc}, ...
                'cost', cost );
        end

        function uc = legacy_user_nln_constraints(obj, dm, mpopt)
            mpc = dm.source;

            %% check for user-defined nonlinear constraints
            nnle = 0;   %% number of nonlinear user-defined equality cons
            nnli = 0;   %% number of nonlinear user-defined inequality cons
            if isfield(mpc, 'user_constraints')
                if isfield(mpc.user_constraints, 'nle')
                    for k = 1:length(mpc.user_constraints.nle)
                        nnle = nnle + mpc.user_constraints.nle{k}{2};
                    end
                end
                if isfield(mpc.user_constraints, 'nli')
                    for k = 1:length(mpc.user_constraints.nli)
                        nnli = nnli + mpc.user_constraints.nli{k}{2};
                    end
                end
            end

            %% initialize cell array for add_nln_constraint() args
            uc = cell(nnle+nnli, 1);

            %% user-defined nonlinear equalities
            if nnle
                for k = 1:length(mpc.user_constraints.nle)
                    nlc = mpc.user_constraints.nle{k};
                    fcn  = eval(['@(x)' nlc{3} '(x, nlc{6}{:})']);
                    hess = eval(['@(x, lam)' nlc{4} '(x, lam, nlc{6}{:})']);
                    uc{k} = {nlc{1:2}, 1, fcn, hess, nlc{5}};
                end
            end

            %% user-defined nonlinear inequalities
            if nnli
                for k = 1:length(mpc.user_constraints.nli)
                    nlc = mpc.user_constraints.nli{k};
                    fcn  = eval(['@(x)' nlc{3} '(x, nlc{6}{:})'])
                    hess = eval(['@(x, lam)' nlc{4} '(x, lam, nlc{6}{:})'])
                    uc{nnle+k} = {nlc{1:2}, 0, fcn, hess, nlc{5}};
                end
            end
        end
    end     %% methods
end         %% classdef


function cx = csscal ( n, sa, cx, incx )

%*****************************************************************************80
%
%% CSSCAL scales a complex vector by a real constant.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    14 April 2006
%
%  Author:
%
%    MATLAB version by John Burkardt
%
%  Reference:
%
%    Jack Dongarra, Jim Bunch, Cleve Moler, Pete Stewart,
%    LINPACK User's Guide,
%    SIAM, 1979,
%    ISBN13: 978-0-898711-72-1,
%    LC: QA214.L56.
%
%    Charles Lawson, Richard Hanson, David Kincaid, Fred Krogh,
%    Basic Linear Algebra Subprograms for Fortran Usage,
%    Algorithm 539,
%    ACM Transactions on Mathematical Software,
%    Volume 5, Number 3, September 1979, pages 308-323.
%
%  Parameters:
%
%    Input, integer N, the number of entries in the vector.
%
%    Input, real SA, the multiplier.
%
%    Input, complex CX(*), the vector to be scaled.
%
%    Input, integer INCX, the increment between successive entries of
%    the vector CX.
%
%    Output, complex CX(*), the scaled vector.
%
  cx(1:incx:1+(n-1)*incx) = sa * cx(1:incx:1+(n-1)*incx);

  return
end


function [p_, sdp_, c_, sdc_, dk_, sdk_, aa_, bb_]=mypval2(var1, mati)

    % clpvla.m                            A.Allmann
    % function to calculate the parameters of the modified Omori Law
    %
    %
    %
    % this function is a modification of a program by Paul Raesenberg
    % that is based on Programs by Carl Kisslinger and Yoshi Ogata
    %
    % function finds the maximum liklihood estimates of p,c and k, the
    % parameters of the modifies Omori equation
    % it also finds the standard deviations of these parameters
    %
    % Input: Earthquake Catalog of an Cluster Sequence
    %
    % Output: p c k values of the modified Omori Law with respective
    %         standard deviations
    %         A and B values of the Gutenberg Relation based on k
    %
    % Create an input window for magnitude thresholds and
    % plot cumulative number versus time to allow input of start and end
    % time

    global backcat ttcat
    global clu1 pyy tmp1 tmp2 tmp3 tmp4 difp
    global xt cumu cumu2
    global p c dk tt pc loop nn pp nit t err1x err2x ieflag isflag
    global cstep pstep tmpcat ts tend eps1 eps2
    global sdc sdk sdp  aa bb pcheck loopcheck
    global autop tmeqtime tmvar
    %if var1 == 3
    tmvar=[];
    %input of parameters(Magnitude,Time)
    ZG.newt2=ttcat;              %function operates with single cluster
    autop=0;
    if var1==4
        autop=1;
    end
    %calculate start -end time of overall catalog
    t0b = min(ZG.newt2.Date);
    teb = max(ZG.newt2.Date);
    tdiff=days(teb-t0b);       %time difference in days

    par3=tdiff/100;

    par5=par3;
    if par5>.5
        par5=par5/5;
    end

    % calculate cumulative number versus time and bin it
    %
    n = ZG.newt2.Count;
    [cumu, xt] = hist(ZG.newt2.Date,(t0b:days(par3):teb));
    [cumu, xt] = hist(days(ZG.newt2.Date-ZG.newt2.Date(1)),(0:par5:tdiff)); %WHY BOTH?
    difp= [0 diff(cumu)];
    cumu2 = cumsum(cumu);

    % find start time of time series
    %
    nn=find(cumu==max(cumu));
    nnn=nn(1,1)-2;
    tmvar=1;           %temperal variable
    if par3>=1
        tmp3=t0b+nnn*days(par3);
    else
        tmp3=nnn*par5;
    end
    tmp3 = mati;
    tmp2=min(ttcat.Magnitude);
    tmp1=max(ttcat.Magnitude);

    tmp3=max(0,tmp3);

    tmp4=teb;

    %%end

    %cumputation part after parameter input
    %elseif var1==8  | var1==6 | var1==7
    %set the error test values
    eps1=.0005;
    eps2=.0005;

    %set the parameter starting values
    PO=1.1;
    CO=0.1;

    %set the initial step size
    pstep=.05;
    cstep=.05;
    pp=PO;
    pc=CO;
    nit=0;
    ieflag=0;
    isflag=0;
    pcheck=false;
    err1x=0;
    err2x=0;
    ts=0.0000001;
    if autop ~= 1             %input was manual

        %Build timecatalog

        mains=find(ttcat.Magnitude == max(ttcat.Magnitude),1);
        mains=ttcat.subset(mains);         %biggest shock in sequence (first one only!)
        assert(mains.Count == 1);
        if par3<0.001
            daycriteria1= ttcat.Date>=days(tmp3)+ttcat.Date(1);
            daycriteria2= ttcat.Date<=days(tmp4)+ttcat.Date(1);
            tmpcat = ttcat.subset(daycriteria1&daycriteria2);
            tmp6=days(tmp3)+ttcat.Date(1);
        else
            tmpcat=ttcat.subset(tmp3<=ttcat.Date & ttcat.Date<=tmp4);
            tmp6=tmp3;
        end
        tmpcat=tmpcat.subset(tmp2<=tmpcat.Magnitude & tmpcat.Magnitude<=tmp1);
        if var1 ==6 || var1==7
            tmpcat=tmpcat.subset(tmpcat.Date>mains.Date);
            tmpcat=[mains; tmpcat];
            ts=(tmp6-mains.Date); %# days
            ts=max(0.0000001,ts)
        end
        tmeqtime=clustime(tmpcat);
        tmeqtime=tmeqtime-tmeqtime(1);     %time in days relative to first eq
        tmeqtime=tmeqtime(2:length(tmeqtime));

        %automatic estimate works with whole sequence
    else
        tmeqtime=clustime(ttcat);
        tmeqtime=tmeqtime-tmeqtime(1);
        tmeqtime=tmeqtime(2:length(tmeqtime));

    end

    tp1 = input('tp1=   ');
    tp2 = input('tp2=    ');
    ts = tp1;
    l = tmeqtime >= tp1 & tmeqtime <= tp2;
    tmeqtime = tmeqtime(l);

    tend=tmeqtime(length(tmeqtime)); %end time

    %Loop begins here
    nn=length(tmeqtime);
    loop=0;
    loopcheck=0;
    tt=tmeqtime(nn)+1;
    t=tmeqtime;
    if pc < 0 ; pc = 0.0; end
    if pc <= ts; pc = ts + 0.05;end

    MIN_CSTEP = 0.000001;
    MIN_PSTEP = 0.00001;

    ploop_c_and_p_calcs(MIN_CSTEP, MIN_PSTEP, true,'kpc'); %call of function who calculates parameters

    if loopcheck<500
        %round values on two digits
        p=round(p, -2);
        sdp=round(sdp, -2);
        c=round(c, -3);
        sdc=round(sdc, -3);
        dk=round(dk, -2);
        sdk= round(sdk, -2);
        aa=round(aa, -2);
        bb=round(bb, -2);

        disp(['p = ' num2str(p)  ' +/- ' num2str(sdp)]);
        disp(['c = ' num2str(c)  ' +/- ' num2str(sdc)]);
        disp(['k = ' num2str(dk)  ' +/- ' num2str(sdk)]);
        disp(['b = ' num2str(bb)  ' +/- ' num2str(sdp)]);
        disp(['a = ' num2str(aa)  ' +/- ' num2str(sdp)]);
    else
        disp('No result');
        %p = nan;
        %c = nan;
        %k = nan;
        %bb = nan;
        %aa = nan;

        % p = nan; sdp = nan;
    end
    if autop~=1
        if par3>=1
            tdiff = round(tmpcat(length(tmpcat(:,1)),3)-tmpcat(1,3));
        else
            tdiff = (tmpcat(length(tmpcat(:,1)),3)-tmpcat(1,3))*365;
        end
        % set arrays to zero
        %
        if par3>=1
            cumu = 0:1:(tdiff/days(par3))+1;
            cumu2 = 0:1:(tdiff/days(par3))-1;
        else
            par5=par3/5;
            cumu = 0:par5:tdiff+2*par3;
            cumu2 =  0:par5:tdiff-1;
        end
        cumu = zeros(size(cumu));
        cumu2 = zeros(size(cumu2));
        %
    end
    tmvar=[];
    %end;

    p_=p;
    sdp_=sdp;
    c_=c;
    sdc_=sdc;
    dk_=dk;
    sdk_=sdk;
    aa_=aa;
    bb_=bb;
end


%-------------------------------------------------------------------------------------------------------------------%
%
% IB2d is an Immersed Boundary Code (IB) for solving fully coupled non-linear
% 	fluid-structure interaction models. This version of the code is based off of
%	Peskin's Immersed Boundary Method Paper in Acta Numerica, 2002.
%
% Author: Nicholas A. Battista
% Email:  nick.battista@unc.edu
% Date Created: May 27th, 2015
% Institution: UNC-CH
%
% This code is capable of creating Lagrangian Structures using:
% 	1. Springs
% 	2. Beams (*torsional springs)
% 	3. Target Points
%	4. Muscle-Model (combined Force-Length-Velocity model, "Hill+(Length-Tension)")
%
% One is able to update those Lagrangian Structure parameters, e.g., spring constants, resting lengths, etc
%
% There are a number of built in Examples, mostly used for teaching purposes.
%
% If you would like us to add a specific muscle model, please let Nick (nick.battista@unc.edu) know.
%
%--------------------------------------------------------------------------------------------------------------------%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% FUNCTION: updates the target point positions
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function targets = update_Target_Point_Positions(dt,current_time,targets)

IDs = targets(:,1);                 % Stores Lag-Pt IDs in col vector
%xPts= targets(:,2);                % Original x-Values of x-Target Pts.
yPts= targets(:,3);                 % Original y-Values of y-Target Pts.
%kStiffs = targets(:,4);             % Stores Target Stiffnesses

N_target = length(targets(:,1));    %Gives total number of target pts!

% NEEDED FOR UPDATE FILE FROM CORAL GEOMETRY! %
N_half = 90;                        % # of pts. on ONE tentacle

    %frames per second in data
     F = 30.0;
     %pixels per dm in data
     S = 1733.333333333333333333;
     %time in frames (mod one pulse)
     t =  F*mod(current_time,2.30);
     tp = F*2.25;
     tend = F*2.30;
     %initial offset from center of the domain in dm
     xoffset = 0.019;
     yoffset = 0.25;
     xoffset2grid1 = 0.2;
     xoffset2grid2 = 0.4;

     qc11 = -8.018203211249667e-13;
     qc12 = 1.706310971452060e-10;
     qc13 = -1.371526883228119e-8;
     qc14 = 5.116506115621489e-7;
     qc15 = -8.613944795521634e-6;
     qc16 = 4.951095471423292e-5;
     qc17 = -1.237254488643602e-4;
     qc21 = -1.093988700839764e-10;
     qc22 = 2.370737151797367e-8;
     qc23 = -1.946684756263760e-6;
     qc24 = 7.447999061932255e-5;
     qc25 = -1.285757196003425e-3;
     qc26 = 7.184871148722453e-3;
     qc27 = -2.357284935085283e-2;
     qc31 = -3.111491615328405e-9;
     qc32 = 6.922752279229525e-7;
     qc33 = -5.951659336196739e-5;
     qc34 = 2.461541908755476e-3;
     qc35 = -4.830999119980220e-2;
     qc36 = 3.293063238297988e-1;
     qc37 = -1.893567487439901;
     qc41 = 2.028150369106308e-8;
     qc42 = -4.188949936239104e-6;
     qc43 = 3.237380275082298e-4;
     qc44 = -1.158763275070524e-2;
     qc45 = 1.908244836752949e-1;
     qc46 = -1.142372536532416;
     qc47 = 3.584246435337805;

    if (t<=tp)

        qpoly1=qc17+qc16*t+qc15*t*t+qc14*t*t*t+qc13*t*t*t*t+qc12*t*t*t*t*t+qc11*t*t*t*t*t*t;
        qpoly2=qc27+qc26*t+qc25*t*t+qc24*t*t*t+qc23*t*t*t*t+qc22*t*t*t*t*t+qc21*t*t*t*t*t*t;
        qpoly3=qc37+qc36*t+qc35*t*t+qc34*t*t*t+qc33*t*t*t*t+qc32*t*t*t*t*t+qc31*t*t*t*t*t*t;
        qpoly4=qc47+qc46*t+qc45*t*t+qc44*t*t*t+qc43*t*t*t*t+qc42*t*t*t*t*t+qc41*t*t*t*t*t*t;

    else
        qpolytf1=qc17+qc16*tp+qc15*tp*tp+qc14*tp*tp*tp+qc13*tp*tp*tp*tp+qc12*tp*tp*tp*tp*tp+qc11*tp*tp*tp*tp*tp*tp;
        qpolytf2=qc27+qc26*tp+qc25*tp*tp+qc24*tp*tp*tp+qc23*tp*tp*tp*tp+qc22*tp*tp*tp*tp*tp+qc21*tp*tp*tp*tp*tp*tp;
        qpolytf3=qc37+qc36*tp+qc35*tp*tp+qc34*tp*tp*tp+qc33*tp*tp*tp*tp+qc32*tp*tp*tp*tp*tp+qc31*tp*tp*tp*tp*tp*tp;
        qpolytf4=qc47+qc46*tp+qc45*tp*tp+qc44*tp*tp*tp+qc43*tp*tp*tp*tp+qc42*tp*tp*tp*tp*tp+qc41*tp*tp*tp*tp*tp*tp;

        qpoly1 = qpolytf1*((tend-tp)-(t-tp))/(tend-tp)+qc17*(t-tp)/(tend-tp);
        qpoly2 = qpolytf2*((tend-tp)-(t-tp))/(tend-tp)+qc27*(t-tp)/(tend-tp);
        qpoly3 = qpolytf3*((tend-tp)-(t-tp))/(tend-tp)+qc37*(t-tp)/(tend-tp);
        qpoly4 = qpolytf4*((tend-tp)-(t-tp))/(tend-tp)+qc47*(t-tp)/(tend-tp);
    end

for i=1:N_target                    % Loops over all target points!

    shifted_Xt1 = yPts(i)-yoffset;

    X_target = xoffset+qpoly4/S-qpoly3*shifted_Xt1+qpoly2*S*shifted_Xt1*shifted_Xt1-qpoly1*S*S*shifted_Xt1*shifted_Xt1*shifted_Xt1;

    if i<= N_target/2
        if i<=N_half
            X_target = -X_target+ xoffset2grid1;
        else
            X_target = X_target + xoffset2grid1;
        end
    else
        if i<=N_target/2+N_half
            X_target = -X_target+ xoffset2grid2;
        else
            X_target = X_target + xoffset2grid2;
        end
    end
    targets(IDs(i),2) = X_target;      % Store new xVals
    %targets(IDs(i),3) = yPts(IDs(i)); % Store new yVals

end


function [x,info] = mgfracLapP1P2(A,b,elem,option,varargin)
%% MGFRACLAPP1P2 multigrid solvers for fractional Laplacian
%
%
% See also mg
%
% Documentation in Help browser ifem mgdoc
%
% Copyright (C) Long Chen. See COPYRIGHT.txt for details.

tic;

%% Size of systems
Ndof = length(b);               % number of dof
N = max(elem(:));               % number of nodes
Nydof = Ndof/N;                 % number of dof in the extend direction
Ny = (Nydof + 1)/2;             % number of nodes in the extend direction
% NT = size(elem,1);                 % number of elements
dim = size(elem,2)-1;

%% Options
% Assign default values to unspecified parameters
if ~exist('option','var'),
    option = [];
end
option = mgoptions(option,Ndof);    % parameters
x0 = option.x0;
N0 = option.N0/5;
tol = option.tol;
maxIt = option.solvermaxit;
mu = option.smoothingstep;
solver = option.solver;
% preconditioner = option.preconditioner;
preconditioner = 'none';
coarsegridsolver = option.coarsegridsolver;
printlevel = option.printlevel;
option.smoother = 'LINE';

%% Fixed dof
isFixDof = [];
if isfield(option,'freeDof') % freeDof is given
   isFreeDof = false(N,1);
   isFreeDof(option.freeDof) = true;
   NA = size(A,1);
   if NA > length(option.freeDof)
       isFixDof = true(NA,1);
       isFixDof(isFreeDof) = false;
   end
else % Find free dof and eliminate isolated dof
    deg = sum(spones(A));  % degree
    isFreeDof = false(Ndof,1);
    isFreeDof(deg>1) = true;
    isFixDof = ~isFreeDof;
    isFixDof(deg == 0) = false;
end
if any(isFixDof) % a bigger matrix is given
    xD = zeros(Ndof,1);
    xD(isFixDof) = b(isFixDof)./diag(A(isFixDof,isFixDof));
    A = A(isFreeDof,isFreeDof);
end
if length(b) > sum(isFreeDof)
    b = b(isFreeDof);
end
if length(x0) > sum(isFreeDof)
    x0 = x0(isFreeDof);
    option.x0 = x0;
end
isFreeNode = isFreeDof(1:N); % default choice for P1

%% Hierarchical Structure of Mesh
%  hierarchical structure for linear element.
if dim == 2  % 2-D
    [HB, NL, level] = HBstructure(elem,N0);
end
if dim == 3  % 3-D
    if nargin > 4
        HBmesh = varargin{end}; % need HB for bisection refinement
        [HB, NL, level] = HBstructure3(elem,N0,HBmesh);
    else % no HBmesh is given. only work for the red uniform refinement
        [HB, NL, level] = HBstructure3(elem,N0);
    end
end

%% Transfer operators between multilevel meshes for P1 element
% standard prolongation and restriction operator for P1 element
[Pro,Res] = transferoperator(HB,NL,isFreeNode);
for j = 1:level-1
    Pro{j} = kron(speye(Nydof-1,Nydof-1),Pro{j}); % for each level in y direction
    Res{j+1} = Pro{j}';
end
clear HB

%% Matrices in each level
Ai = cell(level,1);
Ai{level} = A;
for j = level:-1:2
    Ai{j-1} = Res{j}*Ai{j}*Pro{j-1};           % Ac = Res*Af*Pro
    switch option.smoother
        case 'GS'
            Bi{j} = tril(Ai{j});        % Forward Gauss-Seidel   B = D+L
            BBi{j} = triu(Ai{j});       % Backward Gauss-Seidel BB = D+U
        case 'LINE'
            NxNy = size(Ai{j},1);
            Nx = NxNy/(Nydof-1);
            di = zeros(NxNy,4);
            di(:,1) = full(diag(Ai{j}))/2; % half of the main diagoal
            temp = full(diag(Ai{j},Nx)); % next vertex in the extend direction
            di(1:length(temp),2) = temp;
            temp = full(diag(Ai{j},Nx*(Ny-2))); %
            di(1:length(temp),3) = temp;
            temp = full(diag(Ai{j},Nx*(Ny-1))); % middle point in the extend direction
            di(1:length(temp),4) = temp;
            halfBi = spdiags(di,[0 -Nx -Nx*(Ny-2) -Nx*(Ny-1)],NxNy,NxNy);
            Bi{j} = halfBi + halfBi';
            L{j} = chol(Bi{j},'lower');
            R{j} = L{j}';
%             Bi{j} = spdiags(diag(Ai{j}),0,size(Ai{j},1),size(Ai{j},1));        %
%             BBi{j} = Bi{j};       % Jacobi iteration
    end
end

%% MG cycles
% initial set up
k = 1;
x = x0;
r = b - A*x;
nb = norm(b);
err = zeros(maxIt,2);
if nb > eps  % nb is non-zero
    err(1,:) = norm(r)/nb;
else
    err(1,:) = norm(r);
end
switch solver
    case 'VCYCLE'
        if printlevel >= 1
            fprintf('Multigrid Vcycle Iteration \n')
        end
        while (max(err(k,:)) > tol) && (k <= maxIt)
            k = k + 1;
            % Step 2: Compute Br by one Vcylce MG
            Br = vcycle(r);
            % Step 3: Correct the solution
            x = x + Br;
            % Step 1: Form residual r
            r = r - A*Br;
            err(k,1) = sqrt(abs(Br'*r/(x'*b))); % approximate relative error in energy norm
            err(k,2) = norm(r)/nb; % relative error of the residual in L2-norm
            if printlevel >= 2
                fprintf('#dof: %8.0u,  #nnz: %8.0u, MG Vcycle iter: %2.0u, err = %8.4e\n',...
                         Ndof, nnz(A), k-1, max(err(k,:)));
            end
        end
        err = err(1:k,:);
        itStep = k-1;
    case 'WCYCLE'
        if printlevel >= 1
            fprintf('Multigrid Wcycle Iteration \n')
        end
        while (max(err(k,:)) > tol) && (k <= maxIt)
            k = k + 1;
            % Step 2: Compute Br by one Vcylce MG
            Br = wcycle(r);
            % Step 3: Correct the solution
            x = x + Br;
            err(k,1) = sqrt(abs(Br'*r/(x'*b))); % approximate relative error in energy norm
            % Step 1: Form residual r
            r = r - A*Br;
            err(k,2) = norm(r)/nb; % relative error of the residual in L2-norm
            if printlevel >= 2
                fprintf('#dof: %8.0u,  #nnz: %8.0u, MG Wcycle iter: %2.0u, err = %8.4e\n',...
                         Ndof, nnz(A), k-1, max(err(k,:)));
            end
        end
        err = err(1:k,:);
        itStep = k-1;
end

%% Krylov space method
% set up preconditioner
switch preconditioner
    case 'V'
        prefunc = @vcycle;
        if printlevel >= 1
            fprintf('Multigrid V-cycle Preconditioner with ')
        end
    case 'W'
        prefunc = @wcycle;
        if printlevel >= 1
            fprintf('Multigrid W-cycle Preconditioner with ')
        end
end
switch solver
    case 'CG'
        if printlevel >= 1
            fprintf('Conjugate Gradient Method\n')
        end
        [x,flag,err,itStep] = pcg(A,b,tol,maxIt,prefunc,[],x0);
    case 'MINRES'
        if printlevel >= 1
            fprintf('Minimum Residual Method \n')
        end
        [x,flag,err,itStep] = minres(A,b,tol,maxIt,prefunc,[],x0);
    case 'GMRES'
        if printlevel >= 1
            fprintf('General Minimum Residual Method\n')
        end
        if isfield(option,'restart')
            restart = option.restart;
        else
            restart = min(N,10);
        end
        [x,flag,err,itStep] = gmres(A,b,restart,tol,maxIt,prefunc,[],x0);
        itStep = (itStep(1)-1)*restart + itStep(2);
end

%% Modify x to include fix dof
if any(isFixDof)
    xD(isFreeDof) = x;
    x = xD;
end

%% Output
if k > maxIt
    flag = 1;
else
    flag = 0;
end
time = toc;
if printlevel >= 2
    fprintf('#dof: %8.0u, level: %2.0u,   coarse grid %2.0u, #nnz: %8.0u\n',...
              Ndof, level, size(Ai{1},1), nnz(Ai{1}))
end
if printlevel >= 1
    fprintf('#dof: %8.0u,  #nnz: %8.0u, smoothing: %2.0u, iter: %2.0u,   err = %8.4e,   time = %4.2g s\n',...
                 Ndof, nnz(A), mu, itStep, max(err(end,:)), time)
end
if (flag == 1) && (printlevel>0)
   fprintf('NOTE: the iterative method does not converge! \n');
end
info = struct('solverTime',time,'itStep',itStep,'error',err,'flag',flag,'stopErr',max(err(end,:)));

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% subfunctions vcycle, wcycle, fcycle, bpx
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Vcycle MG
    function Br = vcycle(r,J)        % solve equations Ae = r in each level
    if nargin<=1
        J = level;
    end
    ri = cell(J,1);            % record residual in each level
    ei = cell(J,1);            % record err in each level
    ri{J} = r;
    for i = J:-1:2
        ei{i} = linesmoother(ri{i},i);   % pre-smoothing
        for s = 1:mu-1           % extra mu-1 steps smoothing
            ei{i} = ei{i} + linesmoother(ri{i}-Ai{i}*ei{i},i);
        end
        ri{i-1} = Res{i}*(ri{i} - Ai{i}*ei{i});
    end
    if strcmp(coarsegridsolver,'direct')
        ei{1} = Ai{1}\ri{1}; % direct solver in the coarest level
    else                         % iterative solver in the coarest level
        D = spdiags(diag(Ai{1}),0,size(Ai{1},1),size(Ai{1},1));
        [ei{1},flag] = pcg(Ai{1},ri{1},1/size(Ai{1},1),1000,D);
    end
    for i = 2:J
        ei{i} = ei{i} + Pro{i-1}*ei{i-1};
        ei{i} = ei{i} + linesmoother(ri{i}-Ai{i}*ei{i},i);
        for s = 1:mu-1
            ei{i} = ei{i} + linesmoother(ri{i}-Ai{i}*ei{i},i); % post-smoothing
        end
    end
    Br = ei{J};
    end

%% Wcycle MG
    function e = wcycle(r,J)        % solve equations Ae = r in each level
    if nargin<=1
        J = level;
    end
    if J == 1
        e = Ai{J}\r;   % exact solver in the coaresest grid
        return
    end
    % fine grid pre-smoothing
    e = Bi{J}\r;        % pre-smoothing
    for s = 1:mu-1        % extra mu-1 steps smoothing
        e = e + Bi{J}\(r-Ai{J}*e);
    end
    % coarse grid correction twice
    rc = Res{J}*(r - Ai{J}*e);
    ec = wcycle(rc,J-1);
    ec = ec + wcycle(rc - Ai{J-1}*ec,J-1);
    e = e + Pro{J-1}*ec;
    % fine grid post-smoothing
    e = e + BBi{J}\(r-Ai{J}*e);
    for s = 1:mu-1
        e = e + BBi{J}\(r-Ai{J}*e); % post-smoothing
    end
    end

%% Line smoother
    function e = linesmoother(r,J)
%         NxNy = length(r);
%         Nx = NxNy/(Ny-1);
%         e = zeros(NxNy,1);
%         for vi = 1:Nx
%             linedof = vi + 0:Nx:NxNy-1;
%             e(linedof) = Ai{J}(linedof,linedof)\r(linedof);
%         end
%         e = Bi{J}\r;
        e = R{J}\(L{J}\r);
        e = 0.75*e;
    end
end