예제 #1
0
/* Obtain the molecular extinction by interpolating the opacity grid at
   the specified atmospheric layer:                                         */
int
interpolmolext(struct transit *tr, /* transit struct                        */
               PREC_NREC r,        /* Radius index                          */
               PREC_RES **kiso){   /* Extinction coefficient array          */

  struct opacity    *op=tr->ds.op;  /* Opacity struct                       */
  struct molecules *mol=tr->ds.mol;

  long Nmol, Ntemp, Nwave;
  PREC_RES *gtemp;
  int       *gmol;
  int itemp, imol,
      i, m;   /* for-loop indices                                           */
  double ext; /* Interpolated extinction coefficient                        */

  /* Layer temperature:                                                     */
  PREC_ATM temp = tr->atm.t[r] * tr->atm.tfct;
  /* Gridded temperatures:                                                  */
  gtemp = op->temp;
  Ntemp = op->Ntemp;
  /* Gridded molecules list:                                                */
  gmol = op->molID;
  Nmol = op->Nmol;
  /* Wavenumber array size:                                                 */
  Nwave = op->Nwave;

  /* Interpolate:                                                           */
  /* Find index of grid-temperature immediately lower than temp:            */
  itemp = binsearchapprox(gtemp, temp, 0, Ntemp);
  if (temp < gtemp[itemp])
    itemp--;
  tr_output(TOUT_DEBUG, "Temperature: T[%i]=%.0f < %.2f < T[%.i]=%.0f\n",
    itemp, gtemp[itemp], temp, itemp+1, gtemp[itemp+1]);

  for (i=0; i < Nwave; i++){
    /* Add contribution from each molecule:                                 */
    for (m=0; m < Nmol; m++){
      /* Linear interpolation of the extinction coefficient:                */
      ext = (op->o[r][itemp  ][m][i] * (gtemp[itemp+1]-temp) +
             op->o[r][itemp+1][m][i] * (temp - gtemp[itemp]) ) /
                                                 (gtemp[itemp+1]-gtemp[itemp]);
      imol = valueinarray(mol->ID, gmol[m], mol->nmol);
      kiso[r][i] += mol->molec[imol].d[r] * ext;
    }
  }

  return 0;
}
예제 #2
0
/* DEF */
static PREC_RES
eclipsetau(struct transit *tr,
           PREC_RES height,    /* Altitude down to where calculate tau      */
           PREC_RES *ex){      /* Extinction per layer [rad]                */
  /* Incident angle:                                                        */
  //PREC_RES angle = tr->angles[tr->angleIndex];
  //PREC_RES angle_rad = angle * DEGREES;   
  /* Layers radius array:                                                   */
  prop_samp *rads = &tr->rads;  /* Radius sampling                           */
  PREC_RES *rad  = rads->v;     /* Radius array                              */
  /* Get the index rs, of the sampled radius immediately below or equal
     to height (i.e. rad[rs] <= height < rad[rs+1]):                        */
  int rs = binsearchapprox(rad, height, 0, tr->rads.n-1);

  /* Returns 0 if this is the top layer (no distance travelled):            */
  if (rs == tr->rads.n-1)
    return 0.0;

  /* Move pointers to the location of height:                               */
  rad += rs;
  ex  += rs;

  /* Number of layers beween height and the top layer:                      */
  int nrad = tr->rads.n - rs;

  PREC_RES res;          /* Optical depth divided by units of radius        */
  PREC_RES x3[3], r3[3]; /* Interpolation variables                         */

  /* Conversion to radian:                                                  */
  //PREC_RES angle_rad = angle * DEGREES;   

  /* Distance along the path:                                               */
  PREC_RES s[nrad];

  /* Providing three necessary points for spline integration:               */
  const PREC_RES tmpex  = *ex;
  const PREC_RES tmprad = *rad;

  if(nrad==2) *ex = interp_parab(rad-1, ex-1, rad[0]);
  else        *ex = interp_parab(rad,   ex,   rad[0]);

  if(nrad==2){
    x3[0] = ex[0];
    x3[2] = ex[1];
    x3[1] = (ex[1]+ex[0])/2.0;
    r3[0] = rad[0];
    r3[2] = rad[1];
    r3[1] = (rad[0]+rad[1])/2.0;
    *rad = tmprad;
    *ex  = tmpex;
    rad  = r3;
    ex   = x3;
    nrad++;
  }

  /* Distance along the path:                                               */
  s[0] = 0.0;
  for(int i=1; i < nrad; i++){
    s[i] = s[i-1] + (rad[i] - rad[i-1]); // /cos(angle_rad);
  }

  /* Integrate extinction along the path:                                   */
  /* Use spline if GSL is available along with at least 3 points:           */
//#ifdef _USE_GSL
//  gsl_interp_accel *acc = gsl_interp_accel_alloc();
//  gsl_interp *spl = gsl_interp_alloc(gsl_interp_cspline, nrad);
//  gsl_interp_init(spl, s, ex, nrad);
//  res = gsl_interp_eval_integ(spl, s, ex, 0, s[nrad-1], acc);
//  gsl_interp_free(spl);
//  gsl_interp_accel_free(acc);
//#else
//#error non equispaced integration is not implemented without GSL
//#endif /* _USE_GSL */

  /* Safety mode: GSL is acting up sometimes                                */
  res = integ_trapz(s, ex, nrad);

  /* Optical depth divided by units of radius:                              */
  return res;
}
예제 #3
0
/* FUNCTION: Compute the molecular extinction.
   Store results in kiso.  If permol is true, calculate extinction per
   molecule separately; else, collapse all extinction into kiso[0].         */
