/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* This file is distributed subject to a Software License Agreement found
* in the file LICENSE that is included with this distribution. 
\*************************************************************************/

/* program: resisveWall
 * purpose: calculate power lost on wall of beam chamber using standard formulas
 * 
 * Louis Emery and Kathy Harkay, 2018
 */

#include "mdb.h"
#include "SDDS.h"
#include "scan.h"
#include <ctype.h>
#include "constants.h"

static char *USAGE =  "resistiveWallPower -fullHeight=<value-mm> -resistivityFile=<string> -temperatureRange=begin=<value>,end=<value>,number=<value> -current=<value-mA> -bunches=<value> -length=<value> {-profile=file,time=<string>,shape=<string> | -sigmat=<value-ps> [-explicitsum] }\n";

#define CLO_FULLHEIGHT 0
#define CLO_RESISTIVITY 1
#define CLO_TEMPERATURE 2
#define CLO_CURRENT 3
#define CLO_BUNCHES 4
#define CLO_PROFILE 5
#define CLO_BUNCHLENGTH 6
#define CLO_HARMONICSUM 7
#define CLO_LENGTH 8
#define CLO_PIPE 9
#define CLO_MAJOR_ORDER 10
#define CLO_OPTIONS 11

char *option[CLO_OPTIONS] = {
  "fullheight", "resistivityFile", "temperature", "current", "bunches", "profile", "sigmat", "explicitsum", "length", "pipe", "majorOrder",
};

double fullheight=0,resistivityFixed=0,beginTemperature=20,endTemperature=100,current_mA=100,sigmat_ps=0,length=5.0;
long bunches=24, numberTemperature=11, doexplicitsum=0;

double circumference = 1104;

