function [Params, Transf, psi, V, VDk] = run(this)
    
    format long;

    % --- Obtain simulation parameters --- 
    [Params]                  = this.setupParameters();
    this.SimulationParameters = Params;
    
    % --- Set up spatial grids and transforms --- 
    [Transf]                  = this.setupSpace(Params);

    % --- Set up for Variational method ---
    VParams.SelfConIter       = Params.SelfConIter;      % Max number of iterations to perform self-consistent calculation
    VParams.ell               = Params.ell;              % initial [ell], ell is the "width" - psi ~ e^(z^2/ell^2)
                                  
    % Window of optimization 
    VParams.ell_lower         = Params.ell_lower;
    VParams.ell_upper         = Params.ell_upper;
    
    % Relative cutoffs
    VParams.ellcutoff         = Params.ellcutoff;
    VParams.evarcutoff        = Params.evarcutoff;
    
    % --- Initialize --- 
    mkdir(sprintf(this.SaveDirectory))
    mkdir(sprintf(strcat(this.SaveDirectory, '/Run_%03i'),Params.njob))
    fminconoptions = optimoptions('fmincon','Display','off','StepTolerance',1e-10,'Algorithm','sqp','MaxIterations',2000,'MaxFunctionEvaluations',4000);

    [psi,V,VDk]               = this.initialize(Params,VParams,Transf);
    ells(1)                   = VParams.ell; 
    E_Var                     = @(x) this.Calculator.calculateVariationalEnergy(psi, Params, x, Transf, V, this.IncludeDDICutOff)/Params.N;
    E_vs_iter(1)              = E_Var(ells(1));
    
    t_idx = 1; firstpoint = 1;

    for nn = 1:Params.SelfConIter
        Observ.EVec = []; Observ.NormVec = []; Observ.PCVec = []; Observ.tVecPlot = []; Observ.mucVec = []; Observ.residual = []; Observ.res_idx = 1;
        
        if firstpoint == 1
            [psi,V,VDk]       = this.initialize(Params,VParams,Transf); % Recalculate Psi,VDk with new value for the variational parameter
            firstpoint = 0;
        else
            [~,V,VDk]         = this.initialize(Params,VParams,Transf); % Recalculate Psi,VDk with new value for the variational parameter
        end
                                                                        
        % --- Adding some noise --- 
        % Noise added in every iteration to ensure it is not stuck in some local minimum
        Norm                  = sum(abs(psi(:)).^2)*Transf.dx*Transf.dy; % normalisation
        psi                   = psi/sqrt(Norm);
        r                     = normrnd(0,1,size(psi));
        theta                 = rand(size(psi));
        noise                 = r.*exp(2*pi*1i*theta);
        psi                   = psi + Params.nsf*noise; 
        
        % Renormalize wavefunction
        Norm = sum(abs(psi(:)).^2)*Transf.dx*Transf.dy; % normalisation
        psi = sqrt(Params.N)*psi/sqrt(Norm);
    
        % --- Run --- 
        [psi, Observ]         = this.propagateWavefunction(psi, Params, VParams, Transf, VDk, V, t_idx, Observ, nn);
        psi                   = gather(psi);
        
        % --- Constrained minimization --- 
        E_Var                 = @(x) this.Calculator.calculateVariationalEnergy(psi, Params, x, Transf, V, this.IncludeDDICutOff)/Params.N;
        VParams.ell           = fmincon(E_Var,VParams.ell,[],[],[],[],Params.ell_lower,Params.ell_upper,[],fminconoptions);
        
        % --- Convergence check --- 
        ells                  = [ells VParams.ell]; 
        relelldiff            = abs(ells(nn+1)-ells(nn))/ells(nn); 
        E_vs_iter             = [E_vs_iter E_Var(VParams.ell)];
        relevardiff           = abs(E_vs_iter(nn+1)-E_vs_iter(nn))/E_vs_iter(nn);

        save(sprintf(strcat(this.SaveDirectory, '/Run_%03i/psi_gs_%i.mat'),Params.njob,nn),'psi','Observ','Transf','Params','VDk','V','VParams');
        
        %Plotting
        if this.PlotLive
            figure(1);
            subplots = findobj(gcf, 'Type', 'axes'); % Find all axes (subplots) in figure 1
            subplots = flipud(subplots); % Reverse the order to match the subplot layout
            last_subplot_handle = subplots(end); % Grab the handle of the last subplot
            axes(last_subplot_handle); % Set the last subplot as the current axes
            plot(ells,E_vs_iter,'LineStyle','none','Marker','o','MarkerSize',3,'Color','b','MarkerFaceColor','b')
            xlabel('$\ell$', 'FontSize', 14)
            ylabel('$E_{var}$', 'FontSize', 14)
            title('Variational Energy', 'FontSize', 14);
            grid on
            drawnow
        end
        
        if relelldiff < Params.ellcutoff && relevardiff < Params.evarcutoff
            break
        end
    
        VParams.ells          = ells;
        VParams.E_vs_iter     = E_vs_iter;
    end
    
    fprintf('\n')
    disp('Saving data...');
    save(sprintf(strcat(this.SaveDirectory, '/Run_%03i/psi_gs.mat'),Params.njob),'psi','Observ','Transf','Params','VDk','V','VParams');
    disp('Save complete!');
    delete(sprintf(strcat(this.SaveDirectory, '/Run_%03i/psi_gs_*.mat'),Params.njob))

end
