%{ 
=========================================================
Solving stationary Schrodinger equation
Method: Shooting method with RK4
Alex G. November 2021
=========================================================
%}

clear  % Clears command window
clc    % Clears command history
clf    % Removes anything in the figure window before simulation. 
tic    % set timer for the program

% Parameters for calculations
Xmin = -8.0;
Xmax =  8.0;
X0   = -1.0;
N    = 1601;
eps  = 1.0e-8;

Emin = -4.0;
Emax = -3.5;

state = 1.0;  %  1.0 for  n = 0,2,4... (even-parity states)
              % -1.0 for  n = 1,3,5,...(odd-parity states)

[Ei] = Qwell02(Xmin,Xmax,X0,N,Emin,Emax,eps,state);
fprintf('\n Ei = %10.6f \n',Ei)

% note for |a|<1, V0=4: Ei = -3.778, -3.120, -2.058, -0.695

timeE = ceil(toc);
fprintf('\n Elapsed time = %4i sec \n',timeE)


function [Ei] = Qwell02(Xmin,Xmax,X0,N,Emin,Emax,eps,state)
%{
Solving stationary Schrodinger equation
y'' + 2(E-V(x))y = 0
METHOD: shooting method + Runge-Kutta 4th as initial value problem
CALLS: rk4_2d(x,y,dy,n), ypp(x,y,dy), V(x) (potential)
INPUT: 
  V(X) - a potential (as a function, so far the name is fixed)
  XMIN, XMAX - left and right endpoints for integration
  X0 - "meeting point" (results should not depend on it)
  N  - number of points for the whole interval
  Emin, Emax - energy interval for searching an energy level
  eps - tolerance on matching the log derivative at X0
  state - parity (+1 for odd states and -1 for even states
OUTPUT:
  Ei - eigenvalue (energy)
Additional output (in the function)
Plot 1: matching (left/right) wavefunctions and derivatives
Plot 2: Wavefunction (normalized)
%}
global Ee
MaxIter = 128;            % Max number of iterations

% Working arrays
Delta = zeros(MaxIter,1);
E     = zeros(MaxIter,1);
Vp    = zeros(N,1);

% Phase 1: Prepare arrays x,y xl,yl, xr,yr(left and righ)
h = (Xmax - Xmin)/(N-1);
N0 = ceil((X0-Xmin)/h) + 1;
Nl = N0;
Nr = N - N0 +1;

% reserve arrays
x  = zeros(N,1);
y  = zeros(N,1);

xl  = zeros(Nl,1);
yl  = zeros(Nl,1);
dyl = zeros(Nl,1);

xr  = zeros(Nr,1);
yr  = zeros(Nr,1);
dyr = zeros(Nr,1);

% grid points for x, xl and xr
for k = 1:N
    x(k) = Xmin + h*(k-1);
end
for k=1:N0
    xl(k) = x(k);
end
for k=N:-1:N0
    kr = N-k+1;
    xr(kr) = x(k);
end

% Phase 2: Prepare first three energies (initialization)
E(1) = Emin;
E(2) = Emax;
E(3) = (Emin+Emax)/2;


% Phase 3: the main loop (iterations)

for k = 1:MaxIter
    Ee = E(k);
    b = sqrt(2.0*abs(Ee));
    
%3a Integration from the left to X0
    % initial position and the derivative on the left
    yl(1)  = exp(-b*abs(xl(1)));        
    dyl(1) = b*yl(1);
    [yl,dyl] = rk4_2d(xl,yl,dyl,Nl);

%3b Integration from the right to X0
    % initial position and the derivative on the right
    yr(1) = state*exp(-b*abs(xr(1)));
    dyr(1) = (-1.0)*b*yr(1);
    [yr,dyr] = rk4_2d(xr,yr,dyr,Nr);

%3C Calculating the log derivative difference between the two solutions
    Lelt  = dyl(N0)/yl(N0);
    Right = dyr(Nr)/yr(Nr);
    Delta(k) = (Lelt-Right)/(Lelt + Right);

% print iterations if needed
    if k == 1 % print the header
        fprintf(' Iter   Energy      Match \n')
    end
    fprintf(' %3i   %8.6f  %10.2e \n',k,Ee,Delta(k))
    
    
%3d when k=2 check if there is a solution 
    if k == 2
        if Delta(1)*Delta(2) > 0
            fprintf(' No solution for given Emin and Emax \n')
            Ei = 0.0;
            return
        end
    end
    
    
%3E bisectional search for the root
% Attention: the bisectional method may converge not only to a root 
% of a function f(x)=0 but to a discontinuity, then change the matching
% point X0.
    if k >= 3
        if Delta(1)*Delta(k) < 0.0
            E(2) = E(k);
        else
            E(1) = E(k);
            Delta(1) = Delta(k);
        end
        E(k+1) = (E(1)+E(2))/2.0;
        if abs(Delta(k)) < eps 
%        if abs(E(1)-E(2)) < eps    
            Ei = E(k);
            break
        end
    end   % end bisectional search
               
end % end iterations

if k == MaxIter
    fprintf(' Max iterations - still no solution \n')
    Ei = 0.0;
    return
end
    
figure (1)
plot(xl,yl,'b',xr,yr,'r',xl,dyl,'-m',xr,dyr,'-k','LineWidth',1.5)
title('Matching functions and derivatives')
grid

% Phase 4: Assembling the whole wave function + normalization 

% 4a  One function y(x) as a sum of two solutions (left + right)
Ynorm = yr(Nr)/yl(N0);
for k = 1:N0
    x(k) = xl(k);
    y(k) = yl(k)*Ynorm;
end
for k = 1:Nr
    x(N-k+1) = xr(k);
    y(N-k+1) = yr(k);
end

% 4b  Calculating Integral |y(x)|^2dx for normalization
Sn = 0.0;
for k = 2:2:N-1
    Sn = Sn + 4.0*y(k)*y(k);
    Sn = Sn + 2.0*y(k+1)*y(k+1);
end
Sn = Sn + y(1)*y(1) - y(N)*y(N);
Sn = sqrt(Sn*(h/3.0));
% 4c Normalization
for k = 1:N
    y(k) = y(k)/Sn;
    Vp(k) = V(x(k));
end

figure (2)
plot(x,y,'r','LineWidth',1.5)
%plot(x,y,'r',x,Vp,'b')
str = sprintf('Wave function for Ei = %6.4f',Ei);
title(str);
grid

end %end function


function [y,dy] = rk4_2d(x,y,dy,n)
%{
================================================================
 Solution of a single second-order ODE (Initial-value problem)
 method:  Runge-Kutta 4th-order
 written by: Alex G. (last revision March 2021)
----------------------------------------------------------------
 input ...
  f(x,y,dy)- function from d2y/dx2=f(x,y,dy) (supplied by a user)
  x(i)  - array of x base point (n points)
  y(1)  - initial value for y(1)
 dy(1) - initial value for y'(1)

 output ...
  y(i)  - solutions for y  in n points
 dy(i)  - solutions for y' in n points 
=================================================
%}

for k=2:n
   h   = x(k)-x(k-1);
   k11 = h*dy(k-1);
   k12 = h*ypp(x(k-1),y(k-1),dy(k-1));
   k21 = h*(dy(k-1)+k12/2.0);
   k22 = h*ypp(x(k-1)+h/2.0, y(k-1)+k11/2.0, dy(k-1)+k12/2.0);
   k31 = h*(dy(k-1)+k22/2.0);
   k32 = h*ypp(x(k-1)+h/2.0, y(k-1)+k21/2.0, dy(k-1)+k22/2.0);
   k41 = h*(dy(k-1)+k32);
   k42 = h*ypp(x(k-1)+h,y(k-1)+k31,dy(k-1)+k32);

   y(k) = y(k-1) + (k11 + 2.0*(k21+k31) + k41)/6.0;
  dy(k) = dy(k-1)+ (k12 + 2.0*(k22+k32) + k42)/6.0; 
end
end % function rk4_2d

function [yp2] = ypp(x,y,dy)
% ------------------------------------------
%  Second order ODE as
%  y'' + P(x,y)y' + Q(x,y)y = F(x,y)
%  yp2 = d2y/dx2 = - P(x,y)y' - Q(x,y)y + F(x,y)
% ------------------------------------------
global Ee
P = 0.0;
Q = 2.0*(Ee - V(x));
F = 0.0;
yp2 = (-1.0)*P*dy +(-1.0)*Q*y + F;
end

function Vx = V(x)
a = 2.0;
if abs(x) <= a
    Vx = -4.0;
else
    Vx = 0.0;
end

end


