/*************************************************************************\
* 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 =  "resistiveWall -fullHeight=<value-mm> -resistivityFile=<string> -specificHeatFile=<string> -thermalConductivityFile=<string> -endTemperature=<value-degK> -current=<value-mA> -bunches=<value> -length=<value> -elements=<value> -sigmat=<value-ps>\n";

#define CLO_FULLHEIGHT 0
#define CLO_RESISTIVITY 1
#define CLO_SPECIFICHEAT 2
#define CLO_THERMALCONDUCTIVITY 3
#define CLO_ENDTEMPRATURE 4
#define CLO_CURRENT 5
#define CLO_BUNCHES 6
#define CLO_BUNCHLENGTH 7
#define CLO_LENGTH 8
#define CLO_MASS 9
#define CLO_ELEMENTS 10
#define CLO_STEPS 11
#define CLO_DELTATIME 12
#define CLO_SKIPINTERVAL 13
#define CLO_PIPE 14
#define CLO_MAJOR_ORDER 15
#define CLO_OPTIONS 16

char *option[CLO_OPTIONS] = {
  "fullheight", "resistivityFile", "specificHeatFile", "thermalConductivityFile", "endTemperature", "current", "bunches", "sigmat", "length", "mass", "elements", "steps", "deltaTime", "skipInterval", "pipe", "majorOrder",
};

double fullheight=0,resistivityFixed=0,endTemperature=20,current_mA=100,sigmat_ps=33,mass=50,length=5.0, deltaTime=1;
long elements=100, steps=10, skipInterval=1;
long bunches=24;

double circumference = 1104;

int main(int argc, char* argv[])
{
  SCANNED_ARG *s_arg;
  long i, iStep, i_arg; 
  unsigned long pipeFlags=0, majorOrderFlag, interpCode;
  short columnMajorOrder = -1;
  char *inputFile, *outputFile;
  char *resistivityFile, *thermalConductivityFile, *specificHeatFile;
  SDDS_DATASET resistivityTable, thermalConductivityTable, specificHeatTable;
  SDDS_DATASET outputPage;
  double *resistivityArray, *resistivityTempArray;
  long resistivityRows;
  double *specificHeatArray, *specificHeatTempArray;
  long specificHeatRows;
  double *thermalConductivityArray, *thermalConductivityTempArray;
  long thermalConductivityRows;
  long order;
  long elements1,elements2;
  double crosssection;

  inputFile = outputFile = NULL;
  resistivityFile = thermalConductivityFile = specificHeatFile=NULL;
  columnMajorOrder = 1;
  double radius, omega, sigmaz, halfheight, curlyR, delta_um, powerHighTemp, meanFreePath_um;
  double omega_cutoff, omega_cutoff_20K, curlyR_20K, delta_20K_um, powerLowTemp, meanFreePath_20K_um;
  
  double *temperature, *newTemperature, *internalPower, *powerFlow, *resistivity, *specificHeat, *thermalConductivity;
  OUTRANGE_CONTROL aboveRangeRes, belowRangeRes;
  OUTRANGE_CONTROL aboveRangeCp, belowRangeCp;
  OUTRANGE_CONTROL aboveRangeTC, belowRangeTC;
  long harm_limit;
  double density=2.7e3;
  double *zMid,deltaZ, time, gradient, aveTemperature;

  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_SPECIFICHEAT:
        if (s_arg[i_arg].n_items!=2)
          SDDS_Bomb("Invalid -specificHeatFile syntax.");
        specificHeatFile = s_arg[i_arg].list[1];
        break;
      case CLO_THERMALCONDUCTIVITY:
        if (s_arg[i_arg].n_items!=2)
          SDDS_Bomb("Invalid -thermalConductivityFile syntax.");
        thermalConductivityFile = s_arg[i_arg].list[1];
        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_ELEMENTS:
        if (s_arg[i_arg].n_items!=2)
          SDDS_Bomb("Invalid -elements syntax.");
        if (!get_long(&elements, s_arg[i_arg].list[1]))
          SDDS_Bomb("Invalid -elements value provided.");
        break;
      case CLO_MASS:
        if (s_arg[i_arg].n_items!=2)
          SDDS_Bomb("Invalid -mass syntax.");
        if (!get_double(&mass, s_arg[i_arg].list[1]))
          SDDS_Bomb("Invalid -mass value provided.");
        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;
      case CLO_STEPS:
        if (s_arg[i_arg].n_items!=2)
          SDDS_Bomb("Invalid -steps syntax.");
        if (!get_long(&steps, s_arg[i_arg].list[1]))
          SDDS_Bomb("Invalid -steps value provided.");
        break;
      case CLO_SKIPINTERVAL:
        if (s_arg[i_arg].n_items!=2)
          SDDS_Bomb("Invalid -skipInterval syntax.");
        if (!get_long(&skipInterval, s_arg[i_arg].list[1]))
          SDDS_Bomb("Invalid -skipInterval value provided.");
        break;
      case CLO_DELTATIME:
        if (s_arg[i_arg].n_items!=2)
          SDDS_Bomb("Invalid -deltaTime syntax.");
        if (!get_double(&deltaTime, s_arg[i_arg].list[1]))
          SDDS_Bomb("Invalid -deltaTime 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 || !thermalConductivityFile || !sigmat_ps || !current_mA || !elements) {
    SDDS_Bomb("Invalid values given or unspecified value.");
  }

  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 (!SDDS_InitializeInput(&thermalConductivityTable, thermalConductivityFile) || SDDS_ReadTable(&thermalConductivityTable)!=1)
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  thermalConductivityRows = SDDS_CountRowsOfInterest(&thermalConductivityTable);

  if (!(thermalConductivityTempArray=(double*)SDDS_GetColumnInDoubles(&thermalConductivityTable,"Temperature")))
    SDDS_PrintErrors(stderr, SDDS_EXIT_PrintErrors|SDDS_VERBOSE_PrintErrors);
  if (!(thermalConductivityArray=(double*)SDDS_GetColumnInDoubles(&thermalConductivityTable,"ThermalConductivity")))
    SDDS_PrintErrors(stderr, SDDS_EXIT_PrintErrors|SDDS_VERBOSE_PrintErrors);
  if (!SDDS_Terminate(&thermalConductivityTable))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
  

  if (!SDDS_InitializeInput(&specificHeatTable, specificHeatFile) || SDDS_ReadTable(&specificHeatTable)!=1)
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  specificHeatRows = SDDS_CountRowsOfInterest(&specificHeatTable);

  if (!(specificHeatTempArray=(double*)SDDS_GetColumnInDoubles(&specificHeatTable,"Temperature")))
    SDDS_PrintErrors(stderr, SDDS_EXIT_PrintErrors|SDDS_VERBOSE_PrintErrors);
  if (!(specificHeatArray=(double*)SDDS_GetColumnInDoubles(&specificHeatTable,"SpecificHeat")))
    SDDS_PrintErrors(stderr, SDDS_EXIT_PrintErrors|SDDS_VERBOSE_PrintErrors);
  if (!SDDS_Terminate(&specificHeatTable))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
  

  if (!SDDS_InitializeOutput(&outputPage, SDDS_BINARY, 1, "Heating results",
                             "Heating 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, "zMid", "z", "m", NULL, "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineColumn(&outputPage, "Resistivity", "R", "Ohm-m", NULL, "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineColumn(&outputPage, "SpecificHeat", "Cp", "J/kg/K", NULL, "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineColumn(&outputPage, "InternalPower", "P$bint$n", "W", NULL, "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineColumn(&outputPage, "PowerFlow", "P$bflow$n", "W", NULL, "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineColumn(&outputPage, "ThermalConductivity", "$gk$r", "J/m/K", NULL, "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineParameter(&outputPage, "Time", "t", "s", NULL, "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineParameter(&outputPage, "Step", NULL, 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);
  
  /* MKS units */
  density = 2.7e3;
  crosssection = mass/ density/ length;
  deltaZ = length / elements;

  radius = circumference/ PIx2;
  omega = PIx2 * c_mks / circumference;
  sigmaz = sigmat_ps * 1e-12 * c_mks;
  halfheight = fullheight/2.0;
  curlyR  = sqrt( resistivityArray[resistivityRows-1] * omega * mu_o / 2);
  delta_um = 1e6 * sqrt(2 * resistivityArray[resistivityRows-1] / (omega * mu_o));
  meanFreePath_um = 6.6e-16 / resistivityArray[resistivityRows-1] * 1e6 ;
  powerHighTemp = 1.2 * sqr(current_mA/1000) / bunches * curlyR / (PIx2 * halfheight) * pow(radius/sigmaz, 1.5);

  fprintf( stdout, "At top 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", resistivityTempArray[resistivityRows-1], resistivityArray[resistivityRows-1], curlyR, delta_um, meanFreePath_um, powerHighTemp, powerHighTemp*length);

  curlyR_20K  = sqrt( resistivityArray[0] * omega * mu_o / 2);
  delta_20K_um = 1e6 * sqrt(2 * resistivityArray[0] / (omega * mu_o));
  meanFreePath_20K_um = 6.6e-16 / resistivityArray[0] * 1e6 ;
  
  powerLowTemp = 1.2 * sqr(current_mA/1000) / bunches * curlyR_20K / (PIx2 * halfheight) * pow(radius/sigmaz, 1.5);
  
  fprintf( stdout, "\nAt low 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", resistivityTempArray[0], resistivityArray[0], curlyR_20K, delta_20K_um, meanFreePath_20K_um, powerLowTemp, powerLowTemp*length);

  omega_cutoff = 2 * resistivityArray[resistivityRows-1] / sqr(meanFreePath_um * 1e-6) / mu_o;
  omega_cutoff_20K = 2 * (resistivityArray[0]) / sqr(meanFreePath_20K_um * 1e-6) / mu_o;


  harm_limit = PIx2 / (sigmat_ps * 1e-12 * omega * bunches);
  
  /* N elements . Two two extra ones for the temperature bath.
     Also it would come in useful with mixed bounday conditions.
     There would be N+1 edges or points. Even though each array would have
     different number of elements, we have to allocate the largest number
     in order to copy them to an sdds file */  
  elements1 = elements + 1;
  elements2 = elements + 2; /* make sure there are enough points for all arrays */
  zMid = (double *) tmalloc(sizeof(double)*elements2);  /* z is the midpoint location of the elements */
  internalPower = (double *) tmalloc(sizeof(double)*elements2);  /* need only elements in number */
  temperature = (double *) tmalloc(sizeof(double)*elements2);
  newTemperature = (double *) tmalloc(sizeof(double)*elements2);
  resistivity = (double *) tmalloc(sizeof(double)*elements2);    /* need only elements in number */
  specificHeat = (double *) tmalloc(sizeof(double)*elements2);   /* need only elements in number */
  /* property of edge */
  powerFlow = (double *) tmalloc(sizeof(double)*elements2);
  thermalConductivity = (double *) tmalloc(sizeof(double)*elements2);
  
  aboveRangeRes.flags = belowRangeRes.flags = OUTRANGE_SATURATE;
  aboveRangeCp.flags = belowRangeCp.flags = OUTRANGE_SATURATE;
  aboveRangeTC.flags = belowRangeTC.flags = OUTRANGE_SATURATE;
  order = 1;

  /* initial conditions */
  for (i=0; i< elements2; i++) {
    temperature[i] = endTemperature;
    zMid[i] = (i - 0.5) * length/ elements;
  }
  /* loop over time */
  time = 0;
  for (iStep=0; iStep < steps; iStep++) {
    /* material properties */
    for (i=1; i< elements1; i++) {
      /* the monotonicity code of 1 is important!!! */
      resistivity[i] = interpolate(resistivityArray, resistivityTempArray, resistivityRows, temperature[i], &belowRangeRes,  &aboveRangeRes, order, &interpCode, 1);
      specificHeat[i] = interpolate(specificHeatArray, specificHeatTempArray, specificHeatRows, temperature[i], &belowRangeCp,  &aboveRangeCp, order, &interpCode, 1);
      /* use two elements to determine the corrector thermal conductivity */
      aveTemperature = (temperature[i] + temperature[i+1])/ 2;
      thermalConductivity[i] = interpolate(thermalConductivityArray, thermalConductivityTempArray, thermalConductivityRows, aveTemperature, &belowRangeTC,  &aboveRangeTC, order, &interpCode, 1);
    }
    i=0;
    aveTemperature = (temperature[i] + temperature[i+1])/ 2;
    thermalConductivity[i] = interpolate(thermalConductivityArray, thermalConductivityTempArray, thermalConductivityRows, aveTemperature, &belowRangeTC,  &aboveRangeTC, order, &interpCode, 1);
    
    /* boundary conditions */
    temperature[0] = endTemperature;
    temperature[elements1] = endTemperature;
    for (i=0; i< elements1; i++) 
      newTemperature[i] = temperature[i];
    /* evaluate power and subsequent temperature change in each element */
    for (i=0; i< elements1; i++) {
      /* units are W/m (original formula) times m (deltaZ) */
      curlyR =  sqrt( resistivity[i] * omega * mu_o / 2);
      internalPower[i] = deltaZ * 1.2 * sqr(current_mA/1000) / bunches * curlyR / (PIx2 * halfheight) * pow(radius/sigmaz, 1.5);
      newTemperature[i]  += internalPower[i] / (specificHeat[i] * (mass/elements)) * deltaTime;
    }
    /* evaluate power flow */
    for (i=0; i<elements1; i++) {
      gradient = (temperature[i+1] - temperature[i])/ deltaZ;
      powerFlow[i] = - thermalConductivity[i] * crosssection * gradient;
      /*      fprintf(stdout,"thermalconductivity[%ld]: %le crossection %le  gradient %le\n", i, thermalConductivity[i], crosssection, gradient); */
      /*      fprintf(stdout,"powerFlow[%ld]: %le\n",i,powerFlow[i]); */
      newTemperature[i]   -= powerFlow[i] * deltaTime/ (specificHeat[i] * (mass/elements)) ;
      /* when looking at the change in temperature for one particular
         element one has to collect the heat flow from two consecutive
         loops. In effect the change is due to the double differences of
         three elements. i.e. second derivative. In addition the specific heat
         and the thermal conductivity are variables */
      newTemperature[i+1] += powerFlow[i] * deltaTime/ (specificHeat[i+1] * (mass/elements) );
    } 
    /* enforce boundary conditions again. */
    newTemperature[0] = endTemperature;
    newTemperature[elements] = endTemperature;

    SWAP_PTR(temperature, newTemperature);

    if (!(iStep%skipInterval)) {
      if (!SDDS_StartPage(&outputPage, elements1))
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
      
      if (!SDDS_SetParameters(&outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 
                              "Step", iStep, 
                              "Time", time, 
                              NULL))
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
      if (!SDDS_SetColumn(&outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, temperature, elements1, "Temperature")||
          !SDDS_SetColumn(&outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, zMid, elements1, "zMid")||
          !SDDS_SetColumn(&outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, resistivity, elements1, "Resistivity")||
          !SDDS_SetColumn(&outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, internalPower, elements1, "InternalPower")||
          !SDDS_SetColumn(&outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, powerFlow, elements1, "PowerFlow")||
          !SDDS_SetColumn(&outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, thermalConductivity, elements1, "ThermalConductivity")||
          !SDDS_SetColumn(&outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, specificHeat, elements1, "SpecificHeat")
          )
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
      
      if (!SDDS_WritePage(&outputPage))
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    }
    time += deltaTime;

  }
  if (SDDS_Terminate(&outputPage))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);

  free_scanargs(&s_arg, argc);
  free(zMid);
  free(resistivity);
  free(temperature);
  free(powerFlow);
  free(internalPower);
  return(0);
}

