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);