RiceMunk / omnifit

This is a package for doing ice spectroscopy fitting of interstellar ices.
https://ricemunk.github.io/omnifit/
6 stars 5 forks source link

Kramers-Kronig relation support #3

Closed RiceMunk closed 8 years ago

RiceMunk commented 8 years ago

Currently the CDEspectrum class makes use of complex refractive index values which are commonly calculated using the Kramers-Kronig (KK) relation equation. It is not a terribly complex calculation, yet it's not included as part of Omnifit.

An obvious improvement to Omnifit is to make it be included as part of the library, so CDEspectrum can be initialised without outside help.

RiceMunk commented 8 years ago

Here's an ancient bit of C code that I'm planning on adapting for the KK relation:

/* From tielens  Thu Dec  5 17:52:06 1996
   Received: by dusty.arc.nasa.gov (8.6.8/1.35)
    id RAA10340; Thu, 5 Dec 1996 17:52:06 -0800
  From: tielens (A.G.G.M. Tielens)
  Message-Id: <199612060152.RAA10340@dusty.arc.nasa.gov>
  Subject: optcon.f revised
  To: boogert (Adwin Boogert)
  Date: Thu, 5 Dec 1996 17:52:06 -0800 (PST)
  X-Mailer: ELM [version 2.4 PL22]
  Content-Type: text
  Content-Length: 3745      
  Status: RO

  OPTCON.C: This program calculates optical constants
  from transmission measurements using the Kramers-Kronig relationship.

  v0.0: 27-12-1996 converted from Fortran to C (BOOGERT)

  NOTE: The number of frequency points is defined by ARRDIM.
  Adjust it according to length of input file.
  Finally, note the sign of the KK integral ! */

#include<stdio.h>         
#include<math.h>          
#include"dcomplex.h"
#define ARRDIM 20000   /* dimension of arrays, must be < NPOINTS, i.e.
            number of spectral datapoints */
#define PI 4.*atan(1.)

