49 #include "estar_dataTables.h"
52 #if defined(WIN32) || defined(_WIN32)
54 # define ESTAR_EXPORT __declspec(dllexport)
56 # define ESTAR_EXPORT __declspec(dllimport)
78 struct GetElementsStruct {
80 string elemArrayStrut[100];
82 void tokenize(std::string
const &str,
const char delim,
83 std::vector<std::string> &out) {
85 std::stringstream ss(str);
88 while (std::getline(ss, s, delim)) {
102 void getElemArray(
char *formulaStr,
int NEP, GetElementsStruct &GElem) {
103 const char delim =
' ';
104 std::vector<std::string> components;
105 tokenize(
string(formulaStr), delim, components);
107 if (components.size() < (
size_t)NEP) {
108 egsFatal(
"\nestar::getElemArray: List of elements is inconsistent with the number expected. Expected %d elements but only found %d.\n", NEP, components.size());
113 if (components[k].size() > 1) {
114 components[k][0] =
static_cast<char>(std::toupper(
static_cast<unsigned char>(components[k][0])));
115 components[k][1] =
static_cast<char>(std::tolower(
static_cast<unsigned char>(components[k][1])));
117 GElem.elemArrayStrut[k] = components[k];
176 #define egsEstar F77_OBJ_(egs_estar,EGS_ESTAR)
178 extern __extc__ ESTAR_EXPORT
int egsEstar(
char *formulaStr,
194 egsFatal(
"estar::egsEstar: formulaStr pointer is null for medium.\n");
197 egsFatal(
"estar::egsEstar: massFraction pointer is null.\n");
200 egsFatal(
"estar::egsEstar: numOfAtoms pointer is null.\n");
203 egsFatal(
"estar::egsEstar: mediaDensity pointer is null.\n");
206 egsFatal(
"estar::egsEstar: densityCorr pointer is null.\n");
209 egsFatal(
"estar::egsEstar: enGrid pointer is null.\n");
212 egsFatal(
"estar::egsEstar: NEP pointer is null.\n");
215 egsFatal(
"estar::egsEstar: ISCOMP pointer is null.\n");
218 egsFatal(
"estar::egsEstar: meanIval pointer is null.\n");
221 egsFatal(
"estar::egsEstar: ipotval pointer is null.\n");
224 egsFatal(
"estar::egsEstar: mediaID pointer is null.\n");
226 if (!outputFilename) {
227 egsFatal(
"estar::egsEstar: outputFilename pointer is null.\n");
231 if (*NEP <= 0 || *NEP > 100) {
232 egsFatal(
"estar::egsEstar: NEP=%d is out of valid range [1, 100].\n", *NEP);
235 egsFatal(
"estar::egsEstar: mediaID=%d is invalid, must be > 0.\n", *mediaID);
237 if (*ISCOMP != 0 && *ISCOMP != 1) {
238 egsFatal(
"estar::egsEstar: ISCOMP=%d is invalid, must be 0 or 1.\n", *ISCOMP);
240 if (*mediaDensity <= 0.0f) {
241 egsFatal(
"estar::egsEstar: mediaDensity=%g is invalid, must be > 0.\n",
246 "== MEDIUM %d BLOCK FOR ESTAR ==\n", *mediaID);
249 GetElements elemObject;
250 GetElements::GetElementsStruct GeElems;
252 int isCompInt = *ISCOMP;
253 int mediaNum = *mediaID;
255 elemObject.getElemArray(formulaStr, nepInt, GeElems);
259 vector<string> estarFormulaArrayInput(nepInt);
260 vector<double> estarWeightArrayInput(nepInt);
264 estarFormulaArrayInput[i] = GeElems.elemArrayStrut[i];
265 estarWeightArrayInput[i] = massFraction[i];
267 egsInformation(
"estar::egsEstar: Formula is %s with fraction %g\n",
268 estarFormulaArrayInput[i].c_str(), estarWeightArrayInput[i]);
273 double mediumDensity = *mediaDensity;
279 estarFormulaArrayInput.data(),
280 estarWeightArrayInput.data(),
281 numOfAtoms, densityCorr, enGrid,
282 meanIval, ipotval, mediaNum,
283 string(outputFilename));
285 egsFatal(
"estar::egsEstar: estarCalculation failed for medium %d with error code %d.\n",
295 #define egsCompoundsToElements F77_OBJ_(egs_compoundstoelements,EGS_COMPOUNDSTOELEMENTS)
297 extern __extc__ ESTAR_EXPORT
int egsCompoundsToElements(
char *formulaStr,
298 double *massFraction,
309 egsFatal(
"estar::egsCompoundsToElements: formulaStr pointer is null.\n");
312 egsFatal(
"estar::egsCompoundsToElements: massFraction pointer is null.\n");
315 egsFatal(
"estar::egsCompoundsToElements: mediaDensity pointer is null.\n");
318 egsFatal(
"estar::egsCompoundsToElements: elementStr pointer is null.\n");
321 egsFatal(
"estar::egsCompoundsToElements: rhoz pointer is null.\n");
324 egsFatal(
"estar::egsCompoundsToElements: zelem pointer is null.\n");
327 egsFatal(
"estar::egsCompoundsToElements: ncomp pointer is null.\n");
330 egsFatal(
"estar::egsCompoundsToElements: NEP pointer is null.\n");
334 if (*ncomp <= 0 || *ncomp > 100) {
335 egsFatal(
"estar::egsCompoundsToElements: ncomp=%d is out of valid range "
336 "[1, 100].\n", *ncomp);
338 if (*mediaDensity <= 0) {
339 egsFatal(
"estar::egsCompoundsToElements: mediaDensity=%f must be > 0.\n", *mediaDensity);
344 const size_t MAX_ELEMENT_STR_SIZE = 50 * 50;
346 GetElements elemObject;
347 GetElements::GetElementsStruct GeElems;
348 int numCompounds = *ncomp;
350 elemObject.getElemArray(formulaStr, numCompounds, GeElems);
354 vector<string> estarFormulaArrayInput(numCompounds);
355 vector<double> estarWeightArrayInput(numCompounds);
357 for (
size_t i=0; i < (size_t)numCompounds; ++i) {
358 estarFormulaArrayInput[i] = GeElems.elemArrayStrut[i];
359 estarWeightArrayInput[i] = massFraction[i];
362 float rho = *mediaDensity;
364 estarFormulaArrayInput.data(),
365 estarWeightArrayInput.data(),
373 egsFatal(
"estar::egsCompoundsToElements: medium has %d distinct elements, "
374 "but EGSnrc supports at most 50 ($MXEL).\n", fc.mmax);
378 for (
size_t i=0; i != (size_t)fc.mmax; ++i) {
382 for (
auto it = atomic_number.begin(); it != atomic_number.end(); ++it) {
383 if (it->second == fc.jz[i]) {
384 if (charPos >= MAX_ELEMENT_STR_SIZE) {
385 egsFatal(
"estar::egsCompoundsToElements: elementStr buffer overflow at "
386 "charPos=%zu. Buffer may be too small.\n", charPos);
390 for (
auto &ch : it->first) {
391 elementStr[charPos++] = ch;
395 for (
auto j=it->first.length(); j < 50; j++) {
396 elementStr[charPos++] =
' ';
411 string getFileNameWithoutExtension(
const string &s) {
417 size_t i = s.rfind(sep, s.length());
418 if (i != string::npos) {
419 string filename = s.substr(i+1, s.length() - i);
420 size_t lastindex = filename.find_last_of(
".");
421 string rawname = filename.substr(0, lastindex);
429 size_t firstSpace = outputFilename.find_first_of(
" \n\r\t");
430 if (firstSpace != string::npos) {
431 outputFilename = outputFilename.erase(firstSpace);
433 if (outputFilename.empty()) {
437 egsInformation(
"estar::outputDensityFile: Writing density correction file '%s'.\n",
438 outputFilename.c_str());
440 std::ofstream f(outputFilename.c_str());
443 egsFatal(
"estar::outputDensityFile: Could not open output file '%s'.\n",
444 outputFilename.c_str());
448 f << getFileNameWithoutExtension(outputFilename).c_str() << endl;
449 f << setprecision(8);
450 f <<
"113 " << *meanIval <<
" " << mediaDensity <<
" " << fc.mmax << endl;
453 for (
int k=0; k < fc.mmax; ++k) {
454 f << fc.jz[k] <<
" " << fc.wt[k];
457 if ((k+1) % 6 == 0 || k+1 == fc.mmax) {
466 f << scientific << showpoint;
469 for (
int i = 0; i < 113; i++) {
470 f << setprecision(2);
472 f << setprecision(3);
473 f <<
"," << densityCorr[i];
476 if ((i+1) % 4 == 0 || i+1 == 113) {
492 int estarCalculation(
int isCompound,
int NEP,
float mediaDensity,
string *elementArray,
double *massFraction,
493 float *numOfAtoms,
double *densityCorr,
double *enGrid,
float *meanIval,
float *ipotval,
int mediaNum,
string outputFilename) {
497 if (isCompound == 1) {
513 egsFatal(
"estar::estarCalculation: Density must be greater than 0, got %g "
514 "for medium %d.\n", rho, mediaNum);
520 fc = getDataFromFormulae(knmat, rho, elementArray, massFraction, numOfAtoms, NEP, mediaNum);
525 egsInformation(
"\nestar::estarCalculation: For medium %d I-value (eV) given "
526 "in egsinp file is %g.\n", mediaNum, fc.pot);
529 egsInformation(
"\nestar::estarCalculation: For medium %d I-value (eV) not "
530 "provided in egsinp file. I-value calculated by ESTAR is %g.\n",
543 long double qfac = exp(log(10)/50);
547 vector<long double> q(lmax);
556 for (
int i = 1; i < lmax; i++) {
571 if (mmax <= 0 || mmax > 50) {
572 egsFatal(
"estar::estarCalculation: mmax=%d is out of valid range [1, 50] "
573 "for medium %d.\n", mmax, mediaNum);
583 const int ATB_SIZE =
sizeof(atb) /
sizeof(atb[0]);
585 for (
int i = 0; i < mmax; i++) {
589 if (jz <= 0 || jz > ATB_SIZE) {
590 egsFatal(
"estar::estarCalculation: Atomic number Z=%d at index %d is out of "
591 "valid range [1, %d] for medium %d.\n", jz, i, ATB_SIZE, mediaNum);
602 egsFatal(
"estar::estarCalculation: Atomic mass is zero for Z=%d at index %d "
603 "for medium %d. Check atb table entry.\n", jz, i, mediaNum);
607 g[i] = fc.wt[i]*(z/a);
615 egsFatal(
"estar::estarCalculation: zav=%g is non-positive for medium %d. "
616 "Cannot compute hom.\n", zav, mediaNum);
620 const double plasmaFeqCoeff = 28.81593;
621 double hom = plasmaFeqCoeff*sqrt(rho*zav);
622 double phil = 2.0*log(fc.pot/hom);
625 for (
int i = 0; i < mmax; i++) {
629 egsFatal(
"estar::estarCalculation: zav is zero when normalising g[%d] "
630 "for medium %d.\n", i, mediaNum);
642 vector<double> f(1000);
643 vector<double> en(1000);
649 for (
int m = 0; m < mmax; m++) {
652 nmax = ds.
nmax[record];
654 if (iz == 6 && mmax==1) {
655 ds.
nc[record][nmax -1] = 1;
656 ds.
nc[record][nmax] = -1;
657 ds.
bd[record][nmax] = ds.
bd[record][nmax-1];
661 if (ds.
nc[record][nmax-1] <0) {
668 ds.
nc[record][nmax-1] = - ds.
nc[record][nmax-1];
675 ds.
bd[record][nmax-1] = 0.0;
680 for (
int p = 0; p < nmax; p++) {
683 nsum = nsum + ds.
nc[record][p];
688 if (nbas + nmax > 1000) {
689 egsFatal(
"estar::estarCalculation: Total oscillator count nbas+nmax=%d "
690 "exceeds maximum of 1000 at element index %d for medium %d.\n",
691 nbas+nmax, m, mediaNum);
697 egsFatal(
"estar::estarCalculation: Electron sum is zero for element index %d "
698 "(Z=%d) in medium %d. Cannot compute oscillator strengths.\n",
711 for (
int n = 0; n < nmax; n++) {
713 f[nn] = ds.
nc[record][n]*g[m]/sum;
714 en[nn] = ds.
bd[record][n];
728 vector<double> alf(1000);
729 vector<double> eps(1000);
730 for (
int n = 0; n < nmax; n++) {
739 for (
int n = 0; n < nmax; n++) {
740 eps[n] = (en[n]/hom) * (en[n]/hom);
760 const int MAX_NEWTON_ITER = 1000;
763 while (std::abs(droot)-0.00001 > 0) {
766 for (
int n = 0; n < nmax; n++) {
767 trm = root*eps[n] + alf[n]*f[n];
771 egsFatal(
"estar::estarCalculation: Non-positive trm=%g at oscillator n=%d "
772 "during Newton's method for medium %d. log and division are undefined.\n",
777 fun = fun +f[n]*log(trm);
778 der = der + f[n]*eps[n]/trm;
783 egsFatal(
"estar::estarCalculation: Zero derivative in Newton's method at "
784 "iteration %d (root=%g) for medium %d. Cannot continue.\n",
785 newtonIter, root, mediaNum);
793 if (++newtonIter >= MAX_NEWTON_ITER) {
794 egsFatal(
"estar::estarCalculation: Newton's method failed to converge after "
795 "%d iterations for medium %d.\n", MAX_NEWTON_ITER, mediaNum);
804 egsFatal(
"estar::estarCalculation: Newton's method converged to negative "
805 "root=%g for medium %d. Cannot compute adjustment factor.\n",
813 for (
int n = 0; n < nmax; n++) {
814 eps[n] = root*eps[n];
826 vector<double> yq(lmax);
827 vector<double> yql(lmax);
828 vector<double> d(lmax);
831 for (
int n = 0; n < lmax; n++) {
833 for (
int m = 0; m < nmax; m++) {
834 double denom = eps[m] + q[n];
836 egsFatal(
"estar::estarCalculation: eps[%d] + q[%d] is zero for medium %d.\n"
837 "Check oscillator energies and q grid.\n", m, n, mediaNum);
839 sum = sum + f[m]/denom;
844 egsFatal(
"estar::estarCalculation: Zero sum when computing yq[%d] for "
845 "medium %d. Cannot compute 1/sum.\n", n, mediaNum);
852 egsFatal(
"estar::estarCalculation: yq[%d]=%g is non-positive for medium %d. "
853 "Cannot compute log.\n", n, yq[n], mediaNum);
858 for (
int m = 0; m < nmax; m++) {
859 double denom = eps[m] + alf[m] * f[m];
863 egsFatal(
"estar::estarCalculation: Zero denominator in arg calculation at "
864 "n=%d, m=%d for medium %d.\n", n, m, mediaNum);
868 arg = 1 + q[n]/denom;
869 sum = sum + f[m]*log(arg);
871 d[n] = sum - q[n]/(yq[n] + 1.0);
877 double rmass = 0.510999906;
891 vector<double> dlt(lkmax);
892 double tol = 0.000000001;
902 for (
int i = 0; i < lkmax; i++) {
903 tau = energy_grid[i]/rmass;
910 if (y-yq[lmax-1] <= 0) {
912 bp =
fbspol(yl, yql, sf2.
a, sf2.
b, sf2.
c, sf2.
d);
916 else if (solver == 2) {
920 for (
int m = 0; m < nmax; m++) {
921 double denom = eps[m] + alf[m] * f[m];
925 egsFatal(
"estar::estarCalculation: Zero denominator in bisection arg at "
926 "energy index %d, m=%d for medium %d.\n", i, m, mediaNum);
930 arg = 1 + xroot/denom;
931 sum = sum + f[m]*log(arg);
932 yqn = yqn + f[m]/(eps[m] + xroot);
937 egsFatal(
"estar::estarCalculation: yqn is zero at energy index %d for "
938 "medium %d. Cannot compute 1/yqn.\n", i, mediaNum);
943 nb_density = sum - xroot/(yqn + 1.0);
947 egsFatal(
"estar::estarCalculation: Invalid solver option %d for medium %d. "
948 "Must be 1 or 2.\n", solver, mediaNum);
954 egsFatal(
"estar::estarCalculation: Energy energy_grid[%d]=%g is too high and out of "
955 "range for medium %d.\n", i, energy_grid[i], mediaNum);
964 for (
int i = 0; i < lkmax; i++) {
966 densityCorr[i] = dlt[i];
967 enGrid[i] = energy_grid[i];
970 egsInformation(
"\nestar::estarCalculation: Density correction factors have been "
971 "calculated by ESTAR for medium %d.\n", mediaNum);
974 outputDensityFile(mediaDensity, densityCorr, enGrid, meanIval, fc, outputFilename);
980 bspol fbspol(
double s,
const std::vector<double> &x,
const std::vector<double> &a,
981 const std::vector<double> &b,
const std::vector<double> &c,
982 const std::vector<double> &d) {
983 int n =
static_cast<int>(x.size());
996 egsFatal(
"estar::fbspol: n=%d is too small, need at least 2 points.\n", n);
999 if (x[0] <= x[n-1]) {
1010 if (s > x[mub+idir-1]) {
1011 egsWarning(
"estar::fbspol: s=%g is beyond the upper grid boundary %g. "
1012 "Extrapolating using end spline segment — result may be unreliable.\n",
1014 mu = mub + 2*idir - 1;
1016 else if (s < x[mlb+1-idir-1]) {
1017 egsWarning(
"estar::fbspol: s=%g is below the lower grid boundary %g. "
1018 "Extrapolating using end spline segment — result may be unreliable.\n",
1019 s, x[mlb+1-idir-1]);
1020 mu = mlb - 2*idir + 1;
1027 mav = (ml + mu) / 2;
1035 while (std::abs(mu - ml) > 1);
1052 g = ((d[mu-1]*k+c[mu-1])*k+b[mu-1])*k+a[mu-1];
1062 scof fscof(
int nmax,
const vector<double> &x,
const vector<double> &f) {
1065 egsFatal(
"estar::fscof: fscof requires at least 2 points, got nmax=%d\n", nmax);
1073 for (
int m = 0; m < m2; m++) {
1074 sf.
d[m] = x[m+1] - x[m];
1075 if (sf.
d[m] == 0.0) {
1076 egsFatal(
"estar::fscof: Zero interval at m=%d. "
1077 "Knot positions x[%d] and x[%d] may be identical.\n", m, m, m+1);
1079 r = (f[m+1] - f[m])/sf.
d[m];
1089 for (
int m = 1; m < m2; m++) {
1090 sf.
c[m] = sf.
c[m] + r*sf.
c[m-1];
1091 sf.
b[m] = (x[m-1] - x[m+1])*2 - r*s;
1092 if (sf.
b[m] == 0.0) {
1093 egsFatal(
"estar::fscof: Zero pivot sf.b[%d] in spline solve. "
1094 "Knot data may be degenerate.\n", m);
1102 for (
int m = 1; m < m2; m++) {
1103 sf.
c[mr] = (sf.
d[mr] * sf.
c[mr+1] - sf.
c[mr])/sf.
b[mr];
1107 for (
int m = 0; m < m2; m++) {
1109 r = sf.
c[m+1] - sf.
c[m];
1111 sf.
c[m] = sf.
c[m]*3.0;
1112 sf.
b[m] = (f[m+1]-f[m])/s - (sf.
c[m]+r)*s;
1120 const vector<double> &eps,
int nmax,
double x) {
1121 double arg = tau * (tau + 2.0);
1123 egsFatal(
"estar::objective_function: tau*(tau+2)=%g is non-positive "
1124 "for tau=%g. Cannot compute log.\n", arg, tau);
1126 double yl = log(arg);
1129 for (
int i = 0; i < nmax; i++) {
1130 double denom = eps[i] + x;
1132 egsFatal(
"estar::objective_function: eps[%d]=%g and x=%g sum to "
1133 "zero. Cannot divide. Check oscillator energies and "
1134 "bisection bounds.\n", i, eps[i], x);
1136 yql = yql + f[i]/denom;
1141 egsFatal(
"estar::objective_function: yql is non-positive at x=%g. "
1142 "Cannot compute log.\n", x);
1144 return yl - log(1.0/yql);
1148 double bisec(
double lowerbound,
double upperbound,
double tolerance,
1149 double tau,
const vector<double> &f,
const vector<double> &eps,
1151 if (tolerance <= 0) {
1152 egsFatal(
"estar::bisec: bisection tolerance must be positive, got %g\n", tolerance);
1154 if (nmax >
static_cast<int>(f.size()) || nmax >
static_cast<int>(eps.size())) {
1155 egsFatal(
"estar::bisec: nmax=%d exceeds vector size.\n", nmax);
1161 if ((fLower > 0) == (fUpper > 0)) {
1162 egsFatal(
"estar::bisec: Bounds [%g, %g] do not bracket a root for tau=%g.\n",
1163 lowerbound, upperbound, tau);
1167 double x_mid = lowerbound;
1169 while (del > tolerance) {
1170 x_mid = (lowerbound + upperbound) / 2.0;
1172 if ((fMid > 0) == (fLower > 0)) {
1179 del = std::abs(fMid);
Global egspp functions header file.
Core ESTAR density correction calculation interface.
bspol fbspol(double s, const std::vector< double > &x, const std::vector< double > &a, const std::vector< double > &b, const std::vector< double > &c, const std::vector< double > &d)
Evaluate the cubic spline interpolant at a given point.
double objective_function(double tau, const vector< double > &f, const vector< double > &eps, int nmax, double x)
Objective function for the bisection solver.
scof fscof(int nmax, const vector< double > &x, const vector< double > &f)
Compute natural cubic spline coefficients.
int estarCalculation(int isCompound, int NEP, float mediaDensity, std::string *elementArray, double *massFraction, float *numOfAtoms, double *densityCorr, double *enGrid, float *meanIval, float *ipotval, int mediaNum, std::string outputFilename)
Compute density correction factors for a medium.
double bisec(double lowerbound, double upperbound, double tolerance, double tau, const std::vector< double > &f, const std::vector< double > &eps, int nmax)
Find the root of objective_function() by bisection.
void outputDensityFile(float mediaDensity, double *densityCorr, double *enGrid, float *meanIval, formula_calc fc, std::string outputFilename)
Output a density correction file.
EGS_InfoFunction EGS_EXPORT egsInformation
Always use this function for reporting the progress of a simulation and any other type of information...
EGS_InfoFunction EGS_EXPORT egsFatal
Always use this function for reporting fatal errors.
EGS_InfoFunction EGS_EXPORT egsWarning
Always use this function for reporting warnings.
Holds the dispersion oscillator data for all 100 elements.
int nc[100][26]
Number of electrons in each subshell for each element.
double bd[100][26]
Absorption edge energies (in units of hbar) for each oscillator.
int nmax[100]
Number of dispersion oscillators for each element.
Result struct returned by fbspol().
Spline coefficient struct returned by fscof().