194 optipar.wavelengths_nm.clear();
195 loadCoefficientSeries(tmp_ctx, xml_path,
"fluspect_optipar_nr", optipar.wavelengths_nm, optipar.
nr);
196 loadCoefficientSeries(tmp_ctx, xml_path,
"fluspect_optipar_Kab", optipar.wavelengths_nm, optipar.
Kab);
197 loadCoefficientSeries(tmp_ctx, xml_path,
"fluspect_optipar_Kca", optipar.wavelengths_nm, optipar.
Kca);
198 loadCoefficientSeries(tmp_ctx, xml_path,
"fluspect_optipar_KcaV", optipar.wavelengths_nm, optipar.
KcaV);
199 loadCoefficientSeries(tmp_ctx, xml_path,
"fluspect_optipar_KcaZ", optipar.wavelengths_nm, optipar.
KcaZ);
200 loadCoefficientSeries(tmp_ctx, xml_path,
"fluspect_optipar_Kdm", optipar.wavelengths_nm, optipar.
Kdm);
201 loadCoefficientSeries(tmp_ctx, xml_path,
"fluspect_optipar_Kw", optipar.wavelengths_nm, optipar.
Kw);
202 loadCoefficientSeries(tmp_ctx, xml_path,
"fluspect_optipar_Ks", optipar.wavelengths_nm, optipar.
Ks);
203 loadCoefficientSeries(tmp_ctx, xml_path,
"fluspect_optipar_Kant", optipar.wavelengths_nm, optipar.
Kant);
204 loadCoefficientSeries(tmp_ctx, xml_path,
"fluspect_optipar_Kp", optipar.wavelengths_nm, optipar.
Kp);
205 loadCoefficientSeries(tmp_ctx, xml_path,
"fluspect_optipar_Kcbc", optipar.wavelengths_nm, optipar.
Kcbc);
206 loadCoefficientSeries(tmp_ctx, xml_path,
"fluspect_optipar_phi", optipar.wavelengths_nm, optipar.
phi);
210 if (excitation_step_nm <= 0.f) {
213 const size_t nw = optipar.wavelengths_nm.size();
215 helios_runtime_error(
"ERROR (computeFluspectKernel): Optipar wavelength grid is empty or degenerate.");
221 std::vector<double> Kca(nw);
222 for (
size_t i = 0; i < nw; ++i) {
223 Kca[i] = (1.0 - biochem.
V2Z) * optipar.
KcaV[i] + biochem.
V2Z * optipar.
KcaZ[i];
228 std::vector<double> Kall(nw);
229 for (
size_t i = 0; i < nw; ++i) {
230 Kall[i] = (biochem.
Cab * optipar.
Kab[i] + biochem.
Cca * Kca[i] + biochem.
Cdm * optipar.
Kdm[i] + biochem.
Cw * optipar.
Kw[i] + biochem.
Cs * optipar.
Ks[i] + biochem.
Cant * optipar.
Kant[i] + biochem.
Cp * optipar.
Kp[i] +
231 biochem.
Cbc * optipar.
Kcbc[i]) /
237 std::vector<double> tau(nw, 1.0);
238 std::vector<double> kChlrel(nw, 0.0);
239 for (
size_t i = 0; i < nw; ++i) {
240 const double K = Kall[i];
242 tau[i] = (1.0 - K) * std::exp(-K) + K * K * expint(K);
243 kChlrel[i] = biochem.
Cab * optipar.
Kab[i] / (K * biochem.
N);
248 std::vector<double> talf(nw), ralf(nw), t12(nw), r12(nw), t21(nw), r21(nw);
249 for (
size_t i = 0; i < nw; ++i) {
250 talf[i] = calctav(59.0, optipar.
nr[i]);
251 ralf[i] = 1.0 - talf[i];
252 t12[i] = calctav(90.0, optipar.
nr[i]);
253 r12[i] = 1.0 - t12[i];
254 t21[i] = t12[i] / (optipar.
nr[i] * optipar.
nr[i]);
255 r21[i] = 1.0 - t21[i];
260 std::vector<double> refl(nw), tran(nw);
261 std::vector<double> r_mes(nw), t_mes(nw);
262 for (
size_t i = 0; i < nw; ++i) {
263 const double denom0 = 1.0 - r21[i] * r21[i] * tau[i] * tau[i];
264 const double Ta = talf[i] * tau[i] * t21[i] / denom0;
265 const double Ra = ralf[i] + r21[i] * tau[i] * Ta;
267 const double t_bot = t12[i] * tau[i] * t21[i] / denom0;
268 const double r_bot = r12[i] + r21[i] * tau[i] * t_bot;
273 double D = std::sqrt((1.0 + r + t) * (1.0 + r - t) * (1.0 - r + t) * (1.0 - r - t));
274 const double rq = r * r;
275 const double tq = t * t;
276 double a = (1.0 + rq - tq + D) / (2.0 * r);
277 double b = (1.0 - rq + tq + D) / (2.0 * t);
279 const double bNm1 = std::pow(b, biochem.
N - 1.0);
280 const double bN2 = bNm1 * bNm1;
281 const double a2 = a * a;
282 double denom1 = a2 * bN2 - 1.0;
283 double Rsub = a * (bN2 - 1.0) / denom1;
284 double Tsub = bNm1 * (a2 - 1.0) / denom1;
287 Tsub = t / (t + (1.0 - t) * (biochem.
N - 1.0));
291 const double denom2 = 1.0 - Rsub * r;
292 tran[i] = Ta * Tsub / denom2;
293 refl[i] = Ra + Ta * Rsub * t / denom2;
301 std::vector<double> rho_m(nw), tau_m(nw), Rb_full(nw);
302 for (
size_t i = 0; i < nw; ++i) {
303 const double Rb = (refl[i] - ralf[i]) / (talf[i] * t21[i] + (refl[i] - ralf[i]) * r21[i]);
304 const double Z = tran[i] * (1.0 - Rb * r21[i]) / (talf[i] * t21[i]);
305 double rho_val = (Rb - r21[i] * Z * Z) / (1.0 - (r21[i] * Z) * (r21[i] * Z));
306 double tau_val = (1.0 - Rb * r21[i]) / (1.0 - (r21[i] * Z) * (r21[i] * Z)) * Z;
307 if (rho_val < 0.0) rho_val = 0.0;
313 Rb_full[i] = rho_val + tau_val * tau_val * r21[i] / (1.0 - rho_val * r21[i]);
317 std::vector<double> s(nw), k_km(nw), kChl(nw);
318 for (
size_t i = 0; i < nw; ++i) {
319 const double r = r_mes[i];
320 const double t = t_mes[i];
324 const double D = std::sqrt((1.0 + r + t) * (1.0 + r - t) * (1.0 - r + t) * (1.0 - r - t));
325 a = (1.0 + r * r - t * t + D) / (2.0 * r);
326 b = (1.0 - r * r + t * t + D) / (2.0 * t);
328 double s_val = r / t;
329 double k_val = std::log(b);
330 if (a > 1.0 && std::isfinite(a)) {
331 s_val = 2.0 * a / (a * a - 1.0) * std::log(b);
332 k_val = (a - 1.0) / (a + 1.0) * std::log(b);
336 kChl[i] = kChlrel[i] * k_val;
342 out.
refl.assign(refl.begin(), refl.end());
343 out.
tran.assign(tran.begin(), tran.end());
346 if (biochem.
fqe <= 0.f) {
352 const double int_step = excitation_step_nm;
353 std::vector<float> wle;
354 for (
double w = 400.0; w <= 750.0 + 1e-9; w += int_step) {
355 wle.push_back(
static_cast<float>(w));
358 std::vector<float> wlf;
359 for (
double w = 640.0; w <= 850.0 - 1e-9; w += 4.0) {
360 wlf.push_back(
static_cast<float>(w));
362 const size_t n_wle = wle.size();
363 const size_t n_wlf = wlf.size();
369 auto k_iwle =
interp1(optipar.wavelengths_nm, k_km, wle);
370 auto s_iwle =
interp1(optipar.wavelengths_nm, s, wle);
371 auto kChl_iwle =
interp1(optipar.wavelengths_nm, kChl, wle);
372 auto r21_iwle =
interp1(optipar.wavelengths_nm, r21, wle);
373 auto rho_iwle =
interp1(optipar.wavelengths_nm, rho_m, wle);
374 auto tau_iwle =
interp1(optipar.wavelengths_nm, tau_m, wle);
375 auto talf_iwle =
interp1(optipar.wavelengths_nm, talf, wle);
376 auto Rb_iwle =
interp1(optipar.wavelengths_nm, Rb_full, wle);
381 std::vector<size_t> Iwlf(n_wlf);
383 for (
size_t i = 0; i < n_wlf; ++i) {
384 const float target = wlf[i];
386 for (
size_t j = guess; j < optipar.wavelengths_nm.size(); ++j) {
387 if (std::abs(optipar.wavelengths_nm[j] - target) < 0.5f) {
395 helios_runtime_error(
"ERROR (computeFluspectKernel): emission wavelength " + std::to_string(target) +
" nm not present in Optipar grid.");
400 std::vector<double> k_wlf(n_wlf), s_wlf(n_wlf), phi_wlf(n_wlf), r21_wlf(n_wlf), t21_wlf(n_wlf), tau_wlf(n_wlf), rho_wlf(n_wlf), Rb_wlf(n_wlf);
401 for (
size_t i = 0; i < n_wlf; ++i) {
402 const size_t j = Iwlf[i];
405 phi_wlf[i] = optipar.
phi[j];
408 tau_wlf[i] = tau_m[j];
409 rho_wlf[i] = rho_m[j];
410 Rb_wlf[i] = Rb_full[j];
416 const double eps_step = std::ldexp(1.0, -ndub);
418 std::vector<double> te(n_wle), re(n_wle);
419 for (
size_t j = 0; j < n_wle; ++j) {
420 te[j] = 1.0 - (k_iwle[j] + s_iwle[j]) * eps_step;
421 re[j] = s_iwle[j] * eps_step;
423 std::vector<double> tf(n_wlf), rf(n_wlf);
424 for (
size_t i = 0; i < n_wlf; ++i) {
425 tf[i] = 1.0 - (k_wlf[i] + s_wlf[i]) * eps_step;
426 rf[i] = s_wlf[i] * eps_step;
431 std::vector<std::vector<double>> sigmoid(n_wlf, std::vector<double>(n_wle));
432 for (
size_t i = 0; i < n_wlf; ++i) {
433 const double em_factor = std::exp(-
static_cast<double>(wlf[i]) / 10.0);
434 for (
size_t j = 0; j < n_wle; ++j) {
435 const double ex_factor = std::exp(
static_cast<double>(wle[j]) / 10.0);
436 sigmoid[i][j] = 1.0 / (1.0 + em_factor * ex_factor);
442 std::vector<std::vector<double>> Mf(n_wlf, std::vector<double>(n_wle));
443 std::vector<std::vector<double>> Mb(n_wlf, std::vector<double>(n_wle));
444 const double init_scale = int_step * biochem.
fqe * 0.5 * eps_step;
445 for (
size_t i = 0; i < n_wlf; ++i) {
446 for (
size_t j = 0; j < n_wle; ++j) {
447 const double v = init_scale * phi_wlf[i] * kChl_iwle[j] * sigmoid[i][j];
454 std::vector<std::vector<double>> Mfn(n_wlf, std::vector<double>(n_wle));
455 std::vector<std::vector<double>> Mbn(n_wlf, std::vector<double>(n_wle));
456 for (
int iter = 0; iter < ndub; ++iter) {
458 std::vector<double> xe(n_wle), ten(n_wle), ren(n_wle);
459 for (
size_t j = 0; j < n_wle; ++j) {
460 xe[j] = te[j] / (1.0 - re[j] * re[j]);
461 ten[j] = te[j] * xe[j];
462 ren[j] = re[j] * (1.0 + ten[j]);
465 std::vector<double> xf(n_wlf), tfn(n_wlf), rfn(n_wlf);
466 for (
size_t i = 0; i < n_wlf; ++i) {
467 xf[i] = tf[i] / (1.0 - rf[i] * rf[i]);
468 tfn[i] = tf[i] * xf[i];
469 rfn[i] = rf[i] * (1.0 + tfn[i]);
477 for (
size_t i = 0; i < n_wlf; ++i) {
478 for (
size_t j = 0; j < n_wle; ++j) {
479 const double A11 = xf[i] + xe[j];
480 const double A12 = xf[i] * xe[j] * (rf[i] + re[j]);
481 const double A21 = 1.0 + xf[i] * xe[j] * (1.0 + rf[i] * re[j]);
482 const double A22 = xf[i] * rf[i] + xe[j] * re[j];
483 Mfn[i][j] = Mf[i][j] * A11 + Mb[i][j] * A12;
484 Mbn[i][j] = Mb[i][j] * A21 + Mf[i][j] * A22;
506 std::vector<double> xe_vec(n_wle), ye_vec(n_wle);
507 for (
size_t j = 0; j < n_wle; ++j) {
508 xe_vec[j] = talf_iwle[j] / (1.0 - r21_iwle[j] * Rb_iwle[j]);
509 ye_vec[j] = tau_iwle[j] * r21_iwle[j] / (1.0 - rho_iwle[j] * r21_iwle[j]);
511 std::vector<double> xf_vec(n_wlf), yf_vec(n_wlf);
512 for (
size_t i = 0; i < n_wlf; ++i) {
513 xf_vec[i] = t21_wlf[i] / (1.0 - r21_wlf[i] * Rb_wlf[i]);
514 yf_vec[i] = tau_wlf[i] * r21_wlf[i] / (1.0 - rho_wlf[i] * r21_wlf[i]);
517 out.
Mf.assign(n_wlf, std::vector<float>(n_wle));
518 out.
Mb.assign(n_wlf, std::vector<float>(n_wle));
519 for (
size_t i = 0; i < n_wlf; ++i) {
520 for (
size_t j = 0; j < n_wle; ++j) {
521 const double A = xe_vec[j] * (1.0 + ye_vec[j] * yf_vec[i]) * xf_vec[i];
522 const double B = xe_vec[j] * (ye_vec[j] + yf_vec[i]) * xf_vec[i];
523 const double g = Mb[i][j];
524 const double f = Mf[i][j];
525 const double gn = A * g + B * f;
526 const double fn = A * f + B * g;
527 out.
Mb[i][j] =
static_cast<float>(gn);
528 out.
Mf[i][j] =
static_cast<float>(fn);