Citlali
Loading...
Searching...
No Matches
fitting.h
Go to the documentation of this file.
1#pragma once
2
3#include <Eigen/Core>
4
5#include <tula/algorithm/ei_stats.h>
6
10
11namespace engine_utils {
12
13class mapFitter {
14public:
15 std::shared_ptr<spdlog::logger> logger = spdlog::get("citlali_logger");
16
17 enum FitMode {
20 };
21
22 // number of parameters
23 int n_params = 6;
24
25 // box around source fit
28
29 // fitting limits from config file
30 Eigen::VectorXd flux_limits, fwhm_limits;
31
32 // flux lower limit factor
33 double flux_low = 0.1;
34 // flux upper limit factor
35 double flux_high = 2.0;
36
37 // fwhm lower limit factor
38 double fwhm_low = 0.1;
39 // fwhm upper limit factor
40 double fwhm_high = 2.0;
41
42 // fit rotation angle?
44
45 //lower limit on rotation angle
46 double angle_low = -pi/2;
47 // upper limit on rotation angle
48 double angle_high = pi/2;
49
50 template <typename Model, typename Derived>
51 auto ceres_fit(const Model &,
52 const typename Model::InputType &,
53 const typename Model::InputDataType &,
54 const typename Model::DataType &,
55 const typename Model::DataType &,
56 const Eigen::DenseBase<Derived> &);
57
58 template <mapFitter::FitMode fit_mode, typename Derived>
59 auto fit_to_gaussian(Eigen::DenseBase<Derived> &, Eigen::DenseBase<Derived> &,
60 double, double, double);
61};
62
63template <typename Model, typename Derived>
64auto mapFitter::ceres_fit(const Model &model,
65 const typename Model::InputType &init_params,
66 const typename Model::InputDataType &xy_data,
67 const typename Model::DataType &z_data,
68 const typename Model::DataType &sigma,
69 const Eigen::DenseBase<Derived> &limits) {
70
71 // fitter
73 Fitter* fitter = new Fitter(&model, z_data.size());
74 Eigen::Map<const typename Model::InputDataType> _x(xy_data.data(), xy_data.rows(), xy_data.cols());
75 Eigen::Map<const typename Fitter::ValueType> _y(z_data.data(), z_data.size());
76 Eigen::Map<const typename Fitter::ValueType> _s(sigma.data(), sigma.size());
77
78 // get x, y, and sigma
79 fitter->xdata = &_x;
80 fitter->ydata = &_y;
81 fitter->sigma = &_s;
82
83 // define cost function
84 CostFunction* cost_function =
85 new AutoDiffCostFunction<Fitter, Fitter::ValuesAtCompileTime, Fitter::InputsAtCompileTime>(fitter, fitter->values());
86
87 // parameter vector
88 Eigen::VectorXd params(init_params);
89 auto problem = fitter->createProblem(params.data());
90
91 // including CauchyLoss(0.5) leads to large covariances.
92 problem->AddResidualBlock(cost_function, nullptr, params.data());
93 //problem->AddResidualBlock(cost_function, new ceres::CauchyLoss(0.5), params.data());
94
95 // set limits
96 for (int i=0; i<limits.rows(); ++i) {
97 problem->SetParameterLowerBound(params.data(), i, limits(i,0));
98 problem->SetParameterUpperBound(params.data(), i, limits(i,1));
99 }
100
101 // vector to store indices of parameters to keep constant
102 if (!fit_angle) {
103 std::vector<int> sspv;
104 sspv.push_back(limits.rows()-1);
105 // mark parameter as constant
106 if (sspv.size() > 0 ){
107 ceres::SubsetParameterization *pcssp
108 = new ceres::SubsetParameterization(limits.rows(), sspv);
109 problem->SetParameterization(params.data(), pcssp);
110 }
111 }
112
113 // apply solver options
114 Solver::Options options;
115 // method
116 options.linear_solver_type = ceres::LinearSolverType::DENSE_QR;
117 // disable logging
118 options.logging_type = ceres::LoggingType::SILENT;
119 // disable output
120 options.minimizer_progress_to_stdout = false;
121 // output info
122 Solver::Summary summary;
123 // run the fit
124 Solve(options, problem.get(), &summary);
125
126 // vector for storing uncertainties
127 Eigen::VectorXd uncertainty(params.size());
128
129 // get uncertainty if solution is usable
130 if (summary.IsSolutionUsable()) {
131 // set covariance options
132 Covariance::Options covariance_options;
133 // EIGEN_SPARSE and DENSE_SVD are the slower, but more accurate options
134 covariance_options.sparse_linear_algebra_library_type = ceres::SparseLinearAlgebraLibraryType::EIGEN_SPARSE;
135 covariance_options.algorithm_type = ceres::CovarianceAlgorithmType::DENSE_SVD;
136 // gets rid of error messages related to bad fits
137 covariance_options.null_space_rank = -1;
138 // create covariance object with current covariance options
139 Covariance covariance(covariance_options);
140
141 // set up covariance block
142 std::vector<std::pair<const double*, const double*>> covariance_blocks;
143 // populate covariance block
144 covariance_blocks.push_back(std::make_pair(params.data(), params.data()));
145 // compute covariance
146 auto covariance_result = covariance.Compute(covariance_blocks, problem.get());
147
148 // if covariance calculation suceeded
149 if (covariance_result) {
150 // for storing covariance matrix
151 Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> covariance_matrix;
152 covariance_matrix.resize(params.size(),params.size());
153 covariance.GetCovarianceBlock(params.data(),params.data(),covariance_matrix.data());
154 // calculate uncertainty
155 uncertainty = covariance_matrix.diagonal().cwiseSqrt();
156 }
157 // if covariance calculation fails, set uncertainty to zero
158 else {
159 uncertainty.setConstant(0);
160 }
161 }
162 // if fit is bad, set both fit and uncertainty to zero
163 else {
164 params.setConstant(0);
165 uncertainty.setConstant(0);
166 }
167
168 return std::tuple<Eigen::VectorXd, Eigen::VectorXd,bool>(params,uncertainty,summary.IsSolutionUsable());
169}
170
171template <mapFitter::FitMode fit_mode, typename Derived>
172auto mapFitter::fit_to_gaussian(Eigen::DenseBase<Derived> &signal, Eigen::DenseBase<Derived> &weight,
173 double init_fwhm, double init_row, double init_col) {
174
175 // initial parameters and limits
176 Eigen::VectorXd init_params(n_params);
177 Eigen::MatrixXd limits(n_params,2);
178
179 // intiial position and flux
180 double init_flux = 0;
181
182 // initial gaussian standard deviation
183 double init_sigma = init_fwhm*FWHM_TO_STD;
184
185 // if no initial position is input find peak
186 if (init_row<0 && init_col<0) {
187 // center positions
188 double center_row = (signal.rows() - 1)/2;
189 double center_col = (signal.cols() - 1)/2;
190
191 // signal-to-noise map
192 auto sig2noise = signal.derived().array()*sqrt(weight.derived().array());
193
194 Eigen::Index ir, ic;
195
196 // find peak in the entire map
197 if (fitting_region_pix <= 0) {
198 sig2noise.maxCoeff(&ir, &ic);
199 init_flux = signal(ir,ic);
200 }
201 // find peak within inner radius
202 else {
203 for (Eigen::Index i=0; i<sig2noise.rows(); ++i) {
204 for (Eigen::Index j=0; j<sig2noise.cols(); ++j) {
205 auto dist = sqrt(pow(i - center_row,2) + pow(j - center_col,2));
206 if (dist < fitting_region_pix) {
207 if (sig2noise(i,j) > init_flux) {
208 init_flux = sig2noise(i,j);
209 ir = i;
210 ic = j;
211 }
212 }
213 }
214 }
215 // initial guess for flux
216 init_flux = signal(ir, ic);
217 }
218
219 init_row = ir;
220 init_col = ic;
221 }
222 // otherwise use the input initial position
223 else {
224 init_flux = signal(static_cast<int>(init_row), static_cast<int>(init_col));
225 }
226
227 // initial parameter guesses (order of positions is x,y = col,row)
228 init_params << init_flux, init_col, init_row, init_sigma, init_sigma, 0;
229
230 // limits of bounding box
231 double lower_row, lower_col, upper_row, upper_col;
232
233 // ignore bounding box if less than/equal to zero
234 if (bounding_box_pix <= 0) {
235 lower_row = 0;
236 upper_row = 0;
237
238 upper_row = signal.rows() - 1;
239 upper_col = signal.cols() - 1;
240 }
241 // determine bounding box size
242 else {
243 // ensure lower limits of bounding box are not less than zero
244 lower_row = std::max(init_row - bounding_box_pix, 0.0);
245 lower_col = std::max(init_col - bounding_box_pix, 0.0);
246
247 // ensure upper limits of bounding box are not bigger than the map
248 upper_row = std::min(init_row + bounding_box_pix, static_cast<double>(signal.rows()) - 1);
249 upper_col = std::min(init_col + bounding_box_pix, static_cast<double>(signal.cols()) - 1);
250 }
251
252 // size of bounding box region
253 double n_rows = upper_row - lower_row + 1;
254 double n_cols = upper_col - lower_col + 1;
255
256 // set lower limits of fitting parameters
257 limits.col(0) << flux_low*init_flux, lower_col, lower_row, fwhm_low*init_sigma,
258 fwhm_low*init_sigma, angle_low;
259
260 // set upper limits of fitting parameters
261 limits.col(1) << flux_high*init_flux, upper_col, upper_row, fwhm_high*init_sigma,
262 fwhm_high*init_sigma, angle_high;
263
264 Eigen::VectorXd x, y;
265
266 // axes coordinate vectors for meshgrid
267 x = Eigen::VectorXd::LinSpaced(n_cols, lower_col, upper_col);
268 y = Eigen::VectorXd::LinSpaced(n_rows, lower_row, upper_row);
269
270 // create gaussian 2d model
271 auto g = create_model<Gaussian2D>(init_params);
272 // get meshgrid
273 auto xy = g.meshgrid(x, y);
274
275 // get map stddev
276 double map_sigma = engine_utils::calc_std_dev(signal);
277
278 // standard deviation of signal map
279 Eigen::MatrixXd sigma(weight.rows(), weight.cols());
280
281 // loop through pixels
282 for (Eigen::Index i=0; i<weight.rows(); ++i) {
283 for (Eigen::Index j=0; j<weight.cols(); ++j) {
284 if (weight(i,j)!=0) {
285 // use map sigma for beammaps
286 if constexpr (fit_mode == FitMode::beammap) {
287 sigma(i,j) = map_sigma;
288 }
289 // use weights for pointing
290 else if constexpr (fit_mode == FitMode::pointing) {
291 sigma(i,j) = 1./sqrt(weight(i,j));
292 }
293 }
294 else {
295 sigma(i,j) = 0;
296 }
297 }
298 }
299
300 // copy data and sigma within bounding box region
301 Eigen::MatrixXd _signal = signal.block(lower_row, lower_col, n_rows, n_cols);
302 Eigen::MatrixXd _sigma = sigma.block(lower_row, lower_col, n_rows, n_cols);
303
304 // do the fit and return
305 return ceres_fit(g, init_params, xy, _signal, _sigma, limits);
306}
307} //namespace engine_utils
Definition fitting.h:13
double bounding_box_pix
Definition fitting.h:26
auto fit_to_gaussian(Eigen::DenseBase< Derived > &, Eigen::DenseBase< Derived > &, double, double, double)
Definition fitting.h:172
int n_params
Definition fitting.h:23
double fwhm_low
Definition fitting.h:38
double flux_low
Definition fitting.h:33
double fitting_region_pix
Definition fitting.h:27
bool fit_angle
Definition fitting.h:43
auto ceres_fit(const Model &, const typename Model::InputType &, const typename Model::InputDataType &, const typename Model::DataType &, const typename Model::DataType &, const Eigen::DenseBase< Derived > &)
Definition fitting.h:64
double angle_low
Definition fitting.h:46
FitMode
Definition fitting.h:17
@ beammap
Definition fitting.h:19
@ pointing
Definition fitting.h:18
double angle_high
Definition fitting.h:48
std::shared_ptr< spdlog::logger > logger
Definition fitting.h:15
double fwhm_high
Definition fitting.h:40
double flux_high
Definition fitting.h:35
Eigen::VectorXd fwhm_limits
Definition fitting.h:30
Eigen::VectorXd flux_limits
Definition fitting.h:30
#define FWHM_TO_STD
Definition constants.h:48
constexpr auto pi
Definition constants.h:6
Definition fitting.h:11
auto calc_std_dev(Eigen::DenseBase< DerivedA > &data)
Definition utils.h:336
Definition gauss_models.h:241
Eigen::Matrix< Scalar, InputsAtCompileTime, 1 > InputType
Definition gauss_models.h:26
Definition gauss_models.h:210
const Eigen::Map< const typename Fitter::ValueType > * sigma
Definition gauss_models.h:222
const Eigen::Map< const typename Fitter::ValueType > * ydata
Definition gauss_models.h:221
const Eigen::Map< const typename Model::InputDataType > * xdata
Definition gauss_models.h:220
Definition gauss_models.h:54
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic > DataType
Definition gauss_models.h:59
Eigen::Matrix< double, Eigen::Dynamic, ND > InputDataType
Definition gauss_models.h:60