int main(int argc, char* argv[])
{
  SCANNED_ARG *s_arg;
  long i, i_arg, iharm; 
  unsigned long pipeFlags=0, majorOrderFlag, interpCode, flags;
  short columnMajorOrder = -1;
  char *inputFile, *outputFile;
  char *resistivityFile;
  SDDS_DATASET resistivityTable;
  SDDS_DATASET outputPage;
  double *resistivityArray, *resistivityTempArray;
  long resistivityRows;
  char *profileFile, *profileTimeCol, *profileShapeCol;
  double *profileFFT, *profileFrequency;
  long profileFFTRows;
  long order=2;
  double argument;

  double radius, omega, sigmaz, halfheight, curlyR, delta_um, meanFreePath_um;
  double omega_cutoff;
  
  double temperature, deltaTemperature, resistivity, powerDensity, power;
  OUTRANGE_CONTROL aboveRangeRes, belowRangeRes;
  long harm_limit;
  double formFactor;
  double rhoLambda, alpha, Rinf, Ranomalous;
  long doASE = 1;

  profileFile = profileTimeCol = profileShapeCol = NULL;
  inputFile = outputFile = NULL;
  resistivityFile = NULL;
  columnMajorOrder = 1;

  SDDS_RegisterProgramName(argv[0]);
  argc = scanargs(&s_arg, argc, argv);
  if (argc<2) { 
    fprintf(stderr, "%s", USAGE);
    return(1);
  }
  for (i_arg=1; i_arg<argc; i_arg++) {
    if (s_arg[i_arg].arg_type==OPTION) {
      delete_chars(s_arg[i_arg].list[0], "_");
      switch (match_string(s_arg[i_arg].list[0], option, CLO_OPTIONS, 0)) {
      case CLO_MAJOR_ORDER:
        majorOrderFlag=0;
        s_arg[i_arg].n_items -=1;
        if (s_arg[i_arg].n_items>0 &&
            (!scanItemList(&majorOrderFlag, s_arg[i_arg].list+1, &s_arg[i_arg].n_items, 0,
                           "row", -1, NULL, 0, SDDS_ROW_MAJOR_ORDER,
                           "column", -1, NULL, 0, SDDS_COLUMN_MAJOR_ORDER,
                           NULL)))
          SDDS_Bomb("invalid -majorOrder syntax/values");
         if (majorOrderFlag&SDDS_COLUMN_MAJOR_ORDER)
           columnMajorOrder=1;
         else if (majorOrderFlag&SDDS_ROW_MAJOR_ORDER)
           columnMajorOrder=0;
         break;
      case CLO_PIPE:
        if (!processPipeOption(s_arg[i_arg].list+1, s_arg[i_arg].n_items-1, &pipeFlags))
          SDDS_Bomb("invalid -pipe syntax");
        break;
      case CLO_FULLHEIGHT:
        if (s_arg[i_arg].n_items!=2)
          SDDS_Bomb("Invalid -fullheight syntax.");
        if (!get_double(&fullheight, s_arg[i_arg].list[1]))
          SDDS_Bomb("Invalid -fullheight value provided.");
        break;
      case CLO_RESISTIVITY:
        if (s_arg[i_arg].n_items!=2)
          SDDS_Bomb("Invalid -resistivityFile syntax.");
        resistivityFile = s_arg[i_arg].list[1];
        break;
      case CLO_TEMPERATURE:
        if (s_arg[i_arg].n_items !=4 )
          SDDS_Bomb("Invalid -temperatureRange syntax.");
        s_arg[i_arg].n_items -= 1;
        if (!scanItemList(&flags, s_arg[i_arg].list+1, &s_arg[i_arg].n_items, 0,
                          "begin", SDDS_DOUBLE, &beginTemperature, 1, 0,
                          "end", SDDS_DOUBLE, &endTemperature, 1, 0,
                          "number", SDDS_LONG, &numberTemperature, 1, 0,
                          NULL))
          SDDS_Bomb("invalid -temperatureRange syntax");
     
        break;
      case CLO_CURRENT:
        if (s_arg[i_arg].n_items!=2)
          SDDS_Bomb("Invalid -current syntax.");
        if (!get_double(&current_mA, s_arg[i_arg].list[1]))
          SDDS_Bomb("Invalid -current value provided.");
        break;
      case CLO_BUNCHES:
        if (s_arg[i_arg].n_items!=2)
          SDDS_Bomb("Invalid -bunches syntax.");
        if (!get_long(&bunches, s_arg[i_arg].list[1]))
          SDDS_Bomb("Invalid -bunches value provided.");
        break;
      case CLO_BUNCHLENGTH:
        if (s_arg[i_arg].n_items!=2)
          SDDS_Bomb("Invalid -sigmat_ps syntax.");
        if (!get_double(&sigmat_ps, s_arg[i_arg].list[1]))
          SDDS_Bomb("Invalid -sigmat_ps value provided.");
        break;
      case CLO_PROFILE:
        if (s_arg[i_arg].n_items!=2)
          SDDS_Bomb("Invalid -profile syntax.");
        profileFile = s_arg[i_arg].list[1];
        s_arg[i_arg].n_items -= 1;
        if (!scanItemList(&flags, s_arg[i_arg].list+1, &s_arg[i_arg].n_items, 0,
                          "time", SDDS_STRING, &profileTimeCol, 1, 0,
                          "shape", SDDS_STRING, &profileShapeCol, 1, 0,
                          NULL))
          SDDS_Bomb("invalid -profile syntax");
     
        break;
      case CLO_HARMONICSUM:
        if (s_arg[i_arg].n_items!=1)
          SDDS_Bomb("Invalid -explicitsum syntax.");
        doexplicitsum = 1;
        break;
      case CLO_LENGTH:
        if (s_arg[i_arg].n_items!=2)
          SDDS_Bomb("Invalid -length syntax.");
        if (!get_double(&length, s_arg[i_arg].list[1]))
          SDDS_Bomb("Invalid -length value provided.");
        break;
      default:
        fprintf(stderr, "error: unknown/ambiguous option: %s\n", 
                s_arg[i_arg].list[0]);
        exit(1);
        break;
      }
    }
    else {
      if (!outputFile)
        outputFile = s_arg[i_arg].list[0];
      else
        bomb("too many filenames given", USAGE);
    }
  }

  /*  processFilenames("resistiveWall", NULL, &outputFile, pipeFlags, 0, NULL); */

  if (!fullheight || !resistivityFile ||  !sigmat_ps || !current_mA) {
    SDDS_Bomb("Invalid values given or unspecified value.");
  }

  if (profileFile && sigmat_ps) 
    SDDS_Bomb("Can't use options -profile and -sigmat together.");

  if (!SDDS_InitializeInput(&resistivityTable, resistivityFile) || SDDS_ReadTable(&resistivityTable)!=1)
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  resistivityRows = SDDS_CountRowsOfInterest(&resistivityTable);

  if (!(resistivityTempArray=(double*)SDDS_GetColumnInDoubles(&resistivityTable,"Temperature")))
    SDDS_PrintErrors(stderr, SDDS_EXIT_PrintErrors|SDDS_VERBOSE_PrintErrors);
  if (!(resistivityArray=(double*)SDDS_GetColumnInDoubles(&resistivityTable,"Resistivity")))
    SDDS_PrintErrors(stderr, SDDS_EXIT_PrintErrors|SDDS_VERBOSE_PrintErrors);
  if (!SDDS_Terminate(&resistivityTable))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
 
  if (profileFile) 
     SDDS_Bomb("Options -profile is inoperable for now. Use -sigmat.");
  if (profileFile) {
    /* perhaps we should just have the FFT of the profile as input file, instead of doing hte FFT internally with all the little details to worry about. With FFT file, one could interpolate the necessary frequency values */
    profileFFT = (double *) tmalloc(sizeof(double)*1000);
    profileFrequency = (double *) tmalloc(sizeof(double)*1000);
  }
  
  if (!SDDS_InitializeOutput(&outputPage, SDDS_BINARY, 1, "Resistive wall power",
                             "Power results", outputFile))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  
  
  if (0>SDDS_DefineColumn(&outputPage, "Temperature", "T", "K", NULL, "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineColumn(&outputPage, "Resistivity", "R", "Ohm-m", NULL, "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineColumn(&outputPage, "PowerDensity", "P/l", "W/m", NULL, "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineColumn(&outputPage, "Power", "P", "W", NULL, "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineColumn(&outputPage, "MeanFreePath", "$gl$r", "m", "Mean free path", "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineColumn(&outputPage, "SkinDepth", "$gd$r", "m", "Skin depth", "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineColumn(&outputPage, "CurlyR", "CurlyR", "Ohm", "Resistivity divided by skin depth", "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineParameter(&outputPage, "Current",  "I", "mA", NULL, "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineParameter(&outputPage, "SigmaT", "$gs$r$bt$n", "ps", NULL, "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineParameter(&outputPage, "Bunches", "N", NULL, NULL, "%ld", SDDS_LONG, 0) 
    )
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  if (!SDDS_WriteLayout(&outputPage))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  
  if (!SDDS_StartPage(&outputPage, numberTemperature))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  if (!SDDS_SetParameters(&outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 
                          "Current", current_mA, 
                          "SigmaT", sigmat_ps ,
                          "Bunches", bunches, 
                          NULL))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  
  radius = circumference/ PIx2;
  omega = PIx2 * c_mks / circumference;
  sigmaz = sigmat_ps * 1e-12 * c_mks;
  halfheight = fullheight/2.0;
  harm_limit = PIx2 / (sigmat_ps * 1e-12 * omega * bunches);

  deltaTemperature = (endTemperature - beginTemperature)/ (numberTemperature - 1);
  aboveRangeRes.flags = belowRangeRes.flags = OUTRANGE_SATURATE;

  /* Anomalous Skin Effect constants */
  rhoLambda = 6.6e-16; /* for copper; valid for other? */

  for (i=0; i < numberTemperature; i++) {
    temperature = beginTemperature + i * deltaTemperature;
    resistivity = interpolate(resistivityArray, resistivityTempArray, resistivityRows, temperature, &belowRangeRes,  &aboveRangeRes, order, &interpCode, 1);
    
    curlyR  = sqrt( resistivity * omega * mu_o / 2); /* Rs in S.H. Kim LS-329 */
    delta_um = 1e6 * sqrt(2 * resistivity / (omega * mu_o)); /* delta_s in S.H. Kim LS-329 */
    /* rhoLambda here is suspect, since it applied to Cu in a reference */
    meanFreePath_um = rhoLambda / resistivity * 1e6 ;
    omega_cutoff = 2 * resistivity / sqr(meanFreePath_um * 1e-6) / mu_o;

    if (!doexplicitsum) {
      /* formula for integral if bunch is gaussian */
      powerDensity = 1.22694532077 * sqr(current_mA/1000) / bunches * curlyR / (PIx2 * halfheight) * pow(radius/sigmaz, 1.5);
    }
    else {
      powerDensity = 0;
      /* iharm =0 has zero contribution */
      for (iharm=1 ; iharm < 1.0*harm_limit; iharm++) {
        /* argument for bunch profile fourier transform */
        argument = - pow(iharm * bunches * omega * sigmat_ps * 1e-12, 2 );
        if (!profileFile) 
          formFactor = exp(argument);
        else {
          formFactor = interpolate(profileFFT, profileFrequency, profileFFTRows, iharm * bunches * omega, &belowRangeRes,  &aboveRangeRes, order, &interpCode, 1);
        }
          
        /* Anomalous Skin Effect */
        alpha = 0.75 * sqr(rhoLambda) * iharm * bunches * omega * mu_o / pow(resistivity,3);
        /*        if (!(iharm%100)) {
          fprintf(stderr,"iharm %ld iharm * omega %le omega_cutoff  %le formFactor %le alpha %le \n", iharm, iharm * omega, omega_cutoff, formFactor, alpha);
        }
        */
        if (doASE && (alpha > 3)) {
          Rinf = pow( sqrt(3.0)/(16*PI) * rhoLambda * sqr(iharm * bunches * omega * mu_o) , 1.0/3.0);
          Ranomalous = Rinf * ( 1 + 1.57 * pow(alpha, -0.276) );
          powerDensity += 2 * sqrt(iharm * bunches * 1.0) * Ranomalous * formFactor;
        }
        else
          powerDensity += 2 * sqrt(iharm * bunches * 1.0) * curlyR * formFactor;
      }
      powerDensity *= sqr(current_mA/1000) / (PIx2 * halfheight);
    }
    power = powerDensity * length;
    
    /*    fprintf( stdout, "At temperature %0.1lf K:\nresistivity: %le Ohm-m\nCurlyR: %le\nSkin depth at c/R: %le um\nMean free path: %le um\nPower Bane/Krinski: %le W/m\nTotal Power Bane/Krinski: %le W\n", temperature, resistivity, curlyR, delta_um, meanFreePath_um, powerDensity, power);
     */

    if(!SDDS_SetRowValues(&outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, i,
                          "Temperature", temperature, 
                          "Resistivity", resistivity, 
                          "PowerDensity", powerDensity,
                          "Power", power,
                          "MeanFreePath", meanFreePath_um / 1e6,
                          "SkinDepth", delta_um / 1e6,
                          "CurlyR", curlyR, 
                          NULL))
      SDDS_PrintErrors(stdout, SDDS_EXIT_PrintErrors|SDDS_VERBOSE_PrintErrors);
  }
  if (!SDDS_WritePage(&outputPage) || SDDS_Terminate(&outputPage))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  
  free_scanargs(&s_arg, argc);
  return(0);
}

