# In this test, the LC long axis is rotated to be within the XY plane (theta=90 for in-plane anisotropy)
# The LC is rotated within the XY plane, the different rotations should induce different phase difference between S and P
# if the LC is at 45° between X and Y, the difference of phase between S and P should be 0
# The phase difference should be opposite (x -1) when the long axis is along X or Y

# The purpose of this script is to show how to set up the different attribute to perform the same rotation
# It also shows we get the same results with the different solvers.

clear;
#############################################################
### Choose test case ####
#############################################################
attribute_case=2; #1=LC attribute / 2=Matrix attribute / 3=Rotation attribute
solver_case=3; #1=RCWA  / 2=FDTD / 3=STACK 
# Note: STACK is run by setting the RI matrix directly so there is no effect of attribute type for this solver

plot_result=true;                            
phi_sweep=linspace(0,90,3); #Rotation around Z for 0°, 45°, and 90°

switchtolayout;
setmaterial("Anisotropic_LC","Refractive Index",[1.5,1.5,1.6]); # The long axis is defined initially along the Z axis
theta=90; #theta=90 brings the long axis within the XY plane    
   
setnamed("LC","material","Anisotropic_LC"); 

if (attribute_case==1){attribute_type="LC attribute";}
if (attribute_case==2){attribute_type="Matrix attribute";}
if (attribute_case==3){attribute_type="Rotation attribute";}
if (solver_case==1){solver_type="RCWA";}
if (solver_case==2){solver_type="FDTD";}
if (solver_case==3){solver_type="STACK";attribute_type="N/A";} 
#############################################################
### support functions ####
#############################################################
function apply_attribute(attribute_case,theta,phi){
    #LC attribute    
    if (attribute_case==1){
        setnamed("LC attribute","theta",theta*pi/180); # Bring the long axis within the XY plane
        setnamed("LC attribute","phi",phi*pi/180); # Rotate around Z
        setnamed("LC","grid attribute name","LC attribute");
        }
        
    #Matrix attribute
    if (attribute_case==2){
        # Define the rotation matrix
        Uy=[cos(theta*pi/180),0,sin(theta*pi/180);
            0,1,0;
           -sin(theta*pi/180),0,cos(theta*pi/180)];
        Uz=[cos(phi*pi/180),-sin(phi*pi/180),0;
            sin(phi*pi/180),cos(phi*pi/180),0;
            0,0,1];
       U = mult(Uz,Uy);
       U = ctranspose(U); # Note the ctranspose operation here specific to the convention of this attribute
       pos=find(abs(U)<1e-5);U(pos)=0;
       ?'Rotation Matrix:';
       ?num2str(U);
       
       # Apply the rotation matrix on the attribute
       setnamed("Matrix attribute","U",U);
       setnamed("LC","grid attribute name","Matrix attribute");
       }  
    
    # Rotation Atribute   
    if (attribute_case==3){
        # Apply the rotation matrix on the attribute
        setnamed("Rotation attribute","theta",theta*pi/180);
        setnamed("Rotation attribute","phi",phi*pi/180);        
        setnamed("LC","grid attribute name","Rotation attribute");
        }
}

function run_solver(solver_case,theta,phi){
    # RCWA solver  // Note that the index monitor is part of the results output by the solver  
    if (solver_case==1){
        run("RCWA");
        # Check the results
        results=getresult("RCWA","grating_characterization");
        n0=find(results.n==0);
        m0=find(results.m==0);
        Phase_P=angle(pinch(results.Tpp(1,1,n0,m0,1))); 
        Phase_S=angle(pinch(results.Tss(1,1,n0,m0,1)));      
        Phase_diff=Phase_P-Phase_S;
        ?Phase_diff;
        
        index_result=getresult('RCWA','index');
        ?'index xx:'+num2str(index_result.index_xx(1,1,2,1));
        ?'index yy:'+num2str(index_result.index_yy(1,1,2,1)); 
        ?'index z:'+num2str(index_result.index_z(1,1,2,1)); 
 
        }
        
    # FDTD solver // Note that you would need to add an index monitor to extract the index info     
    if (solver_case==2){
        #Run FDTD and check the field
        run("FDTD");
        # Check the results
        E=getresult("monitor_FDTD","E");E=E.E;
        Phase_diff=angle(E(1,1,1,1,1))-angle(E(1,1,1,1,2)); 
        } 
        
     # STACK (independent from attribute, permittivity matrix is directly defined)   
     if (solver_case==3){
        N_layers = 3;
        Nfreqs = 1;
        d = [0; 2.5e-6; 0]; # air/ birefringent slab / air
        f = c/(0.55e-6);
               
        n = matrix(N_layers, Nfreqs, 9);
        n(1, :, 1) = 1; # Incidence Medium, must be isotropic and loss-less
        n(1, :, 5) = 1; # Incidence Medium, must be isotropic and loss-less
        n(1, :, 9) = 1; # Incidence Medium, must be isotropic and loss-less
        
       # Define the rotation to apply on the index matrix 
       Uy=[cos(theta*pi/180),0,sin(theta*pi/180);
           0,1,0;
           -sin(theta*pi/180),0,cos(theta*pi/180)];
        Uz=[cos(phi*pi/180),-sin(phi*pi/180),0;
            sin(phi*pi/180),cos(phi*pi/180),0;
            0,0,1];
       U = mult(Uz,Uy);
       U=ctranspose(U);
       pos=find(abs(U)<1e-5);U(pos)=0;  
       nxx=getindex('Anisotropic_LC',f,1);
       nyy=getindex('Anisotropic_LC',f,2);
       nzz=getindex('Anisotropic_LC',f,3);
       n0=[nxx,0,0;0,nyy,0;0,0,nzz];      
       n(2,:,:)=mult(mult(ctranspose(U),n0),U); #Same operation than what the Matrix attribute is doing
        
        n(3, :, 1) = 1; # Outgoing material can be lossy
        n(3, :, 5) = 1; # Outgoing material can be lossy
        n(3, :, 9) = 1; # Outgoing material can be lossy
        
        ?num2str(U);
        ?'index xx:'+num2str(n(2,1,1));
        ?'index yy:'+num2str(n(2,1,5)); 
        ?'index z:'+num2str(n(2,1,9)); 
        RT= stackrt(n,d,f,0);
        Phase_diff=angle(RT.tpp)-angle(RT.tss);
     }
    return Phase_diff;
    
    }
      
    
#############################################################
### Run simulation in selected case ####
#############################################################
## Initialize the matrix data
Phase_diff=matrix(length(phi_sweep));
Phase_P=matrix(length(phi_sweep));
Phase_S=matrix(length(phi_sweep));

## Start the sweep over Phi
?'---------------------';
?'Solver: ' + solver_type;
?'Attribute: ' + attribute_type;
for (p=1:length(phi_sweep)){
    switchtolayout;
    phi=phi_sweep(p); 
    ?' '; 
    ?'Phi = ' + num2str(phi) + '°';  
    apply_attribute(attribute_case,theta, phi);
    Phase_diff(p)=run_solver(solver_case,theta,phi);
}

if (plot_result==1){
title_plot="Solver " + solver_type +" / Attribute " + attribute_type;
plot(phi_sweep,Phase_diff,"Phi [deg]","Phase Difference S/P", title_plot);
}