1#include "PAHEmissionModel.h" 
    3double PAHEmissionModel::_energy;
 
    5double PAHEmissionModel::_frequency;
 
    7double PAHEmissionModel::_nc;
 
    9bool PAHEmissionModel::_approximate = 
false;
 
   11PAHEmissionModel::PAHEmissionModel()
 
   12    : _transitions(std::vector<std::vector<std::pair<double, double>>>(0)),
 
   13      _grid(std::vector<double>(0)) {}
 
   15PAHEmissionModel::PAHEmissionModel(
 
   16    const std::vector<std::vector<std::pair<double, double>>> &transitions)
 
   17    : _transitions(transitions), _grid(std::vector<double>(0)) {}
 
   19void PAHEmissionModel::setGrid(
const std::vector<double> &grid) {
 
   23  _fmin = *min_element(_grid.begin(), _grid.end());
 
   25  _fmax = *max_element(_grid.begin(), _grid.end());
 
   28void PAHEmissionModel::makeGrid(
double fmin, 
double fmax, 
double step) {
 
   47    _grid.push_back(fmin);
 
   52  if (_grid.back() != fmax) {
 
   54    _grid.push_back(fmax);
 
   58void PAHEmissionModel::setTransitions(
 
   59    const std::vector<std::vector<std::pair<double, double>>> &transitions) {
 
   61  _transitions = transitions;
 
   64void PAHEmissionModel::getTransitions(
 
   65    std::vector<std::vector<std::pair<double, double>>> &transitions) {
 
   67  transitions = _transitions;
 
   70void PAHEmissionModel::printTransitions() {
 
   72  std::cout.setf(std::ios::right);
 
   74  std::cout << std::setiosflags(std::ios::fixed) << std::setprecision(5)
 
   77  for (
const auto &transition : _transitions) {
 
   79    for (
const auto &t : transition) {
 
   81      std::cout << std::setw(10) << t.first << 
'\t' << std::setw(10) << t.second
 
   85    std::cout << std::endl;
 
   89void PAHEmissionModel::getSpectraAndConvolveWithLorentianOfFHWM(
 
   90    std::vector<std::vector<double>> &vector, 
double fwhm) {
 
   94  double hwhm = fwhm / 2.0;
 
   96  for (
auto &transition : _transitions) {
 
   98    std::vector<double> intensities(_grid.size());
 
  100    for (
auto &t : transition) {
 
  102      if (t.second == 0 || t.first < (_fmin - 22.0 * hwhm) ||
 
  103          t.first > (_fmax + 22.0 * hwhm)) {
 
  109      for (
const auto &g : _grid) {
 
  111        intensities.at(i++) += t.second * Lorentzian(g, t.first, hwhm);
 
  115    vector.push_back(intensities);
 
  119void PAHEmissionModel::getSpectraAndConvolveWithGaussianOfFHWM(
 
  120    std::vector<std::vector<double>> &vector, 
double fwhm) {
 
  124  vector.reserve(_transitions.size());
 
  126  double sigma = fwhm / 2.0 / sqrt(2.0 * log(2.0));
 
  128  for (
auto &transition : _transitions) {
 
  130    std::vector<double> intensities(_grid.size());
 
  132    for (
auto &t : transition) {
 
  134      if (t.second == 0 || t.first < (_fmin - 3.0 * sigma) ||
 
  135          t.first > (_fmax + 3.0 * sigma)) {
 
  141      for (
const auto &g : _grid) {
 
  143        intensities.at(i++) += t.second * Gaussian(g, t.first, sigma);
 
  147    vector.push_back(intensities);
 
  151void PAHEmissionModel::getSpectraAndConvolveWithDrudeOfFHWM(
 
  152    std::vector<std::vector<double>> &vector, 
double fwhm) {
 
  156  vector.reserve(_transitions.size());
 
  158  for (
auto &transition : _transitions) {
 
  160    std::vector<double> intensities(_grid.size());
 
  162    for (
auto &t : transition) {
 
  164      if (t.second == 0 || t.first < (_fmin - 8.0 * fwhm) ||
 
  165          t.first > (_fmax + 8.0 * fwhm)) {
 
  171      for (
const auto &g : _grid) {
 
  173        intensities.at(i++) += t.second * Drude(g, t.first, fwhm);
 
  177    vector.push_back(intensities);
 
  181void PAHEmissionModel::applyBlackbodyWithTemperature(
double temperature) {
 
  183  for (
auto &transition : _transitions) {
 
  185    for (
auto &t : transition) {
 
  188          1e5 * 4.0 * M_PI * Blackbody(t.first, temperature) / AvogadrosNumber;
 
  193void PAHEmissionModel::applyBlackbodyWithTemperatureForEach(
 
  194    const std::vector<double> &temperatures) {
 
  198  for (
auto &transition : _transitions) {
 
  200    for (
auto &t : transition) {
 
  202      t.second *= 1e5 * 4.0 * M_PI * Blackbody(t.first, temperatures[i]) /
 
  209void PAHEmissionModel::applyTemperatureWithEnergy(
 
  210    double energy, std::vector<double> &temperatures) {
 
  212  temperatures.clear();
 
  214  temperatures.reserve(_transitions.size());
 
  218  for (
auto &transition : _transitions) {
 
  222      _nc = 
static_cast<double>(_carbons.at(j++));
 
  225    temperatures.push_back(solveInitialTemperature(energy, transition));
 
  228  applyBlackbodyWithTemperatureForEach(temperatures);
 
  231void PAHEmissionModel::applyCascadeWithEnergy(
 
  232    double energy, std::vector<double> &temperatures) {
 
  234  temperatures.clear();
 
  236  temperatures.reserve(_transitions.size());
 
  238  double TemperatureMax;
 
  246  gsl_integration_workspace *w = gsl_integration_workspace_alloc(MaxSteps);
 
  252    F.function = PAHEmissionModel::featureStrength;
 
  255    F.function = PAHEmissionModel::approximateFeatureStrength;
 
  260  for (
auto &transition : _transitions) {
 
  262    std::vector<std::pair<double, double>> transitions(transition.cbegin(),
 
  269      F.params = &transitions;
 
  272      _nc = 
static_cast<double>(_carbons.at(j));
 
  274      factor = 2.48534271218563e-23 * _nc /
 
  276                   transitions.begin(), transitions.end(), 0.0,
 
  277                   [](
const auto &a, 
const auto &b) { return a + b.second; });
 
  279      F.params = &_charges.at(j++);
 
  282    TemperatureMax = solveInitialTemperature(energy, transitions);
 
  284    temperatures.push_back(TemperatureMax);
 
  286    for (
auto &t : transition) {
 
  288      if (t.second == 0.0) {
 
  293      PAHEmissionModel::_frequency = t.first;
 
  295      gsl_integration_qag(&F, TemperatureMin, TemperatureMax, 0,
 
  296                          IntegrationAccuracy, MaxSteps, GSL_INTEG_GAUSS61, w,
 
  299      t.second *= factor * pow(t.first, 3) * strength;
 
  303  gsl_integration_workspace_free(w);
 
  306double PAHEmissionModel::featureStrength(
double temperature,
 
  307                                         void *transitions_p) {
 
  309  std::vector<double> sum;
 
  311  std::vector<std::pair<double, double>> *transitions =
 
  312      static_cast<std::vector<std::pair<double, double>
> *>(transitions_p);
 
  314  sum.reserve(transitions->size());
 
  318  for (
auto &t : *transitions) {
 
  320    val = PlanckConstant * SpeedOfLight * t.first /
 
  321          (BoltzmannConstant * temperature);
 
  323    sum.push_back(t.second * pow(t.first, 3) / (exp(val) - 1.0));
 
  326  val = PlanckConstant * SpeedOfLight * _frequency /
 
  327        (BoltzmannConstant * temperature);
 
  329  return ((PAHEmissionModel::heatCapacity(temperature, transitions_p) /
 
  331          (1.0 / (std::accumulate(sum.begin(), sum.end(), 0.0))));
 
  334double PAHEmissionModel::approximateFeatureStrength(
double temperature,
 
  337  double a = 0.0, b = 0.0;
 
  339  if (*
static_cast<int *
>(charge) != 0) {
 
  340    if (temperature > 1000.0) {
 
  343    } 
else if (temperature > 300.0 && temperature <= 1000.0) {
 
  346    } 
else if (temperature > 100.0 && temperature <= 300.0) {
 
  349    } 
else if (temperature > 40.0 && temperature <= 100.0) {
 
  352    } 
else if (temperature > 20.0 && temperature <= 40.0) {
 
  355    } 
else if (temperature > 2.7 && temperature <= 20.0) {
 
  360    if (temperature > 270.0) {
 
  363    } 
else if (temperature > 200.0 && temperature <= 270.0) {
 
  366    } 
else if (temperature > 60.0 && temperature <= 200.0) {
 
  369    } 
else if (temperature > 30.0 && temperature <= 60.0) {
 
  372    } 
else if (temperature > 2.7 && temperature <= 30.0) {
 
  378  double val = 1.4387751297850830401 * _frequency / temperature;
 
  380  if (a <= 0.0 || val > log(DBL_MAX)) {
 
  385  return 1.0 / ((exp(val) - 1.0) * a * pow(temperature, b));
 
  388double PAHEmissionModel::solveInitialTemperature(
 
  389    double energy, std::vector<std::pair<double, double>> &transitions) {
 
  391  PAHEmissionModel::_energy = energy;
 
  393  size_t niterations = 0;
 
  397  const gsl_root_fsolver_type *T;
 
  401  double temperature = 0;
 
  407    F.function = PAHEmissionModel::solveInitialTemperatureFunc;
 
  409    F.params = &transitions;
 
  412    F.function = PAHEmissionModel::solveApproximateInitialTemperatureFunc;
 
  417  T = gsl_root_fsolver_brent;
 
  419  s = gsl_root_fsolver_alloc(T);
 
  421  double min = TemperatureMin;
 
  423  double max = TemperatureMax;
 
  425  gsl_root_fsolver_set(s, &F, min, max);
 
  431    status = gsl_root_fsolver_iterate(s);
 
  433    temperature = gsl_root_fsolver_root(s);
 
  435    min = gsl_root_fsolver_x_lower(s);
 
  437    max = gsl_root_fsolver_x_upper(s);
 
  439    status = gsl_root_test_interval(min, max, 0, RootAccuracy);
 
  440  } 
while (status == GSL_CONTINUE && niterations < MaxIterations);
 
  442  gsl_root_fsolver_free(s);
 
  444  return (temperature);
 
  447inline double PAHEmissionModel::solveInitialTemperatureFunc(
double temperature,
 
  449  return integralOverHeatCapacity(temperature, transitions) - _energy;
 
  453PAHEmissionModel::solveApproximateInitialTemperatureFunc(
double temperature,
 
  456  return *
static_cast<double *
>(nc) *
 
  457             (7.54267e-11 * erf(-4.989231 + 0.41778 * log(temperature)) +
 
  462double PAHEmissionModel::integralOverHeatCapacity(
double temperature,
 
  465  gsl_integration_workspace *w = gsl_integration_workspace_alloc(MaxSteps);
 
  473  F.function = PAHEmissionModel::heatCapacity;
 
  475  F.params = transitions;
 
  477  gsl_integration_qag(&F, TemperatureMin, temperature, 0, IntegrationAccuracy,
 
  478                      MaxSteps, GSL_INTEG_GAUSS61, w, &energy, &error);
 
  480  gsl_integration_workspace_free(w);
 
  485double PAHEmissionModel::heatCapacity(
double temperature, 
void *transitions_p) {
 
  487  double heatcapacity = 0.0;
 
  491  std::vector<std::pair<double, double>> *transitions =
 
  492      static_cast<std::vector<std::pair<double, double>
> *>(transitions_p);
 
  494  for (
const auto &t : *transitions) {
 
  496    value = PlanckConstant * SpeedOfLight * t.first /
 
  497            (BoltzmannConstant * temperature);
 
  500        BoltzmannConstant * exp(-value) * pow((value / (1.0 - exp(-value))), 2);
 
  503  return (heatcapacity);
 
  506void PAHEmissionModel::shiftTransitions(
double shift) {
 
  508  for (
auto &transition : _transitions) {
 
  510    for (
auto &t : transition) {
 
  517void PAHEmissionModel::convertFromFrequencyToWavelength(
 
  518    std::vector<double> &grid) {
 
  520  for (
auto &g : grid) {
 
  526void PAHEmissionModel::convertFromFrequencyToWavelength(
 
  527    std::array<double, 2> &grid) {
 
  529  for (
auto &g : grid) {
 
  536PAHEmissionModel::convertFromWavelengthToFrequency(std::vector<double> &grid) {
 
  538  convertFromFrequencyToWavelength(grid);
 
  541void PAHEmissionModel::convertFromFrequencyToWavelength(
 
  542    std::vector<std::vector<std::pair<double, double>>> &transitions_p) {
 
  544  for (
auto &transition : transitions_p) {
 
  546    for (
auto &t : transition) {
 
  548      t.first = 1e4 / t.first;
 
  553inline void PAHEmissionModel::convertFromWavelengthToFrequency(
 
  554    std::vector<std::vector<std::pair<double, double>>> &transitions) {
 
  555  convertFromFrequencyToWavelength(transitions);