Manticore  Version 1.0
Physics of Molecular Clouds
solve.cc
Go to the documentation of this file.
1 
12 #include <ctime>
13 #include <thread>
14 
15 #include <gsl/gsl_multifit_nlin.h>
16 
17 #include "Graybody.h"
18 #include "PACS.h"
19 #include "SPIRE.h"
20 
21 namespace mu = mutils;
22 
24 namespace manticore {
25 
27 struct GrayData {
29  GrayData(Graybody *g, std::vector<mapDataType> *o, size_t n) :
30  gray(g), outMap(o)
31  {
32  Fobs.resize(n);
33  Ferr.resize(n);
34  band.resize(n);
35  }
36 
38 
39  std::vector<long> axes;
40 
41  std::vector<double> Fobs,
42  Ferr,
43  dnu0;
44 
45  std::vector<const Detector*>
46  band;
47 
48  std::vector<mapDataType>
49  *outMap;
50 };
51 
59 int f_gray(const gsl_vector *x, void *p, gsl_vector *f)
60 {
61  GrayData *gd = static_cast<GrayData *>(p);
62  const auto &Fobs = gd->Fobs, &Ferr = gd->Ferr;
63  const auto &gray = *gd->gray;
64 
65  size_t n = Ferr.size();
66  double T = gsl_vector_get(x, 0), S = gsl_vector_get(x, 1);
67  for (size_t i=0; i != n; i++) {
68  auto Di = gd->band[i];
69  double iFerr = 1.0/Ferr[i];
70  gsl_vector_set(f, i, (gray.F(T, S, Di) - Fobs[i])*iFerr);
71  }
72 
73  return GSL_SUCCESS;
74 }
75 
83 int df_gray(const gsl_vector *x, void *p, gsl_matrix *J)
84 {
85  GrayData *gd = static_cast<GrayData *>(p);
86  const auto &Ferr = gd->Ferr;
87  const auto &gray = *gd->gray;
88 
89  size_t n = Ferr.size();
90  double T = gsl_vector_get(x, 0), S = gsl_vector_get(x, 1);
91  for (size_t i=0; i != n; i++) {
92  auto Di = gd->band[i];
93  double iFerr = 1.0/Ferr[i];
94  gsl_matrix_set(J, i, 0, gray.dF_dT(T, S, Di)*iFerr);
95  gsl_matrix_set(J, i, 1, gray.dF_dS(T, S, Di)*iFerr);
96  }
97 
98  return GSL_SUCCESS;
99 }
100 
109 int fdf_gray(const gsl_vector *x, void *p, gsl_vector *f, gsl_matrix *J)
110 { return f_gray(x, p, f), df_gray(x, p, J); }
111 
112 
114 std::string asctime()
115 {
116  char buf[64];
117  struct tm tmbuf;
118  auto t = std::time(nullptr);
119  std::string rtn = asctime_r(localtime_r(&t, &tmbuf), buf);
120  rtn.pop_back(); // Remove newline.
121  return rtn;
122 }
123 
124 
139  const std::vector<mapDataType> &bandData,
140  const std::vector<mapDataType> &bandError,
141  const mu::CommandLine &cli,
142  const GrayData &gdata0, unsigned id, unsigned nThreads)
143 {
144  const char *const fn = "solveStage1";
145  size_t nBands = bandData.size(), nParam = 2,
146  minBands = cli.getInteger('n', 3);
147  const double imu_g = 1.0/(2.75*m_H);
148  auto &Tmap = gdata0.outMap->at(0),
149  &dTmap = gdata0.outMap->at(1),
150  &Nmap = gdata0.outMap->at(2),
151  &dNmap = gdata0.outMap->at(3),
152  &Cmap = gdata0.outMap->at(4);
153 
154  // Thread-local storage.
155  Graybody gray{*gdata0.gray};
156  GrayData gdata{gdata0};
157  gdata.gray = &gray;
158 
159  // GSL v1.x least-squares fitting setup.
160  gsl_multifit_fdfsolver *solver =
161  gsl_multifit_fdfsolver_alloc(gsl_multifit_fdfsolver_lmsder, // or lmder
162  nBands, nParam);
163  //
164  gsl_multifit_function_fdf fdf;
165  fdf.f = f_gray;
166  fdf.df = df_gray;
167  fdf.fdf = fdf_gray;
168  fdf.n = nBands;
169  fdf.p = nParam;
170  fdf.params = &gdata;
171  //
172  // Initial guess.
173  gsl_vector *x0 = gsl_vector_alloc(nParam);
174  gsl_vector_set(x0, 0, 15.0);
175  gsl_vector_set(x0, 1, 1.0);
176  //
177  // Covariance storage.
178  gsl_matrix *cov = gsl_matrix_alloc(nParam, nParam);
179 
180  // Loop over map pixels.
181  const double relerr = cli.getDouble('A', 1e-2);
182  size_t maxIter = std::max(1L, cli.getInteger('M', 25)),
183  px = bandData[0].size(),
184  i0 = (id*px)/nThreads, i1 = ((id+1)*px)/nThreads;
185  for (unsigned i=i0; i != i1; i++) {
186  // Set target data.
187  unsigned nOk = 0;
188  for (size_t j=0; j != nBands; j++) {
189  gdata.Fobs[j] = gdata.dnu0[j]*bandData[j][i];
190  gdata.Ferr[j] = gdata.dnu0[j]*bandError[j][i];
191 
192  if (gdata.Fobs[j] > 0.0 && gdata.Ferr[j] > 0.0) {
193  nOk++;
194  } else {
195  gdata.Fobs[j] = 0.0;
196  gdata.Ferr[j] = 1e10; // Weight-out bad data.
197  }
198  }
199 
200  // Progress display.
201  MU_INFO_IF(id == 0 && i%(i1/10) == 0, fn,
202  "Fitting solution %2.0f%% complete @ %s",
203  (100.0*i)/i1, asctime());
204 
205  // Skip invalid pixels.
206  if (nOk < minBands) {
207  Tmap[i] = dTmap[i] = Nmap[i] = dNmap[i] = Cmap[i] = NAN;
208  continue;
209  }
210 
211  // Initialize solver.
212  gsl_multifit_fdfsolver_set(solver, &fdf, x0);
213 
214  // Iterate on solution.
215  size_t iter = 0;
216  do {
217  gsl_multifit_fdfsolver_iterate(solver);
218  MU_DEBUG(fn, "[%2lu] T = %.2f S = %.4f", iter,
219  gsl_vector_get(solver->x, 0), gsl_vector_get(solver->x, 1));
220  } while (gsl_multifit_test_delta(solver->dx, solver->x, 0.0, relerr) ==
221  GSL_CONTINUE && ++iter != maxIter);
222 
223  // Results.
224  gsl_multifit_covar(solver->J, 1e-8, cov);
225  double chi2 = 0.0;
226  for (size_t k=0; k != nBands; k++) {
227  auto fk = gsl_vector_get(solver->f, k);
228  chi2 += fk*fk;
229  }
230  //
231  double chi2_dof = chi2/(nBands-nParam), vscale = std::max(1.0, chi2_dof),
232  T = gsl_vector_get(solver->x, 0), S = gsl_vector_get(solver->x, 1),
233  dT = sqrt(vscale * gsl_matrix_get(cov, 0, 0)),
234  dS = sqrt(vscale * gsl_matrix_get(cov, 1, 1));
235  MU_DEBUG(fn, "T = %.2f(%.2f), S = %.4f(%.4f), chi2/DOF = %.3f\n",
236  T, dT, S, dS, chi2_dof);
237  //
238  Tmap[i] = T, dTmap[i] = dT;
239  Nmap[i] = S*imu_g, dNmap[i] = dS*imu_g;
240  Cmap[i] = chi2_dof;
241  //
242  MU_WARN_IF(iter == maxIter, fn,
243  "Fitting failed for pixel (%ld,%ld): dT/T = %.4g, dN/N = %.4g",
244  1+i%gdata.axes[0], 1+i/gdata.axes[0],
245  gsl_vector_get(solver->dx, 0)/T,
246  gsl_vector_get(solver->dx, 1)/S);
247 
248  // Chain next initial guess to previous solution.
249  gsl_vector_set(x0, 0, T);
250  gsl_vector_set(x0, 1, S);
251  }
252 
253  // Free storage.
254  gsl_multifit_fdfsolver_free(solver);
255  gsl_vector_free(x0);
256  gsl_matrix_free(cov);
257 }
258 
259 
286 void solve(std::vector<mapDataType> &outMap,
287  std::vector<CCfits::ExtHDU *> &outHDU,
288  CCfits::FITS *outFITS,
289  const std::vector<mapDataType> &bandData,
290  const std::vector<mapDataType> &bandError,
291  const std::vector<CCfits::PHDU*> &bandHDU,
292  const mu::CommandLine &cli)
293 {
294  const char *const fn = "solve";
295 
296  // Dust model.
297  bool extend = !cli.getOption('p');
298  Dust dust(cli.getString('d', "OH5"), cli.getDouble('g', 100.0));
299  MU_INFO(fn, "Using %s source detector response",
300  extend ? "extended" : "point");
301  MU_INFO(fn, "Dust model: %s, gas-to-dust ratio = %g",
302  dust.name(), dust.rho());
303 
304  // Allocate result maps.
305  const double relerr = cli.getDouble('A', 1e-2);
306  std::vector<long> axes = {bandHDU[0]->axis(0), bandHDU[0]->axis(1)};
307  auto px = axes[0]*axes[1];
308  char comm[1024];
309  snprintf(comm, sizeof(comm),
310  "%s sources, %s dust, gas/dust = %g, relerr = %.1e",
311  (extend ? "Extended" : "Point"), dust.name().c_str(),
312  dust.rho(), relerr);
313  //
314  for (auto name: {"T", "dT", "N(H2)", "dN(H2)", "Chi2/DOF"}) {
315  outMap.push_back(mapDataType(0.0, px));
316  outHDU.push_back(outFITS->addImage(name, mapDataCode, axes));
317  outHDU.back()->writeDate();
318  outHDU.back()->writeComment(comm);
319  }
320 
321  // Band detector setup.
322  // Currently supports PACS/SPIRE only!
323  size_t nBands = bandData.size();
324  std::vector<std::unique_ptr<Detector>> detect;
325  for (size_t j=0; j != nBands; j++) {
326  int band;
327  bandHDU[j]->keyWord("BAND").value(band);
328 
329  auto d = (band < 200 ? (Detector *)new PACS(band, extend)
330  : (Detector *)new SPIRE(band, extend));
331  d->init();
332  detect.emplace_back(d);
333  }
334 
335  // Emission model.
336  constexpr double MJy = 1e-17; // MJy -> erg/cm^2/s/Hz
337  Graybody gray{dust, 0.1*relerr};
338  GrayData gdata{&gray, &outMap, nBands};
339  for (size_t j=0; j != nBands; j++) {
340  gdata.band[j] = detect[j].get();
341  gdata.dnu0.push_back(detect[j]->freqWidth() * MJy);
342  }
343  gdata.axes = axes;
344 
345  // Create threads and process maps.
346  unsigned nThreads = std::max(1L, cli.getInteger('t', 1));
347  std::vector<std::thread> threads;
348  for (unsigned i=1; i != nThreads; i++) {
349  threads.emplace_back(&solveStage1, bandData, bandError, cli,
350  gdata, i, nThreads);
351  }
352  solveStage1(bandData, bandError, cli, gdata, 0, nThreads);
353  //
354  for (auto &t: threads) { t.join(); }
355 
356  MU_INFO(fn, "Solution complete @ %s", asctime());
357 }
358 
359 } // namespace manticore
#define MU_WARN_IF(cond, src, msg,...)
Conditionally log a message at level Log::WARN.
Definition: Log.h:142
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.
Definition: solve.cc:286
std::vector< double > Fobs
Observed flux in each band.
Definition: solve.cc:41
unsigned getOption(const Option &option, Arguments *values=nullptr, unsigned nMax=-1) const
Retrieves all copies of a command line option.
Definition: CommandLine.cc:197
std::string asctime()
Current time a la std::asctime().
Definition: solve.cc:114
std::vector< long > axes
Image dimensions.
Definition: solve.cc:39
Command line options and arguments.
Definition: CommandLine.h:47
int df_gray(const gsl_vector *x, void *p, gsl_matrix *J)
Observation fitting function Jacobian.
Definition: solve.cc:83
std::string getString(char shortName, const std::string &defValue) const
Retrieves unique string option.
Definition: CommandLine.cc:392
#define MU_DEBUG(src, msg,...)
Log a message at level Log::DEBUG.
Definition: Log.h:68
int f_gray(const gsl_vector *x, void *p, gsl_vector *f)
Observation fitting function.
Definition: solve.cc:59
Package namespace.
Definition: Detector.cc:13
constexpr double m_H
Mass of hygdrogen atom (g).
Definition: manticore.h:29
void solveStage1(const std::vector< mapDataType > &bandData, const std::vector< mapDataType > &bandError, const mu::CommandLine &cli, const GrayData &gdata0, unsigned id, unsigned nThreads)
Stage 1 solver.
Definition: solve.cc:138
std::vector< double > Ferr
Flux uncertainty in each band.
Definition: solve.cc:41
int fdf_gray(const gsl_vector *x, void *p, gsl_vector *f, gsl_matrix *J)
Observation fitting function plus Jacobian.
Definition: solve.cc:109
std::vector< double > dnu0
Effective bandwidth.
Definition: solve.cc:41
std::vector< mapDataType > * outMap
Output maps.
Definition: solve.cc:49
MathUtils package.
Definition: CommandLine.cc:10
PACS detector class.
Definition: PACS.h:17
Detector base class.
Definition: Detector.h:50
Graybody emission model.
Definition: Graybody.h:33
std::vector< const Detector * > band
Band detectors.
Definition: solve.cc:46
Graybody * gray
Graybody emission model.
Definition: solve.cc:37
SPIRE detector class.
Definition: SPIRE.h:14
#define MU_INFO(src, msg,...)
Log a message at level Log::INFO.
Definition: Log.h:100
#define MU_INFO_IF(cond, src, msg,...)
Conditionally log a message at level Log::INFO.
Definition: Log.h:110
Observation fitting data structure.
Definition: solve.cc:27
constexpr int mapDataCode
Output FITS maps data code (cf. mapDataType).
Definition: manticore.h:14
GrayData(Graybody *g, std::vector< mapDataType > *o, size_t n)
Constructor.
Definition: solve.cc:29
long getInteger(char shortName) const
Retrieves unique decimal integer option.
Definition: CommandLine.cc:341
double getDouble(char shortName, double defValue) const
Retrieves unique floating point option.
Definition: CommandLine.cc:376
std::valarray< float > mapDataType
Output FITS maps data type (cf. mapDataCode).
Definition: manticore.h:11
Dust model.
Definition: Dust.h:28