21 #include <gsl/gsl_randist.h> 22 #include <gsl/gsl_rng.h> 23 #include <gsl/gsl_statistics_double.h> 26 #include <ceres/ceres.h> 27 #include <glog/logging.h> 93 template <
unsigned NB,
unsigned NP>
94 class Fcost_gray :
public ceres::SizedCostFunction<NB,NP> {
97 static constexpr
double scale = 128.0;
103 virtual bool Evaluate(
double const*
const* parameters,
double* residuals,
104 double** jacobians)
const final {
105 const char *
const fn =
"Fcost_gray";
106 const double *p = parameters[0];
107 double *jac = (jacobians && jacobians[0] ? jacobians[0] :
nullptr);
110 if (!residuals) {
clearCache();
return true; }
120 constexpr
unsigned P0 = NP&1;
124 Sh = p[1-P0] * rescale,
125 Tc = (NP > 2 ? p[2-P0] : 0.0),
126 Sc = (NP > 2 ? p[3-P0] : 0.0) * rescale;
128 MU_DEBUG(fn,
"Th = %7.6f, Sh = %9.8f, Tc = %7.6f, Sc = %9.8f%s",
129 Th, Sh, Tc, Sc, jac ?
" *" :
"");
134 for (
unsigned i=0; i != NB; i++) {
136 double w = iFerr[i], F = (NP < 3 ? gray.F(Th, Sh, Di) :
137 gray.F(Tc, Sc, Th, Sh, Di));
138 residuals[i] = w*(F - Fobs[i]);
143 jac[NP*i+0] = w*gray.dF_dT(Th, Sh, Di);
145 jac[NP*i+1-P0] = w*gray.dF_dS(Th, Sh, Di)*rescale;
148 jac[NP*i+0] = w*gray.dF_dTh(Tc, Sc, Th, Sh, Di);
150 jac[NP*i+1-P0] = w*gray.dF_dSh(Tc, Sc, Th, Sh, Di)*rescale;
151 jac[NP*i+2-P0] = w*gray.dF_dTc(Tc, Sc, Th, Sh, Di);
152 jac[NP*i+3-P0] = w*gray.dF_dSc(Tc, Sc, Th, Sh, Di)*rescale;
187 std::string rtn = asctime_r(localtime_r(&t, &tmbuf), buf);
210 const GrayData &gdata,
unsigned nParam)
212 const auto &gray = *gdata.
gray;
214 const double in1 = 1.0/(n-1);
219 Tc = 6.0*(1.0 + (j%n)*in1),
220 Sc =
mu_g*3e21*pow(100.0, (j/n)*in1),
221 Th = 15.0 + 3.0*(i%n)*in1,
222 Sh =
mu_g*3e19*pow(100.0, (i/n)*in1);
224 "(%lu, %lu) Tc = %.2f, Sc = %.2e, Th = %.2f, Sh = %.2e",
225 i, j, Tc, Sc, Th, Sh);
227 for (
unsigned iBand=0; iBand != bandData.
size(); iBand++) {
228 auto &data = bandData[iBand], &err = bandError[iBand];
229 auto Di = gdata.
band[iBand];
230 data[iMap] = (nParam == 2 ? gray.F(Th, Sh, Di) :
231 gray.F(Tc, Sc, Th, Sh, Di)) /
233 err[iMap] = 0.001*data[iMap];
234 MU_DEBUG(
"makeModelMap",
" [%lu] %.4e", iBand, data[iMap]);
255 double gridSample(
double data,
double err,
unsigned iBand,
unsigned iCycle,
258 if (nSamp == 2) {
return data + ((iCycle>>iBand)&1 ? -err : +err); }
261 for (
unsigned i=iBand; i != 0; i--) { div *= nSamp; }
263 int digit = (iCycle/div) % nSamp;
264 double half = 0.5*(nSamp-1);
265 return data + (digit/half-1.0)*err;
286 double sysSample(
double data,
unsigned iBand,
unsigned iCycle,
292 for (
unsigned i=0; i != instr.
size(); i++) {
293 if (! bands[iBand]->name().compare(0, instr[i].size(), instr[i])) {
301 unsigned cBit = (iCycle>>(iInstr+bands.
size()))&1, uBit = (iCycle>>iBand)&1;
302 auto sErr = bands[iBand]->sysError();
303 double cErr = (cBit ? -sErr.first : +sErr.first),
304 uErr = (uBit ? -sErr.second : +sErr.second);
305 MU_DEBUG(
"sysSample",
"%lu[%lu] %lu/%g %lu/%g",
306 iCycle, iBand, cBit, cErr, uBit, uErr);
307 return ((cBit && !cErr) || (uBit && !uErr) ? NAN : data*(1.0+cErr+uErr));
323 void calcStats(
double stats[],
const double x0[],
unsigned n)
326 memcpy(x, x0,
sizeof(x));
328 double med = gsl_stats_median_from_sorted_data(x, 1, n),
329 qdev = gsl_stats_quantile_from_sorted_data(x, 1, n, 0.8413) -
330 gsl_stats_quantile_from_sorted_data(x, 1, n, 0.1587),
331 ave = gsl_stats_mean(x, 1, n),
332 sdev = sqrt(gsl_stats_variance_m(x, 1, n, ave)),
333 min = x[0], max = x[n-1];
335 stats[0] = med, stats[1] = 0.5*qdev;
336 stats[2] = ave, stats[3] = sdev;
337 stats[4] = min, stats[5] = max;
355 const double *pdata,
unsigned nModels,
unsigned nCols)
357 unsigned P0 = nParam&1, imin = 0;
359 for (
unsigned i=0; i != nModels; i++) {
361 for (
unsigned j=P0; j != P0+nParam; j++) {
362 merit += fabs(pdata[nCols*j+i] - pstats[j][0]) / pstats[j][1];
364 merit += 0.1*fabs(pdata[nCols*4+i]-pdata[nCols*4]);
365 if (merit < min) { min = merit, imin = i; }
387 int trialFit(ceres::Problem &prob, ceres::Solver::Summary &sum,
388 ceres::Solver::Options &opts,
const ceres::CostFunction *cost,
389 double p[],
unsigned NP,
const double p0[] =
nullptr,
390 const ceres::Solver::Summary *sum0 =
nullptr)
393 const double *
const pp = p;
394 (void )cost->Evaluate(&pp,
nullptr,
nullptr);
397 ceres::Solve(opts, &prob, &sum);
398 auto nIter = sum.num_successful_steps + sum.num_unsuccessful_steps - 1;
400 if (p0 && sum0 && (!sum.IsSolutionUsable() ||
401 (sum0->IsSolutionUsable() && sum0->final_cost < sum.final_cost))) {
402 memcpy(p, p0, NP*
sizeof(
double));
433 int robustFit(ceres::Problem &prob, ceres::Solver::Summary &sum,
434 ceres::Solver::Options &opts,
const ceres::CostFunction *cost,
435 double p[],
const double dp[],
436 const double pmin[],
const double pmax[],
437 unsigned NP,
double minCost = 0.25,
double maxCost = 25.0,
441 int nIter =
trialFit(prob, sum, opts, cost, p, NP);
444 auto needRefit = [minCost,maxCost,refit](
const ceres::Solver::Summary &s) {
445 return (!s.IsSolutionUsable() ||
446 s.termination_type == ceres::NO_CONVERGENCE ||
447 s.final_cost > maxCost || (s.final_cost > minCost && refit));
449 if (needRefit(sum)) {
451 auto perturb = [NP](
double p[],
const double dp[],
452 const double pmin[],
const double pmax[]) noexcept {
453 for (
unsigned i=0; i != NP; i++) {
454 double del = 0.1*fabs(dp[i]);
460 ceres::Solver::Summary sum0{sum};
462 memcpy(p0, p,
sizeof(p0));
467 if (dp) { memcpy(dp0, dp,
sizeof(dp0)); }
468 else {
for (
unsigned i=0; i != NP; i++) { dp0[i] = 0.10*p[i]; } }
470 for (
unsigned i=1; i < NP; i+=2) { dp0[i] = -dp0[i]; }
473 perturb(p, dp0, pmin, pmax);
474 nIter +=
trialFit(prob, sum, opts, cost, p, NP, p0, &sum0);
475 if (needRefit(sum)) {
477 for (
unsigned i=0; i != NP; i++) { dp0[i] = -dp0[i]; }
478 memcpy(p0, p,
sizeof(p0)), sum0 = sum;
479 perturb(p, dp0, pmin, pmax);
480 nIter +=
trialFit(prob, sum, opts, cost, p, NP, p0, &sum0);
500 for (
auto &b: band) {
502 auto iname = b->name();
503 iname = iname.substr(0, iname.find(
'-')+1);
505 for (
auto &i: instr) {
if (! i.compare(iname)) { found =
true;
break; } }
539 template<
unsigned NP>
545 const GrayData &gdata0,
unsigned id,
unsigned nThreads)
547 constexpr
unsigned P0 = NP&1,
548 M0 = (NP > 2 ? 4 : 0);
549 const unsigned NB = gdata0.
nBands;
551 const char *
const fn =
"findParameters";
552 size_t minBands = cli.
getInteger(
'n', NP==4 ? NP : 3);
554 auto &Thalo = gdata0.
outMap->at(0),
555 &dThalo = gdata0.
outMap->at(1),
556 &Nhalo = gdata0.
outMap->at(2),
557 &dNhalo = gdata0.
outMap->at(3),
558 &Tcore = gdata0.
outMap->at(0+M0),
559 &dTcore = gdata0.
outMap->at(1+M0),
560 &Ncore = gdata0.
outMap->at(2+M0),
561 &dNcore = gdata0.
outMap->at(3+M0),
562 &Cmap = gdata0.
outMap->at(4+M0),
563 &dCmap = gdata0.
outMap->at(5+M0),
564 &Imap = gdata0.
outMap->at(6+M0);
566 auto *diffMap = &gdata0.
outMap->at(7+M0),
567 *fracMap = &gdata0.
outMap->at(7+M0+(NP>2 ? NB : 0));
569 const double relerr = cli.
getDouble(
'A', 1e-3),
570 minCost = 0.125*NB, maxCost = 4.0*NB;
577 const unsigned sysBits = (sysSamp ? instr.
size()+NB : 0);
580 nSamp = 1+(gridSamp ? (gridSamp == 1 ? 16 : 81) :
581 sysSamp ?
std::max(1U, randSamp) : randSamp) *
582 (sysSamp ? 1U<<sysBits : 1);
583 gsl_rng *rng = gsl_rng_alloc(gsl_rng_mt19937);
584 gsl_rng_set(rng, 12345678901234567890UL +
id);
588 double chi2Max = (chi2Map.size() ? cli.
getDouble(
'c', 1.0) : -1.0);
589 if (chi2Map.size() && chi2Map.size() != bandData[0].
size()) {
590 MU_ERROR(fn,
"Ignoring --single-temp map (size %lu, require %lu)",
591 chi2Map.size(), bandData[0].
size());
601 auto calcF = [NB,&gdata,&gray](
double F[],
double Th,
double Sh,
602 double Tc,
double Sc) {
603 for (
unsigned j=0; j != NB; j++) {
604 auto Dj = gdata.band[j];
605 F[j] = (NP < 3 ? gray.F(Th, Sh, Dj) : gray.F(Tc, Sc, Th, Sh, Dj));
611 auto sample = [rng](
double mean,
double sigma) {
613 do { dev = gsl_ran_gaussian(rng, 1.0); }
while (fabs(dev) > 2.5);
614 return mean + dev*sigma;
616 auto randF = [NB,&gdata,&sample](
const double F[]) {
617 for (
unsigned j=0; j != NB; j++) {
618 if (
double iFerr = gdata.iFerr[j]) {
619 gdata.Fobs[j] = sample(F[j], 1.0/iFerr);
623 auto gridF = [NB,gridSamp,&gdata](
const double F[],
unsigned iSamp) {
624 for (
unsigned j=0; j != NB; j++) {
625 if (
double iFerr = gdata.iFerr[j]) {
626 gdata.Fobs[j] =
gridSample(F[j], 1.0/iFerr, j, iSamp, 1+gridSamp);
630 auto sysF = [NB,&instr, &gdata](
const double F[],
unsigned iSamp) {
631 for (
unsigned j=0; j != NB; j++) {
632 gdata.Fobs[j] =
sysSample(F[j], j, iSamp, instr, gdata.band);
637 const double T0 = cli.
getDouble(
'T', 15.0), S0 = 0.05,
638 T1 = (NP > 2 ? 10.0 : 0.0), S1 = (NP > 2 ? 0.5 : 0.0),
641 gdata.fixed.push_back(T0);
647 p[] = {0.0, 0.0, 0.0, 0.0},
648 p0[] = {T0, S0*Sscale, T1, S1*Sscale},
649 pmin[] = { 5.0, Nmin*Sscale, 5.0, 0.0*Sscale},
650 pmax[] = {50.0, 50.0*Sscale, 15.0, 50.0*Sscale};
653 if (P0) { pmax[2] = T0; }
654 if (NP == 4) { pmin[0] = pmax[2] =
std::min(14.0, T0-1.0); }
657 ceres::Solver::Summary sum;
658 ceres::CostFunction *cost;
665 prob.AddResidualBlock(cost,
nullptr, p+P0);
666 for (
unsigned i=0; i != NP; i++) {
667 prob.SetParameterLowerBound(p+P0, i, pmin[i+P0]);
668 prob.SetParameterUpperBound(p+P0, i, pmax[i+P0]);
671 ceres::Solver::Options opts;
672 opts.trust_region_strategy_type = ceres::LEVENBERG_MARQUARDT;
673 opts.linear_solver_type = ceres::DENSE_QR;
674 opts.dense_linear_algebra_library_type = ceres::EIGEN;
675 opts.initial_trust_region_radius = 1e4;
676 opts.min_trust_region_radius = 1e-12;
677 opts.max_trust_region_radius = 1e+12;
678 opts.use_nonmonotonic_steps =
false;
679 opts.max_consecutive_nonmonotonic_steps = 4;
680 opts.max_num_consecutive_invalid_steps = 4;
683 opts.max_num_iterations = maxIter;
684 opts.parameter_tolerance = relerr;
685 opts.function_tolerance = 0.1*relerr;
688 opts.check_gradients =
false;
689 opts.gradient_check_relative_precision = 1e-12;
694 ceres::Covariance::Options covopts;
695 covopts.algorithm_type = ceres::DENSE_SVD;
696 covopts.null_space_rank = -1;
698 ceres::Covariance covinst(covopts);
702 memcpy(p, p0, (P0+NP)*
sizeof(p[0]));
705 double pdata[4+1][nSamp], pstats[4+1][6];
710 const unsigned nRows = gdata.axis[1], nCols = gdata.axis[0],
711 px = nRows*nCols, px0 = ((nRows+nThreads-1)/nThreads)*nCols,
712 px10 = px0/10, iSkip = (nThreads-1)*nCols;
713 unsigned iOut = (
id==0 ? 0 : ~0U);
714 for (
unsigned i=
id*nCols, iCol=0, iTot=0; i < px;
715 iTot++, iCol=(iCol==nCols-1 ? 0 : iCol+1), i+=1+(iCol ? 0 : iSkip)) {
718 MU_INFO(fn,
"Solution %3lu%% complete @ %s",
719 (iOut == px0-1 ? 100 : 10*(iOut/px10)),
asctime());
721 iOut = (iOut == 9*px10 ? px0-1 : iOut+px10);
726 for (
unsigned j=0; j != NB; j++) {
727 auto Fobs = gdata.dnu0[j]*bandData[j][i],
728 Ferr = gdata.dnu0[j]*bandError[j][i];
731 gdata.Fobs[j] = Fobs;
732 gdata.iFerr[j] = 1.0/Ferr;
735 gdata.Fobs[j] = gdata.iFerr[j] = 0.0;
740 if (nOk < minBands || (chi2Max >= 0.0 && chi2Map[i] <= chi2Max)) {
741 for (
auto &omap: *gdata0.
outMap) { omap[i] = NAN; }
742 memcpy(p, p0, (P0+NP)*
sizeof(p[0]));
747 if (tempMap.size()) {
749 if (NP == 3) { prob.SetParameterUpperBound(p+P0, 1, p[0]); }
753 int nIter =
robustFit(prob, sum, opts, cost,
754 p+P0,
nullptr, pmin+P0, pmax+P0,
755 NP, minCost, maxCost, nSamp > 1);
756 double chi2_dof = 2.0*sum.final_cost*iDOF, dchi2_dof = 0.0;
757 MU_DEBUG(fn,
"CERES report:\n%s", sum.FullReport().c_str());
760 if (! sum.IsSolutionUsable()) {
761 MU_WARN(fn,
"(%ld,%ld) Solution unusable; masking pixel",
763 for (
auto &omap: *gdata0.
outMap) { omap[i] = NAN; }
764 memcpy(p, p0, (P0+NP)*
sizeof(p[0]));
767 MU_INFO_IF(sum.termination_type == ceres::NO_CONVERGENCE, fn,
768 "(%ld,%ld) Iteration limit reached", 1+iCol, 1+i/nCols);
771 double Th = p[0], Sh = p[1]*Srescale, dTh = 0.0, dSh = 0.0,
772 Tc = p[2], Sc = p[3]*Srescale, dTc = 0.0, dSc = 0.0;
776 if (covinst.Compute(covblocks, &prob)) {
777 covinst.GetCovarianceBlock(p+P0, p+P0, cov);
779 MU_INFO(fn,
"(%ld,%ld) Bad covariance matrix; masking values",
781 for (
unsigned j = 0; j != NP; j++) { cov[NP*j+j] = NAN; }
783 dTh = (P0 == 0 ? sqrt(cov[NP*0+0]) : 0.0);
784 dSh = sqrt(cov[NP*(1-P0)+(1-P0)])*Srescale;
785 dTc = (NP > 2 ? sqrt(cov[NP*(2-P0)+(2-P0)]) : 0.0);
786 dSc = (NP > 2 ? sqrt(cov[NP*(3-P0)+(3-P0)]) : 0.0)*Srescale;
791 double F0[NB], Fobs0[NB];
792 calcF(F0, Th, Sh, Tc, Sc);
793 memcpy(Fobs0, &gdata.Fobs[0],
sizeof(Fobs0));
797 pdata[0][0] = p[0], pdata[1][0] = p[1];
798 pdata[2][0] = p[2], pdata[3][0] = p[3];
799 pdata[4][0] = chi2_dof;
802 for (
unsigned j=1; j != nSamp; j++) {
804 p[0] = Th, p[1] = Sh*Sscale;
805 p[2] = Tc, p[3] = Sc*Sscale;
809 if (P0 == 0) { p[0] += 2.0*(gsl_rng_uniform(rng)-0.5); }
810 p[1] *= 0.5 + 1.5*gsl_rng_uniform(rng);
812 p[2] += 2.0*(gsl_rng_uniform(rng)-0.5);
813 p[3] *= 0.5 + 1.5*gsl_rng_uniform(rng);
817 const double *Fsys = F0;
818 if (sysSamp) { sysF(F0, j-1); Fsys = &gdata.Fobs[0]; }
820 if (gridSamp) { gridF(Fsys, (j-1)>>sysBits); }
821 else if (randSamp) { randF(Fsys); }
822 MU_DEBUG(fn,
"sample %2lu: dF = {%7.4f, %7.4f, %7.4f, %7.4f}", nOk,
823 (gdata.Fobs[0]-F0[0])*gdata.iFerr[0],
824 (gdata.Fobs[1]-F0[1])*gdata.iFerr[1],
825 (gdata.Fobs[2]-F0[2])*gdata.iFerr[2],
826 (gdata.Fobs[3]-F0[3])*gdata.iFerr[3]);
830 for (
auto f: gdata.Fobs) {
833 if (!ok) {
MU_DEBUG(fn,
"Skipping...");
continue; }
834 #endif // INIT_SAMPLES 837 nIter +=
robustFit(prob, sum, opts, cost,
838 p+P0,
nullptr, pmin+P0, pmax+P0,
839 NP, minCost, maxCost);
840 if (sum.IsSolutionUsable() &&
841 sum.termination_type != ceres::NO_CONVERGENCE) {
842 pdata[0][nOk] = p[0], pdata[1][nOk] = p[1];
843 pdata[2][nOk] = p[2], pdata[3][nOk] = p[3];
844 pdata[4][nOk] = 2.0*sum.final_cost*iDOF;
845 MU_DEBUG(fn,
"sample %lu: p = {%7.4f, %7.4f, %7.4f, %7.4f} [%.4f]",
846 nOk, p[0], p[1], p[2], p[3], pdata[4][nOk]);
852 if (P0) { pstats[0][1] = pstats[0][3] = 0.0; }
853 for (
unsigned j=P0; j != P0+NP; j++) {
855 MU_DEBUG(fn,
"pstats[%lu]: %7.4f(%7.4f) %7.4f(%7.4f) %7.4f %7.4f", j,
856 pstats[j][0], pstats[j][1], pstats[j][2],
857 pstats[j][3], pstats[j][4], pstats[j][5]);
859 dTh = pstats[0][3], dSh = pstats[1][3]*Srescale;
860 dTc = pstats[2][3], dSc = pstats[3][3]*Srescale;
863 dchi2_dof = pstats[4][3];
866 memcpy(&gdata.Fobs[0], Fobs0,
sizeof(Fobs0));
871 Th = pdata[0][iOpt], Sh = pdata[1][iOpt]*Srescale;
872 Tc = pdata[2][iOpt], Sc = pdata[3][iOpt]*Srescale;
873 chi2_dof = pdata[4][iOpt];
877 for (
unsigned j=P0; j != P0+NP; j++) {
878 p[j] = pstats[j][0], dp[j] = pstats[j][3];
880 nIter +=
robustFit(prob, sum, opts, cost, p+P0, dp+P0, pmin+P0, pmax+P0,
881 NP, minCost, maxCost,
true);
884 double chi2_dof1 = 2.0*sum.final_cost*iDOF;
885 if (chi2_dof1 < pdata[4][0]) {
886 Th = p[0], Sh = p[1]*Srescale;
887 Tc = p[2], Sc = p[3]*Srescale;
888 chi2_dof = chi2_dof1;
890 p[0] = pdata[0][0], p[1] = pdata[1][0];
891 p[2] = pdata[2][0], p[3] = pdata[3][0];
895 MU_DEBUG(fn,
"(%2ld,%2ld) Th = %5.2f(%.2f), Sh = %7.4f(%.4f), " 896 "Tc = %5.2f(%.2f), Sc = %7.4f(%.4f), chi2/DOF = %.3f(%.3f)",
897 1+iCol, 1+i/nCols, Th, dTh, Sh, dSh, Tc, dTc, Sc, dSc,
898 chi2_dof, dchi2_dof);
901 Thalo[i] = Th, dThalo[i] = dTh;
904 Tcore[i] = Tc, dTcore[i] = dTc;
907 Cmap[i] = chi2_dof, dCmap[i] = dchi2_dof;
911 for (
unsigned j=0; j != NB; j++) {
912 auto Dj = gdata.band[j];
913 double Fj = (NP < 3 ? gray.F(Th, Sh, Dj) : gray.F(Tc, Sc, Th, Sh, Dj));
915 diffMap[j][i] = (Fj - gdata.Fobs[j])*(absdiff ? 1.0/gdata.dnu0[j] :
917 if (NP > 2) { fracMap[j][i] = gray.F(Tc, Sc, Th, 0.0, Dj) / Fj; }
922 if (chi2_dof > 40.0 || (NP > 2 && fabs(Tc-T1) > 3.0)) {
923 memcpy(p, p0, (P0+NP)*
sizeof(p[0]));
959 const char *
const fn =
"getSingleMap";
966 fmap{
new CCfits::FITS(files.front(), CCfits::Read, hduName,
true)};
967 if (st[0]) { fmap->extension(hduName).read(map, ll, ur, st); }
968 else { fmap->extension(hduName).read(map); }
970 MU_WARN(fn,
"No HDU '%s' in file '%s'", hduName, files.
front());
1008 CCfits::FITS *outFITS,
1018 const char *
const fn =
"solve";
1023 auto modelh = cli.
getString(
'D',
"DL3"),
1027 auto mpos = modelh.
find(
','), rpos = ratioh.find(
',');
1028 decltype(modelh) modelc{modelh}, ratioc{ratioh};
1030 modelc = modelh.substr(mpos+1);
1034 ratioc = ratioh.substr(rpos+1);
1041 MU_INFO(fn,
"Using %s source detector response",
1042 extend ?
"extended" :
"point");
1044 MU_INFO(fn,
"Hot/halo dust model: %s, gas-to-dust ratio = %g",
1045 dusth.name(), dusth.rho());
1046 MU_INFO_IF(nParam > 2, fn,
"Cold/core dust model: %s, gas-to-dust ratio = %g",
1051 size_t nBands = bandData.
size();
1053 for (
size_t j=0; j != nBands; j++) {
1054 int band =
getBand(*bandHDU[j]);
1055 auto d = (band < 200 ? (
Detector *)
new PACS(band, extend)
1062 const double relerr = cli.
getDouble(
'A', 1e-3);
1063 auto px = axis[0]*axis[1];
1065 snprintf(comm,
sizeof(comm),
1066 "%s sources, N(H2) >= %.1e cm^-2," 1067 " %s/%s dust, gas/dust = %g/%g, relerr = %.1e",
1068 (extend ?
"Extended" :
"Point"), cli.
getDouble(
'N', 0.0),
1069 dusth.name().c_str(), dustc.
name().
c_str(),
1070 dusth.rho(), dustc.
rho(), relerr);
1074 mapNames = {
"Th",
"dTh",
"Nh(H2)",
"dNh(H2)",
1075 "Tc",
"dTc",
"Nc(H2)",
"dNc(H2)"};
1077 for (
auto m: {
"Chi2/DOF",
"dChi2/DOF",
"nIter"}) { mapNames.
emplace_back(m); }
1078 for (
auto &b: bandHDU) {
1083 for (
auto &b: bandHDU) {
1091 for (
auto name: mapNames) {
1094 outHDU.
back()->writeDate();
1095 outHDU.
back()->writeComment(comm);
1099 constexpr
double MJy = 1e-17;
1100 Graybody gray{dusth, dustc, 0.1*relerr};
1101 GrayData gdata{&gray, &outMap, nBands};
1102 for (
size_t j=0; j != nBands; j++) {
1103 gdata.band[j] = detect[j].get();
1104 gdata.dnu0.
push_back(detect[j]->freqWidth() * MJy);
1116 google::InitGoogleLogging(
"manticore");
1121 auto chi2Map =
getSingleMap(cli, ll, ur, st,
"Chi2/DOF");
1122 auto solver = (nParam == 1 ? &findParameters<1> :
1123 nParam == 2 ? &findParameters<2> :
1124 nParam == 3 ? &findParameters<3> :
1125 &findParameters<4>);
1131 double sErr[5], sMax[5] = {0.05, 0.04, 0.015, 0.015, 0.015};
1132 const unsigned iChi = (nParam <= 2 ? 4 : 8);
1133 for (
unsigned j=0, n=5, nSamp=n*n*n*n*n; j != nSamp; j++) {
1135 for (
unsigned k=0; k != 5; k++) {
1138 for (
unsigned k=0; k != px; k++) {
1139 bandData1[0][k] = bandData[0][k] * (1.0 + sErr[0]);
1140 bandData1[1][k] = bandData[1][k] * (1.0 + sErr[1] + sErr[2]);
1141 bandData1[2][k] = bandData[2][k] * (1.0 + sErr[1] + sErr[3]);
1142 bandData1[3][k] = bandData[3][k] * (1.0 + sErr[1] + sErr[4]);
1145 # define bandData1 bandData 1149 for (
unsigned i=1; i != nThreads; i++) {
1151 gdata, i, nThreads);
1154 solver(
bandData1, bandError, tempMap, chi2Map, cli, gdata, 0, nThreads);
1156 for (
auto &t: threads) { t.join(); }
1159 double chi2_tot = 0.0;
1160 for (
unsigned k=0; k != px; k++) { chi2_tot += outMap[iChi][k]; }
1161 MU_INFO(fn,
"<chi2>: %6.3f %+6.3f %+6.3f %+6.3f %+6.3f %+6.3f\n",
1162 chi2_tot/px, sErr[0], sErr[1], sErr[2], sErr[3], sErr[4]);
std::vector< double > fixed
Fixed parameters (optional).
static FILE * stream
Message log file stream.
std::vector< double > iFerr
Inverse flux uncertainty in each band.
void calcStats(double stats[], const double x0[], unsigned n)
Computes median (and arithmetic) statistics on an input vector.
std::vector< double > Fobs
Observed flux in each band.
unsigned getOption(const Option &option, Arguments *values=nullptr, unsigned nMax=-1) const
Retrieves all copies of a command line option.
void makeModelMap(std::vector< mapDataType > &bandData, std::vector< mapDataType > &bandError, const GrayData &gdata, unsigned nParam)
Replace input observations with theoretical model.
#define MU_ERROR(src, msg,...)
Log a message at level Log::ERROR.
double rcache_[NB]
Residual cache.
std::string asctime()
Current time a la std::asctime().
Ceres solver graybody cost model.
std::vector< long > axis
Image dimensions.
Command line options and arguments.
const std::string & name() const noexcept
Current model name.
std::string getString(char shortName, const std::string &defValue) const
Retrieves unique string option.
#define MU_DEBUG(src, msg,...)
Log a message at level Log::DEBUG.
Fcost_gray(const GrayData &gd)
Constructor.
std::vector< std::string > Arguments
Command line arguments type.
const double imu_g
Inverse mean molecular weight.
int robustFit(ceres::Problem &prob, ceres::Solver::Summary &sum, ceres::Solver::Options &opts, const ceres::CostFunction *cost, double p[], const double dp[], const double pmin[], const double pmax[], unsigned NP, double minCost=0.25, double maxCost=25.0, bool refit=false)
Non-linear fit with robustness checking.
double jcache_[NB *NP]
Jacobian cache.
std::vector< const Detector * > band
Band detectors.
constexpr double m_H
Mass of hygdrogen atom (g).
double sysSample(double data, unsigned iBand, unsigned iCycle, const std::vector< std::string > &instr, const std::vector< const Detector * > &bands)
Sample systematic data errors on a fixed grid.
void clearCache() const
Clear result cache.
unsigned nBands
Number of observation bands.
static int level
Message logging level for output to stream.
std::map< std::string, manticore::tableType > modelMap
std::vector< double > dnu0
Effective bandwidth.
std::vector< mapDataType > * outMap
Output maps.
std::string::size_type npos
static constexpr int DEBUG
Verbose information.
void solve(std::vector< mapDataType > &outMap, std::vector< CCfits::ExtHDU * > &outHDU, CCfits::FITS *outFITS, const std::vector< mapDataType > &bandData, const std::vector< mapDataType > &bandError, const std::vector< CCfits::PHDU * > &bandHDU, const std::vector< long > &axis, const mutils::CommandLine &cli, const std::vector< long > &ll, const std::vector< long > &ur, const std::vector< long > &st)
Performs least-squares fitting of band SEDs.
double gridSample(double data, double err, unsigned iBand, unsigned iCycle, unsigned nSamp)
Sample random data errors on a fixed grid.
std::vector< std::string > setInstruments(const std::vector< const Detector * > &band)
Set unique instrument types.
unsigned findMedianModel(const double pstats[][6], unsigned nParam, const double *pdata, unsigned nModels, unsigned nCols)
Finds optimal model from a set of alternatives.
mapDataType getSingleMap(const mu::CommandLine &cli, const std::vector< long > &ll, const std::vector< long > &ur, const std::vector< long > &st, const std::string &hduName)
Read a map plane from the --single-temp FITS file.
int getBand(const CCfits::HDU &hdu)
Returns nominal band center (microns) from an HDU.
Graybody * gray
Graybody emission model.
const double mu_g
Mean molecular weight.
#define MU_INFO(src, msg,...)
Log a message at level Log::INFO.
constexpr long modelMapLen
One-dimensional length of model map image.
virtual bool Evaluate(double const *const *parameters, double *residuals, double **jacobians) const final
Evaluate the fitting function and its Jacobian.
#define MU_INFO_IF(cond, src, msg,...)
Conditionally log a message at level Log::INFO.
#define MU_WARN(src, msg,...)
Log a message at level Log::WARN.
int trialFit(ceres::Problem &prob, ceres::Solver::Summary &sum, ceres::Solver::Options &opts, const ceres::CostFunction *cost, double p[], unsigned NP, const double p0[]=nullptr, const ceres::Solver::Summary *sum0=nullptr)
Trial fit with rejection check.
Observation fitting data structure.
void findParameters(const std::vector< mapDataType > &bandData, const std::vector< mapDataType > &bandError, const mapDataType &tempMap, const mapDataType &chi2Map, const mu::CommandLine &cli, const GrayData &gdata0, unsigned id, unsigned nThreads)
Finds best-fit model parameters.
constexpr int mapDataCode
Output FITS maps data code (cf. mapDataType).
GrayData(Graybody *g, std::vector< mapDataType > *o, size_t numBands)
Constructor.
static constexpr double scale
Surface density scaling factor.
long getInteger(char shortName) const
Retrieves unique decimal integer option.
double getDouble(char shortName, double defValue) const
Retrieves unique floating point option.
double rho() const noexcept
Current model gas-to-dust ratio.
std::valarray< float > mapDataType
Output FITS maps data type (cf. mapDataCode).
T emplace_back(T... args)
double pcache_[NP]
Parameter cache.