main()
{
    dcomplex xc,yc,n1[ARRDIM],eix2,eix,r01,r12,t01,t12,t02,j ;
        dcomplex m0, m1[ARRDIM], m2, Trans(), Refl();
    double nu0,x,xx,alpha[ARRDIM],nu[ARRDIM],n01,d,delta ;
    double t[ARRDIM],alpnew,tcal[ARRDIM], dif, zc, kkint(),dif1,dif2;
        FILE *fpi, *fpo;
    char filein[30];
        char fileout[40]; 
        int k, dk, nn,NPOINTS;

    /* INPUT SECTION */   
    printf("Geef optische brekingsindex n01\n");
    scanf("%lf", &n01 ); /* N01~1.22-1.35 op grote frequentie */ 
    printf("Geef dikte ijs in micron\n");
    scanf("%lf", &d ); /* lab ijsdikte D in cm, e.g. D=3.0D-5 */
    printf("Geef input file naam\n");
    scanf("%s", filein ); 
    printf("Geef output file naam\n");
    scanf("%s", fileout ); 
    printf("Number of datapoints\n");
    scanf("%i", &NPOINTS ); 

    /*NUMBER OF DATAPOINTS MUST BE <= ARRAY DIMENSION*/
    if (NPOINTS > ARRDIM) {
     printf("\a\a\a **Array dimensions too small!!! \a\a\a\n");
     printf("\a\a\a **Program aborted, adjust ARRDIM \a\a\a\n");
    } else {
     j=DComplex(0.,1.); /* the complex i */
     m0=DComplex(1., 0.); /* complex refractive index of vacuum */
     m2=DComplex(1.74, 0.); /* complex refractive index of CsI window*/
         d=d*1.e-4; /*thickness ice sample in cm*/  
     /*printf("\t%f\t%f\n\n", n01,d);*/
     delta=1.e-5 ; /*stop criterion final iteration, |1-Tcal/Tobs|*/
     /*printf("\t%lf\n\n", delta);*/

     /* READ DATA FROM FILE*/
     fpi=fopen(filein,"r");
     for (k=0; k < NPOINTS; k++) {
          fscanf(fpi,"%le %le", &nu[k], &t[k]);
      /*printf("%i got here\n\n\n", k);*/
      /* printf("%e\t%e\n", nu[k],t[k]);*/
      t[k]=pow(10,-t[k]);
      m1[k]=DComplex(1.3,0.);
      alpha[k]=0.;
         }

    nn=0;
    dif=1.;
    /* BEGINNING LOOP TO CONVERGE TO OBSERVED SPECTRUM */         
    while (dif > delta) {
      nn++;
      /* GIVEN N1(K), CALCULATE ALPHA (AND K1(K))
      FROM MEASURED SPECTRA */
      for (k=0; k<NPOINTS ; k++) { 
        xc=DCmul(DRCmul(4.*PI*d*nu[k], m1[k]), j);
        eix=DCexp(xc); 
        r01=Refl(m0,m1[k]);
        r12=Refl(m1[k],m2);
        t01=Trans(m0,m1[k]);
        t12=Trans(m1[k], m2);
        t02=Trans(m0, m2);
        yc=DCdiv(DCdiv(DCmul(t01,t12),t02),(DCadd(DComplex(1.,0.),DCmul(DCmul(r01,r12),eix))));
        zc=DCmul(yc,DConjg(yc)).r; /* this is a double */   
        alpnew=(-log(t[k])+log(zc))/d;
        alpha[k]=alpnew;
      }
      /* GIVEN ALPHA (OR K1(K)), M1(K) IS CALCULATED
      USING THE KRAMERS KRONIG RELATIONSHIP.
      CALCULATE SPECTRUM AND COMPARE WITH MEASURED ONE. */
      dif=0.;
      for (k=0; k<NPOINTS; k++) { 
        m1[k]=DComplex(kkint(k,nu, alpha, n01,NPOINTS),alpha[k]/(4.*PI*nu[k]));
        if (dif <= delta) {
          xc=DCmul(DRCmul(2.*PI*d*nu[k], m1[k]), j);
          eix=DCexp(xc);
          eix2=DCexp(DRCmul(2.,xc));
          r01=Refl(m0,m1[k]);
          r12=Refl(m1[k],m2);
          t01=Trans(m0,m1[k]);
          t12=Trans(m1[k], m2);
          t02=Trans(m0, m2);
          yc=DCdiv(DCdiv(DCmul(DCmul(t01,t12),eix),t02),(DCadd(DComplex(1.,0.),DCmul(DCmul(r01,r12),eix2))));
          x=DCmul(yc,DConjg(yc)).r; /* this is a double */
          tcal[k]=x;
          if (x==0. || t[k]==0.) 
            dif2=fabs(x-t[k]);
          else
            dif2=fabs(1.-t[k]/x);
          if (dif2 > dif) {
            dif=dif2; /* the maximum deviating point ( dangerous!!)*/ 
            dk=k;
          } 
          /*      printf("%le\t%le\n",dif2,dif);          */
          /*         printf("%i\t",k);*/
        }
     }
    printf("\a iteration %i difference %le at %le cm-1\n",nn,dif,nu[dk]);
        } /* end of convergence while loop*/
     printf("got here after %i iterations\n", nn);
     fpo=fopen(fileout,"w");
     for (k=0; k<NPOINTS; k++) { 
          fprintf(fpo, "%le\t%le\t%le\t%le\t%le\n", nu[k],m1[k].r,m1[k].i,t[k],tcal[k]);
         }
    }
}

/* FUNCTION "Refl": Complex reflection coefficient medium p-->q */  

dcomplex Refl(Np, Nq)
dcomplex Np, Nq;
{
   dcomplex r;
   r=DCdiv(DCsub(Np,Nq),DCadd(Np,Nq));
   return r;
}

/* FUNCTION "Trans": Complex transmission coefficient medium p-->q*/    
dcomplex Trans(dcomplex Np, dcomplex Nq)
{
   return DCdiv(DRCmul(2.,Np),DCadd(Np,Nq));
}

/* FUNCTION "KKINT": Kramers-Kroenig integration */ 
double kkint(k,nu, alpha, n01, NPOINTS)
double nu[ARRDIM], alpha[ARRDIM], n01 ;
int k, NPOINTS;
{
  int i; 
  double dn, n, wnu[ARRDIM];

/*  Note that sign in Kramers-Kronig equation depends
    on sign of WNU (ie., deltanu=+ sign is +). 
        Use trapezium rule, but consider more sophisticated integration
        routine in near future. */

    for (i=1; i<(NPOINTS-1); i++) {
      wnu[i]=(nu[i+1]-nu[i-1])/2.;
        }
    wnu[0]=(nu[1]-nu[0])/2.;
    wnu[NPOINTS-1]=(nu[NPOINTS-1]-nu[NPOINTS-2])/2.;
    dn=0.;
    for (i=0; i<NPOINTS; i++) {
      if (i != k) 
        dn=dn+alpha[i]/(pow(nu[i],2.)-pow(nu[k],2.))*wnu[i];
        }
        n=n01+dn/(2.*pow(PI,2));
    /*  printf("\t%.25lf\n\n", n);*/
    return n ;
}
RiceMunk commented 8 years ago

Implemented to develop branch with Merge #17