16 #include <gsl/gsl_multifit_nlin.h> 34 size_t bands,
size_t param) :
51 std::vector<const Detector*>
54 std::vector<mapDataType>
70 int f_gray(
const gsl_vector *x,
void *p, gsl_vector *f)
73 const auto &Fobs = gd->
Fobs, &Ferr = gd->
Ferr;
74 const auto &gray = *gd->
gray;
76 size_t n = Ferr.size();
77 double T = gsl_vector_get(x, 0), S = gsl_vector_get(x, 1);
78 for (
size_t i=0; i != n; i++) {
79 auto Di = gd->
band[i];
80 double iFerr = 1.0/Ferr[i], F = gray.F(T, S, Di);
81 gsl_vector_set(f, i, (F - Fobs[i])*iFerr);
94 int df_gray(
const gsl_vector *x,
void *p, gsl_matrix *J)
97 const auto &Ferr = gd->
Ferr;
98 const auto &gray = *gd->
gray;
100 size_t n = Ferr.size();
101 double T = gsl_vector_get(x, 0), S = gsl_vector_get(x, 1);
102 for (
size_t i=0; i != n; i++) {
103 auto Di = gd->
band[i];
104 double iFerr = 1.0/Ferr[i];
105 gsl_matrix_set(J, i, 0, gray.dF_dT(T, S, Di)*iFerr);
106 gsl_matrix_set(J, i, 1, gray.dF_dS(T, S, Di)*iFerr);
120 int fdf_gray(
const gsl_vector *x,
void *p, gsl_vector *f, gsl_matrix *J)
131 int f_gray2(
const gsl_vector *x,
void *p, gsl_vector *f)
134 const auto &Fobs = gd->
Fobs, &Ferr = gd->
Ferr;
135 const auto &gray = *gd->
gray;
137 size_t n = Ferr.size(), nParam = gd->
nParam;
138 double Sc = gsl_vector_get(x, 0), Tc = gsl_vector_get(x, 1),
139 Sh = gsl_vector_get(x, 2),
140 Th = (nParam == 4 ? gsl_vector_get(x, 3) : gd->
fixed[0]);
141 for (
size_t i=0; i != n; i++) {
142 auto Di = gd->
band[i];
143 double iFerr = 1.0/Ferr[i];
144 gsl_vector_set(f, i, (gray.F(Tc, Sc, Th, Sh, Di) - Fobs[i])*iFerr);
157 int df_gray2(
const gsl_vector *x,
void *p, gsl_matrix *J)
160 const auto &Ferr = gd->
Ferr;
161 const auto &gray = *gd->
gray;
163 size_t n = Ferr.size(), nParam = gd->
nParam;
164 double Sc = gsl_vector_get(x, 0), Tc = gsl_vector_get(x, 1),
165 Sh = gsl_vector_get(x, 2),
166 Th = (nParam == 4 ? gsl_vector_get(x, 3) : gd->
fixed[0]);
167 for (
size_t i=0; i != n; i++) {
168 auto Di = gd->
band[i];
169 double iFerr = 1.0/Ferr[i];
170 gsl_matrix_set(J, i, 0, gray.dF_dSc(Tc, Sc, Th, Sh, Di)*iFerr);
171 gsl_matrix_set(J, i, 1, gray.dF_dTc(Tc, Sc, Th, Sh, Di)*iFerr);
172 gsl_matrix_set(J, i, 2, gray.dF_dSh(Tc, Sc, Th, Sh, Di)*iFerr);
174 gsl_matrix_set(J, i, 3, gray.dF_dTh(Tc, Sc, Th, Sh, Di)*iFerr);
189 int fdf_gray2(
const gsl_vector *x,
void *p, gsl_vector *f, gsl_matrix *J)
198 auto t = std::time(
nullptr);
199 std::string rtn = asctime_r(localtime_r(&t, &tmbuf), buf);
220 std::vector<mapDataType> &bandError,
223 const auto nParam = gdata.
nParam;
224 const auto &gray = *gdata.
gray;
226 const double in1 = 1.0/(n-1);
231 Tc = 6.0*(1.0 + (j%n)*in1),
232 Sc =
mu_g*3e21*pow(100.0, (j/n)*in1),
233 Th = 15.0 + 3.0*(i%n)*in1,
234 Sh =
mu_g*3e19*pow(100.0, (i/n)*in1);
236 "(%lu, %lu) Tc = %.2f, Sc = %.2e, Th = %.2f, Sh = %.2e",
237 i, j, Tc, Sc, Th, Sh);
239 for (
unsigned iBand=0; iBand != bandData.size(); iBand++) {
240 auto &data = bandData[iBand], &err = bandError[iBand];
241 auto Di = gdata.
band[iBand];
242 data[iMap] = (nParam == 2 ? gray.F(Th, Sh, Di) :
243 gray.F(Tc, Sc, Th, Sh, Di)) /
245 err[iMap] = 0.001*data[iMap];
246 MU_DEBUG(
"makeModelMap",
" [%lu] %.4e", iBand, data[iMap]);
266 const std::vector<mapDataType> &bandError,
269 const GrayData &gdata0,
unsigned id,
unsigned nThreads)
271 const char *
const fn =
"solveStage1";
274 auto &Tmap = gdata0.
outMap->at(0),
275 &dTmap = gdata0.
outMap->at(1),
276 &Nmap = gdata0.
outMap->at(2),
277 &dNmap = gdata0.
outMap->at(3),
278 &Cmap = gdata0.
outMap->at(4),
279 &Imap = gdata0.
outMap->at(5);
280 auto *diffMap = &gdata0.
outMap->at(6);
283 "Fit is 2-param, ignoring --single-temp map");
291 gsl_multifit_fdfsolver *solver =
292 gsl_multifit_fdfsolver_alloc(gsl_multifit_fdfsolver_lmsder,
295 gsl_multifit_function_fdf fdf;
304 const double T0 = cli.
getDouble(
'T', 16.5), S0 = 0.1;
305 gsl_vector *x0 = gsl_vector_alloc(nParam);
306 gsl_vector_set(x0, 0, T0);
307 gsl_vector_set(x0, 1, S0);
310 gsl_matrix *cov = gsl_matrix_alloc(nParam, nParam);
314 const double relerr = cli.
getDouble(
'A', 1e-2);
315 size_t maxIter = std::max(1L, cli.
getInteger(
'm', 25)),
317 i0 = (
id*px)/nThreads, i1 = ((
id+1)*px)/nThreads;
318 for (
unsigned i=i0; i != i1; i++) {
321 for (
size_t j=0; j != nBands; j++) {
322 gdata.Fobs[j] = gdata.dnu0[j]*bandData[j][i];
323 gdata.Ferr[j] = gdata.dnu0[j]*bandError[j][i];
325 if (std::isfinite(gdata.Fobs[j]) && std::isfinite(gdata.Ferr[j])) {
329 gdata.Ferr[j] = 1e10;
334 MU_INFO_IF(
id == 0 && i%std::max(i1/10, 1UL) == 0, fn,
335 "Fitting solution %2.0f%% complete @ %s",
339 if (nOk < minBands) {
340 for (
auto &omap: *gdata0.
outMap) { omap[i] = NAN; }
345 gsl_multifit_fdfsolver_set(solver, &fdf, x0);
351 gsl_multifit_fdfsolver_iterate(solver);
352 MU_DEBUG(fn,
"[%2lu] T = %.2f S = %.4f", iter,
353 gsl_vector_get(solver->x, 0), gsl_vector_get(solver->x, 1));
354 }
while (gsl_multifit_test_delta(solver->dx, solver->x, 0.0, relerr) ==
355 GSL_CONTINUE && iter != maxIter);
358 gsl_multifit_covar(solver->J, 1e-8, cov);
360 for (
size_t j=0; j != nBands; j++) {
361 auto fj = gsl_vector_get(solver->f, j);
362 diffMap[j][i] = fj*(absdiff ? bandError[j][i] : 1.0);
366 double chi2_dof = chi2/(nBands-nParam), vscale = std::max(1.0, chi2_dof),
367 T = gsl_vector_get(solver->x, 0), S = gsl_vector_get(solver->x, 1),
368 dT = sqrt(vscale * gsl_matrix_get(cov, 0, 0)),
369 dS = sqrt(vscale * gsl_matrix_get(cov, 1, 1));
370 MU_DEBUG(fn,
"T = %.2f(%.2f), S = %.4f(%.4f), chi2/DOF = %.3f\n",
371 T, dT, S, dS, chi2_dof);
373 Tmap[i] = T, dTmap[i] = dT;
375 Cmap[i] = chi2_dof; Imap[i] = iter;
378 "Fitting failed for pixel (%ld,%ld): dT/T = %.4g, dN/N = %.4g",
379 1+i%gdata.axes[0], 1+i/gdata.axes[0],
380 gsl_vector_get(solver->dx, 0)/T,
381 gsl_vector_get(solver->dx, 1)/S);
385 if (
modelMap) { T += (i%7)-2.5, S *= 1.0 + ((i%11)-5.5)/10.0; }
386 gsl_vector_set(x0, 0, T > 1.0 && T < 1e3 ? T : T0);
387 gsl_vector_set(x0, 1, S > 1e-3 && S < 1e3 ? S : S0);
391 gsl_multifit_fdfsolver_free(solver);
393 gsl_matrix_free(cov);
414 const std::vector<mapDataType> &bandError,
417 const GrayData &gdata0,
unsigned id,
unsigned nThreads)
419 const char *
const fn =
"solveStage2";
422 auto &Tcore = gdata0.
outMap->at(0),
423 &dTcore = gdata0.
outMap->at(1),
424 &Ncore = gdata0.
outMap->at(2),
425 &dNcore = gdata0.
outMap->at(3),
426 &Thalo = gdata0.
outMap->at(4),
427 &dThalo = gdata0.
outMap->at(5),
428 &Nhalo = gdata0.
outMap->at(6),
429 &dNhalo = gdata0.
outMap->at(7),
430 &Cmap = gdata0.
outMap->at(8),
431 &Imap = gdata0.
outMap->at(9);
432 auto *diffMap = &gdata0.
outMap->at(10);
437 double chi2Max = (chi2Map.size() ? cli.
getDouble(
'c', 1.0) : -1.0);
438 if (chi2Map.size() && chi2Map.size() != bandData[0].size()) {
439 MU_ERROR(fn,
"Ignoring --single-temp map (size %lu, require %lu)",
440 chi2Map.size(), bandData[0].size());
450 gsl_multifit_fdfsolver *solver =
451 gsl_multifit_fdfsolver_alloc(gsl_multifit_fdfsolver_lmsder,
454 gsl_multifit_function_fdf fdf;
463 const double T0 = cli.
getDouble(
'T', 16.5), S0 = 1.0, S1 = 0.1;
464 gsl_vector *x0 = gsl_vector_alloc(nParam);
465 gsl_vector_set(x0, 0, S0);
466 gsl_vector_set(x0, 1, T0-5.0);
467 gsl_vector_set(x0, 2, S1);
469 gsl_vector_set(x0, 3, T0+5.0);
471 gdata.fixed.resize(1);
474 gsl_matrix *cov = gsl_matrix_alloc(nParam, nParam);
477 const double relerr = cli.
getDouble(
'A', 1e-2);
478 size_t maxIter = std::max(1L, cli.
getInteger(
'm', 25)),
480 i0 = (
id*px)/nThreads, i1 = ((
id+1)*px)/nThreads;
481 for (
unsigned i=i0; i != i1; i++) {
484 for (
size_t j=0; j != nBands; j++) {
485 gdata.Fobs[j] = gdata.dnu0[j]*bandData[j][i];
486 gdata.Ferr[j] = gdata.dnu0[j]*bandError[j][i];
488 if (std::isfinite(gdata.Fobs[j]) && std::isfinite(gdata.Ferr[j])) {
492 gdata.Ferr[j] = 1e10;
499 "Fitting solution %2.0f%% complete @ %s",
503 if (nOk < minBands || (chi2Max >= 0.0 && chi2Map[i] <= chi2Max)) {
504 for (
auto &omap: *gdata0.
outMap) { omap[i] = NAN; }
509 gsl_multifit_fdfsolver_set(solver, &fdf, x0);
514 MU_DEBUG(fn,
"[%2lu] Tc = %5.2f Sc = %7.4f " 515 " Th = %5.2f Sh = %7.4f", iter,
516 gsl_vector_get(solver->x, 1), gsl_vector_get(solver->x, 0),
517 nParam == 4 ? gsl_vector_get(solver->x, 3) : gdata.fixed[0],
518 gsl_vector_get(solver->x, 2));
520 gsl_multifit_fdfsolver_iterate(solver);
521 }
while (gsl_multifit_test_delta(solver->dx, solver->x, 0.0, relerr) ==
522 GSL_CONTINUE && ++iter != maxIter);
525 gsl_multifit_covar(solver->J, 1e-6, cov);
527 for (
size_t j=0; j != nBands; j++) {
528 auto fj = gsl_vector_get(solver->f, j);
533 double chi2_dof = chi2/std::max(nBands-nParam, 1UL),
534 vscale = std::max(1.0, chi2_dof),
535 Sc = gsl_vector_get(solver->x, 0),
536 Tc = gsl_vector_get(solver->x, 1),
537 Sh = gsl_vector_get(solver->x, 2),
538 Th = nParam == 4 ? gsl_vector_get(solver->x, 3) : gdata.fixed[0],
539 dSc = sqrt(vscale * gsl_matrix_get(cov, 0, 0)),
540 dTc = sqrt(vscale * gsl_matrix_get(cov, 1, 1)),
541 dSh = sqrt(vscale * gsl_matrix_get(cov, 2, 2)),
542 dTh = nParam == 4 ? sqrt(vscale * gsl_matrix_get(cov, 3, 3)) : 0.0;
543 MU_DEBUG(fn,
"(%ld,%ld) Tc = %5.2f(%.2f), Sc = %7.4f(%.4f), " 544 "Th = %5.2f(%.2f), Sh = %7.4f(%.4f), chi2/DOF = %.3f\n",
545 1+i%gdata.axes[0], 1+i/gdata.axes[0],
546 Tc, dTc, Sc, dSc, Th, dTh, Sh, dSh, chi2_dof);
548 Tcore[i] = Tc, dTcore[i] = dTc;
550 Thalo[i] = Th, dThalo[i] = dTh;
552 Cmap[i] = chi2_dof; Imap[i] = iter;
555 "Fitting failed for pixel (%ld,%ld): " 556 "dTc/Tc = %.4g, dNc/Nc = %.4g, " 557 "dTh/Th = %.4g, dNh/Nh = %.4g",
558 1+i%gdata.axes[0], 1+i/gdata.axes[0],
559 gsl_vector_get(solver->dx, 1)/Tc,
560 gsl_vector_get(solver->dx, 0)/Sc,
561 nParam == 4 ? gsl_vector_get(solver->dx, 3)/Th : 0.0,
562 gsl_vector_get(solver->dx, 2)/Sh);
566 gsl_vector_set(x0, 0, S0);
567 gsl_vector_set(x0, 1, T0-5.0);
568 gsl_vector_set(x0, 2, S1);
570 gsl_vector_set(x0, 3, T0+5.0);
575 gsl_multifit_fdfsolver_free(solver);
577 gsl_matrix_free(cov);
599 const std::string hduName =
"Chi2/DOF";
600 std::unique_ptr<CCfits::FITS>
601 fchi2{
new CCfits::FITS(files.front(), CCfits::Read, hduName,
true)};
602 fchi2->extension(hduName).read(chi2);
635 void solve(std::vector<mapDataType> &outMap,
636 std::vector<CCfits::ExtHDU *> &outHDU,
637 CCfits::FITS *outFITS,
638 const std::vector<mapDataType> &bandData,
639 const std::vector<mapDataType> &bandError,
640 const std::vector<CCfits::PHDU*> &bandHDU,
643 const char *
const fn =
"solve";
649 MU_INFO(fn,
"Using %s source detector response",
650 extend ?
"extended" :
"point");
651 MU_INFO(fn,
"Dust model: %s, gas-to-dust ratio = %g",
652 dust.name(), dust.rho());
656 size_t nBands = bandData.size();
657 std::vector<std::unique_ptr<Detector>> detect;
658 for (
size_t j=0; j != nBands; j++) {
659 int band =
getBand(*bandHDU[j]);
660 auto d = (band < 200 ? (
Detector *)
new PACS(band, extend)
663 detect.emplace_back(d);
667 const double relerr = cli.
getDouble(
'A', 1e-2);
668 std::vector<long> axes = {bandHDU[0]->axis(0), bandHDU[0]->axis(1)};
671 auto px = axes[0]*axes[1];
673 snprintf(comm,
sizeof(comm),
674 "%s sources, %s dust, gas/dust = %g, relerr = %.1e",
675 (extend ?
"Extended" :
"Point"), dust.name().c_str(),
680 std::vector<std::string>
681 mapNames = {
"T",
"dT",
"N(H2)",
"dN(H2)",
"Chi2/DOF",
"nIter"};
684 mapNames = {
"Tc",
"dTc",
"Nc(H2)",
"dNc(H2)",
685 "Th",
"dTh",
"Nh(H2)",
"dNh(H2)",
"Chi2/DOF",
"nIter"};
687 for (
auto &b: bandHDU) {
689 mapNames.push_back(
"diff" + std::to_string(
getBand(*b)));
692 for (
auto name: mapNames) {
694 outHDU.push_back(outFITS->addImage(name,
mapDataCode, axes));
695 outHDU.back()->writeDate();
696 outHDU.back()->writeComment(comm);
700 constexpr
double MJy = 1e-17;
702 GrayData gdata{&gray, &outMap, nBands, nParam};
703 for (
size_t j=0; j != nBands; j++) {
704 gdata.
band[j] = detect[j].get();
705 gdata.dnu0.push_back(detect[j]->freqWidth() * MJy);
711 makeModelMap(
const_cast<std::vector<mapDataType>&
>(bandData),
712 const_cast<std::vector<mapDataType>&
>(bandError),
717 unsigned nThreads = std::max(1L, cli.
getInteger(
't', 1));
719 std::vector<std::thread> threads;
720 for (
unsigned i=1; i != nThreads; i++) {
721 threads.emplace_back(solver, bandData, bandError, chi2Map, cli,
724 solver(bandData, bandError, chi2Map, cli, gdata, 0, nThreads);
726 for (
auto &t: threads) { t.join(); }
std::vector< double > fixed
Fixed parameters (optional).
int df_gray2(const gsl_vector *x, void *p, gsl_matrix *J)
Two-temperature observation fitting function Jacobian.
#define MU_WARN_IF(cond, src, msg,...)
Conditionally log a message at level Log::WARN.
int f_gray2(const gsl_vector *x, void *p, gsl_vector *f)
Two-temperature observation fitting function.
mapDataType getChi2(const mu::CommandLine &cli)
Read 2-param reduced map.
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 mutils::CommandLine &cli)
Performs least-squares fitting of band SEDs.
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.
#define MU_ERROR(src, msg,...)
Log a message at level Log::ERROR.
std::string asctime()
Current time a la std::asctime().
std::vector< long > axes
Image dimensions.
Command line options and arguments.
int df_gray(const gsl_vector *x, void *p, gsl_matrix *J)
Single-temperature observation fitting function Jacobian.
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.
std::vector< std::string > Arguments
Command line arguments type.
const double imu_g
Inverse mean molecular weight.
int f_gray(const gsl_vector *x, void *p, gsl_vector *f)
Single-temperature observation fitting function.
GrayData(Graybody *g, std::vector< mapDataType > *o, size_t bands, size_t param)
Constructor.
constexpr double m_H
Mass of hygdrogen atom (g).
void solveStage2(const std::vector< mapDataType > &bandData, const std::vector< mapDataType > &bandError, const mapDataType &chi2Map, const mu::CommandLine &cli, const GrayData &gdata0, unsigned id, unsigned nThreads)
Stage 2 solver.
std::vector< double > Ferr
Flux uncertainty in each band.
int fdf_gray(const gsl_vector *x, void *p, gsl_vector *f, gsl_matrix *J)
Single-temperature observation fitting function plus Jacobian.
int fdf_gray2(const gsl_vector *x, void *p, gsl_vector *f, gsl_matrix *J)
Two-temperature observation fitting function plus Jacobian.
void makeModelMap(std::vector< mapDataType > &bandData, std::vector< mapDataType > &bandError, const GrayData &gdata)
Replace input observations with theoretical model.
std::map< std::string, manticore::tableType > modelMap
std::vector< double > dnu0
Effective bandwidth.
std::vector< mapDataType > * outMap
Output maps.
size_t nBands
Number of observation bands.
std::vector< const Detector * > band
Band detectors.
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.
#define MU_INFO_IF(cond, src, msg,...)
Conditionally log a message at level Log::INFO.
Observation fitting data structure.
constexpr int mapDataCode
Output FITS maps data code (cf. mapDataType).
long getInteger(char shortName) const
Retrieves unique decimal integer option.
double getDouble(char shortName, double defValue) const
Retrieves unique floating point option.
void solveStage1(const std::vector< mapDataType > &bandData, const std::vector< mapDataType > &bandError, const mapDataType &chi2Map, const mu::CommandLine &cli, const GrayData &gdata0, unsigned id, unsigned nThreads)
Stage 1 solver.
size_t nParam
Number of fitting parameters.
std::valarray< float > mapDataType
Output FITS maps data type (cf. mapDataCode).