int
computemolext(struct transit *tr, /* transit struct                         */
              PREC_RES **kiso,    /* Extinction coefficient array [mol][wn] */
              PREC_ATM temp,      /* Temperature                            */
              PREC_ATM *density,  /* Density per species                    */
              double *Z,          /* Partition Function per isotope         */
              int permol){        /* Calculate the extinction per molecule  */

  /* Transit structures:                                                    */
  struct opacity    *op =tr->ds.op;
  struct isotopes   *iso=tr->ds.iso;
  struct molecules  *mol=tr->ds.mol;
  struct line_transition *lt=&(tr->ds.li->lt);

  PREC_NREC ln;
  int i, m=0, mm,
      *idop, *ilor;
  long j, maxj, minj, offset;

  /* Voigt profile variables:                                               */
  PREC_VOIGT ***profile=op->profile;  /* Voigt profile                      */
  PREC_NREC **profsize=op->profsize;  /* Voigt-profile half-size            */
  double *aDop=op->aDop,          /* Doppler-width sample                   */
         *aLor=op->aLor;          /* Lorentz-width sample                   */
  int nDop=op->nDop,              /* Number of Doppler samples              */
      nLor=op->nLor;              /* Number of Lorentz samples              */

  PREC_NREC subw,
            nlines=tr->ds.li->n_l; /* Number of line transitions            */
  PREC_RES wavn, next_wn;
  double fdoppler, florentz, /* Doppler and Lorentz-broadening factors      */
         csdiameter;         /* Collision diameter                          */
  double propto_k;
  double *kmax, *kmin;       /* Maximum and minimum values of propto_k      */

  PREC_VOIGTP *alphal, *alphad;

  int niso = iso->n_i,        /* Number of isotopes in atmosphere           */
      nmol = mol->nmol,       /* Number of species in atmosphere            */
      Nmol;                   /* Number of species with line-transitions    */

  int iown, idwn;             /* Line-center indices                        */

  double maxwidth=0,   /* Maximum width between Lorentz and Doppler         */
         minwidth=1e5; /* Minimum width among isotopes in a Layer           */

  int ofactor=tr->owns.o;  /* Dynamic oversampling factor                   */

  long nadd  = 0, /* Number of co-added lines                               */
       nskip = 0, /* Number of skipped lines                                */
       neval = 0; /* Number of evaluated profiles                           */

  /* Wavenumber array variables:                                            */
  PREC_RES  *wn = tr->wns.v;
  PREC_NREC  nwn = tr->wns.n,
            onwn = tr->owns.n;

  /* Wavenumber sampling intervals:                                         */
  PREC_RES  dwn = tr->wns.d /tr->wns.o,   /* Output array                   */
           odwn = tr->owns.d/tr->owns.o;  /* Oversampling array             */

  /* Allocate alpha Lorentz and Doppler arrays:                             */
  alphal = (PREC_VOIGTP *)calloc(niso, sizeof(PREC_VOIGTP));
  alphad = (PREC_VOIGTP *)calloc(niso, sizeof(PREC_VOIGTP));

  /* Allocate width indices array:                                          */
  idop = (int *)calloc(niso, sizeof(int));
  ilor = (int *)calloc(niso, sizeof(int));

  kmax = (double *)calloc(op->Nmol, sizeof(double));
  kmin = (double *)calloc(op->Nmol, sizeof(double));

  /* Number of species in output array:                                     */
  if (permol)
    Nmol = op->Nmol;
  else
    Nmol = 1;

  /* Zero the extinction array:                                             */
  for (mm=0; mm < Nmol; mm++)
    for (i=0; i < nwn; i++)
      kiso[mm][i] = 0.0;

  /* Constant factors for line widths:                                      */
  fdoppler = sqrt(2*KB*temp/AMU) * SQRTLN2 / LS;
  florentz = sqrt(2*KB*temp/PI/AMU) / (AMU*LS);

  /* Calculate the isotope's widths for this layer:                         */
  for(i=0; i<niso; i++){
    /* Lorentz profile width:                                               */
    alphal[i] = 0.0;
    for(j=0; j<nmol; j++){
      /* Isotope's collision diameter:                                      */
      csdiameter = (mol->radius[j] + mol->radius[iso->imol[i]]);
      /* Line width:                                                        */
      alphal[i] += density[j]/mol->mass[j] * csdiameter * csdiameter *
                   sqrt(1/iso->isof[i].m + 1/mol->mass[j]);
    }
    alphal[i] *= florentz;

    /* Doppler profile width (divided by central wavenumber):               */
    alphad[i] = fdoppler / sqrt(iso->isof[i].m);

    /* Print Lorentz and Doppler broadening widths:                         */
    if(i <= 0)
      tr_output(TOUT_RESULT, "Broadening (cm-1): Lorentz: %.5e, Doppler: "
              "%.5e (T=%.2f).\n", alphal[i], alphad[i]*wn[0], temp);

    maxwidth = fmax(alphal[i], alphad[i]*wn[0]); /* Max between Dop and Lor */
    minwidth = fmin(minwidth, maxwidth);

    /* Search for aDop and aLor indices for alphal[i] and alphad[i]:        */
    idop[i] = binsearchapprox(aDop, alphad[i]*wn[0], 0, nDop);
    ilor[i] = binsearchapprox(aLor, alphal[i],       0, nLor);
  }

  tr_output(TOUT_DEBUG, "Minimum width in layer: %.9f\n", minwidth);

  /* Determine the maximum and minimum line-strength per isotope:           */
  for(ln=0; ln<nlines; ln++){
    /* Wavenumber of line transition:                                       */
    wavn = 1.0 / (lt->wl[ln] * lt->wfct);
    /* Isotope ID of line:                                                  */
    i = lt->isoid[ln];
    /* Species index in output array:                                       */
    if (permol)
      m = valueinarray(op->molID, mol->ID[iso->imol[i]], op->Nmol);

    /* If it is beyond the lower limit, skip to next line transition:       */
    if ((wavn < tr->wns.i) || (wavn > tr->owns.v[onwn-1]))
      continue;

    /* Calculate the extinction coefficient except the broadening factor:   */
    propto_k = iso->isoratio[i]               *       /* Density            */
            SIGCTE     * lt->gf[ln]           *       /* Constant * gf      */
            exp(-EXPCTE*lt->efct*lt->elow[ln]/temp) * /* Level population   */
            (1-exp(-EXPCTE*wavn/temp))        /       /* Induced emission   */
            iso->isof[i].m                    /       /* Isotope mass       */
            Z[i];                                     /* Partition function */
    /* Maximum line strength among all transitions for each species:        */
    if (kmax[m] == 0){
      kmax[m] = kmin[m] = propto_k;
    } else{
      kmax[m] = fmax(kmax[m], propto_k);
      kmin[m] = fmin(kmin[m], propto_k);
    }
  }

  /* Compute the spectra, proceed for every line:                           */
  for (ln=0; ln<nlines; ln++){
    wavn = 1.0/(lt->wl[ln]*lt->wfct);
    i    = lt->isoid[ln];
    if (permol)
      m = valueinarray(op->molID, mol->ID[iso->imol[i]], op->Nmol);

    if ((wavn < tr->wns.i) || (wavn > tr->owns.v[onwn-1]))
      continue;

    /* Extinction coefficient (factors depending on the line transition):   */
    propto_k = lt->gf[ln]                              *
               exp(-EXPCTE*lt->efct*lt->elow[ln]/temp) *
               (1-exp(-EXPCTE*wavn/temp));

    /* Index of closest oversampled wavenumber:                             */
    iown = (wavn - tr->wns.i)/odwn;
    if (fabs(wavn - tr->owns.v[iown+1]) < fabs(wavn - tr->owns.v[iown]))
      iown++;

    /* Check if the next line falls on the same sampling index:             */
    while (ln != nlines-1 && lt->isoid[ln+1] == i){
      next_wn = 1.0/(lt->wl[ln+1]*lt->wfct);
      if (fabs(next_wn - tr->owns.v[iown]) < odwn){
        nadd++;
        ln++;
        /* Add the contribution from this line into the opacity:            */
        propto_k += lt->gf[ln]                                    *
                    exp(-EXPCTE * lt->efct * lt->elow[ln] / temp) *
                    (1-exp(-EXPCTE*next_wn/temp));
      }
      else
        break;
    }
    /* The rest of the factors:                                             */
    propto_k *= SIGCTE*iso->isoratio[i] / (iso->isof[i].m * Z[i]);

    /* If line is too weak, skip it:                                        */
    if (propto_k < tr->ds.th->ethresh * kmax[m]){
      nskip++;
      continue;
    }
    /* Multiply by the species density:                                     */
    if (permol == 0)
      propto_k *= density[iso->imol[i]];

    /* Index of closest (but not larger than) coarse-sampling wavenumber:   */
    idwn = (wavn - tr->wns.i)/dwn;

    /* FINDME: de-hard code this threshold                                  */
    /* Update Doppler width according to the current wavenumber:            */
    if (alphad[i]*wavn/alphal[i] >= 1e-1){
      /* Recalculate index for Doppler width:                               */
      idop[i] = binsearchapprox(aDop, alphad[i]*wavn, 0, nDop);
    }

    /* Sub-sampling offset between center of line and dyn-sampled wn:       */
    subw   = iown - idwn*ofactor;
    /* Offset between the profile and the wavenumber-array indices:         */
    offset = iown - profsize[idop[i]][ilor[i]];
    /* Range that contributes to the opacity:                               */
    /* Set the lower and upper indices of the profile to be used:           */
    minj = idwn - (profsize[idop[i]][ilor[i]] - subw) / ofactor;
    maxj = idwn + (profsize[idop[i]][ilor[i]] + subw) / ofactor;
    if (minj < 0)
      minj = 0;
    if (maxj >= nwn)
      maxj = nwn-1;

    /* Add the contribution from this line to the opacity spectrum:         */
    /* Adding in more complex but faster array indexing based on simpler
     * pointer arrithmatic                                                  */
    PREC_VOIGT * tmp_point = profile[idop[i]][ilor[i]];
    int beg_j = ofactor*minj - offset;
    for(j=minj; j<=maxj; ++j){
        if (beg_j > 2*profsize[idop[i]][ilor[i]])
            break;
        if (beg_j >= 0)
            kiso[m][j] += propto_k * tmp_point[beg_j];
        beg_j += ofactor;
    }
    neval++;
  }

  tr_output(TOUT_DEBUG, "Number of co-added lines:     %8li  (%5.2f%%)\n",
    nadd,  nadd*100.0/nlines);
  tr_output(TOUT_DEBUG, "Number of skipped profiles:   %8li  (%5.2f%%)\n",
    nskip, nskip*100.0/nlines);
  tr_output(TOUT_DEBUG, "Number of evaluated profiles: %8li  (%5.2f%%)\n",
    neval, neval*100.0/nlines);

  /* Free allocated memory:                                                 */
  free(alphal);
  free(alphad);
  free(idop);
  free(ilor);
  free(kmax);
  free(kmin);

  return 0;
}