cphot 0.1
A C++ tool for computing photometry from spectra.
licks.hpp
Go to the documentation of this file.
1 /**
2  * @defgroup LICKS Lick indices
3  * @brief Lick indices calculations
4  *
5  * This package provides function to compute spectral indices.
6  *
7  * We provide a collection of many common indices. The Lick system of spectral
8  * line indices is one of the most commonly used methods of determining ages and
9  * metallicities of unresolved (integrated light) stellar populations.
10  *
11  * The calibration of the Lick/ IDS system is complicated because the original
12  * Lick spectra were not flux calibrated, so there are usually systematic
13  * effects due to differences in continuum shape. Proper calibration involves
14  * observing many of the original Lick/IDS standard stars and deriving offsets
15  * to the standard system.
16  *
17  * references:
18  * - Worthey G., Faber S. M., Gonzalez J. J., Burstein D., 1994, ApJS, 94, 687
19  * - Worthey G., Ottaviani D. L., 1997, ApJS, 111, 377
20  * - Puzia et al. 2002
21  * - Zhang, Li & Han 2005, http://arxiv.org/abs/astro-ph/0508634v1
22  *
23  * @note
24  * In Vazdekis et al. (2010), we propose a new Line Index System, hereafter LIS,
25  * with three new spectral resolutions at which to measure the Lick indices.
26  * Note that this new system should not be restricted to the Lick set of indices
27  * in a flux calibrated system. In fact, LIS can be used for any index in the
28  * literature (e.g., for the Rose (1984) indices), including newly defined
29  * indices (e.g., Cervantes & Vazdekis 2009).
30  * The LIS system is defined for 3 different spectral resolutions which are best
31  * suited for the following astrophysical cases:
32  * - LIS-5.0AA: globular clusters
33  * - LIS-8.4AA: low and intermediate-mass galaxies
34  * - LIS-14.0AA: massive galaxies
35  * Conversions to transform the data from the Lick/IDS system to LIS can be found
36  * with discussion of indices and information in Johansson, Thomas & Maraston (2010)
37  * http://wwwmpa.mpa-garching.mpg.de/~jonasj/milesff/milesff.pdf
38  *
39  */
40 #include <cmath>
41 #include <vector>
42 #include <cphot/rquantities.hpp>
44 
45 namespace cphot{
46 
47 using DMatrix = xt::xarray<double, xt::layout_type::row_major>;
48 
49 /**
50  * @ingroup LICKS
51  * @brief Adapt the resolution of the spectra to match the lick definitions.
52  *
53  * Lick definitions have different resolution elements as function of wavelength.
54  * These definition are hard-coded in this function
55  *
56  * @param w
57  * wavelength definition in Angstrom
58  * @param flux
59  * spectra to convert
60  * @param fwhm0
61  * initial broadening in the spectra `fi`
62  * @param sigma_floor
63  * minimal dispersion to consider
64  * @return flux_red reduced spectra
65  *
66  */
67 DMatrix reduce_resolution(const DMatrix& w, const DMatrix& flux, double fwhm0, double sigma_floor){
68  // all in AA
69  const DMatrix w_lick_res {4000., 4400., 4900., 5400., 6000.}; // Lick resolution anchor points in AA
70  const DMatrix lick_res {11.5, 9.2, 8.4, 8.4, 9.8}; // FWHM in AA
71 
72  // Linear interpolation of lick_res over w
73  // TODO: need to add extrapolation
74  DMatrix res = xt::interp(w, w_lick_res, lick_res);
75 
76  // Compute width from fwhm
77  double constant = 2. * std::sqrt(2. * std::log(2)); // constant that converts fwhm --> sigma
78  DMatrix lick_sigma = xt::sqrt((res * res - fwhm0 * fwhm0)) / constant;
79 
80  // Convolution by g=1/sqrt(2*pi*sigma^2) * exp(-r^2/(2*sigma^2))
81  DMatrix flux_red = xt::zeros<double>({flux.size()});
82 
83  for (size_t i=0; i < lick_sigma.size(); ++i){
84  double sigma = lick_sigma(i);
85  double maxsigma = 3. * sigma;
86  // sampling floor: min (0.2, sigma * 0.1)
87  double delta = std::min(sigma_floor, sigma * 0.1);
88  DMatrix delta_wj = xt::arange(-maxsigma, + maxsigma, delta);
89  auto wj = delta_wj + w[i];
90  auto fluxj = xt::interp(wj, w, flux, 0., 0.);
91  flux_red[i] = xt::sum(fluxj * delta * xt::exp(-0.5 * xt::pow(delta_wj / sigma, 2)))();
92  }
93  flux_red /= lick_sigma * constant;
94  return flux_red;
95 }
96 
97 /**
98  * @ingroup LICKS
99  * @brief Define a Lick Index similarily to a Filter object
100  */
101 class LickIndex{
102 
103  private:
104  std::string name; ///< name of the index
105  double index_band_min; ///< minimal wavelength of index interval
106  double index_band_max; ///< maximal wavelength of index interval
107  double blue_continuum_min; ///< minimal wavelength of blue continuum
108  double blue_continuum_max; ///< maximal wavelength of blue continuum
109  double red_continuum_min; ///< minimal wavelength of red continuum
110  double red_continuum_max; ///< maximal wavelength of red continuum
111  QLength wavelength_unit; ///< wavelength unit (usually Angstrom or nm)
112  bool b_mag; ///< is the index in magnitudes? (or equivalent width)
113  std::string description; ///< description/notes
114 
115  public:
116  LickIndex(const std::string& name, double index_band_min, double index_band_max,
117  double blue_continuum_min, double blue_continuum_max,
118  double red_continuum_min, double red_continuum_max,
119  const QLength & wavelength_unit, const std::string& description,
120  bool is_mag);
121  LickIndex(const cphot_licks::lickdata& data);
122 
123  double get(const DMatrix& w, const DMatrix& flux, const QLength & wavelength_unit);
124 
125  std::string get_name() const;
126  bool is_mag() {return this->b_mag;}
127 
128  void info() const;
129 
130  /*
131  * void get_continuum_normalized_region_around_line(const DMatrix & wi,
132  * const DMatrix & flux);
133  */
134 };
135 
136 /**
137  * @brief Construct a new Lick Index:: Lick Index object
138  *
139  * @param name name of the index
140  * @param index_band_min minimal wavelength of index interval
141  * @param index_band_max maximal wavelength of index interval
142  * @param blue_continuum_min minimal wavelength of blue continuum
143  * @param blue_continuum_max maximal wavelength of blue continuum
144  * @param red_continuum_min minimal wavelength of red continuum
145  * @param red_continuum_max maximal wavelength of red continuum
146  * @param wavelength_unit wavelength unit (usually Angstrom or nm)
147  * @param description description/notes
148  */
149 LickIndex::LickIndex(const std::string& name, double index_band_min, double index_band_max,
150  double blue_continuum_min, double blue_continuum_max,
151  double red_continuum_min, double red_continuum_max,
152  const QLength& wavelength_unit, const std::string& description, bool is_mag)
153  : name(name), index_band_min(index_band_min), index_band_max(index_band_max),
154  blue_continuum_min(blue_continuum_min), blue_continuum_max(blue_continuum_max),
155  red_continuum_min(red_continuum_min), red_continuum_max(red_continuum_max),
156  wavelength_unit(wavelength_unit), description(description), b_mag(is_mag){}
157 
158 /**
159  * @brief Construct a new Lick Index:: Lick Index object
160  *
161  * @param data data from the hardcoded data indices
162  */
164  : name(data.name), index_band_min(data.index_band_min), index_band_max(data.index_band_max),
165  blue_continuum_min(data.blue_continuum_min), blue_continuum_max(data.blue_continuum_max),
166  red_continuum_min(data.red_continuum_min), red_continuum_max(data.red_continuum_max),
167  wavelength_unit(data.wavelength_unit), b_mag(data.is_mag){}
168 
169 /**
170  * @brief Get the name object
171  *
172  * @return std::string
173  */
174 std::string LickIndex::get_name() const {
175  return this->name;
176 }
177 
178 
179 /**
180  * @brief display some information about the index
181  *
182  */
183 void LickIndex::info() const {
184  double wave_conv = this->wavelength_unit.to(nm);
185 
186  std::cout << "Lick Index: " << this->name << "\n"
187  << " Index band: [" << this->index_band_min * wave_conv << ", " << this->index_band_max * wave_conv << "] nm" << "\n"
188  << " Blue continuum: [" << this->blue_continuum_min * wave_conv << ", " << this->blue_continuum_max * wave_conv << "] nm" << "\n"
189  << " Red continuum: [" << this->red_continuum_min * wave_conv << ", " << this->red_continuum_max * wave_conv << "] nm" << "\n"
190  << " Expressed value in " << (this->b_mag ? "magnitude" : "equivalent-width") << "\n";
191 }
192 
193 /*
194  * void LickIndex::get_continuum_normalized_region_around_line(
195  * const DMatrix & wi, const DMatrix & flux
196  * ){
197  * auto ind_cont = xt::argwhere(
198  * ( (wi >= this->blue_continuum_min) && (wi <= this->blue_continuum_max) ) ||
199  * ( (wi >= this->red_continuum_min) && (wi <= this->red_continuum_max) )
200  * );
201  *
202  * auto ind_range = xt::argwhere(
203  * ( (wi > this->index_band_min) && (wi < this->index_band_max) )
204  * );
205  *
206  * const DMatrix & wnew = wi[ind_range];
207  * const DMatrix & wcont = wi[ind_cont];
208  *
209  * // Make a flux array of shape (ind_range.size()))
210  * }
211  */
212 
213 /**
214  * @brief compute spectral index after continuum subtraction
215  *
216  * @param w array of wavelengths in AA
217  * @param flux array of flux values for different spectra in the series
218  * @return double equivalent width or magnitude
219  */
220 double LickIndex::get(const DMatrix& w,
221  const DMatrix& flux,
222  const QLength & wavelength_unit){
223 
224  double wave_conv =wavelength_unit.to(angstrom);
225  DMatrix w_aa = w * wave_conv;
226  return 0;
227 
228 
229 }
230 
231 /**
232  * @brief Nice representation of Filter objects
233  *
234  * @param os stream to output the representation
235  * @param F Filter object
236  * @return std::ostream& same as os
237  */
238 std::ostream & operator<<(std::ostream &os,
239  LickIndex &index){
240  os << "LickIndex: " << index.get_name()
241  << "\n";
242  return os;
243 }
244 
245 /**
246  * @ingroup LICKS
247  * @brief Collection of Lick indices
248  */
250  private:
251  std::vector<LickIndex> licks; ///< registered lick indices
252 
253  public:
254  LickLibrary();
255  std::vector<std::string> get_content();
256  std::vector<std::string> find (const std::string & name,
257  bool case_sensitive=true);
258  LickIndex load_filter(const std::string& filter_name);
259  size_t size(){return this->licks.size();};
260 
261 };
262 
263 /**
264  * @brief Construct a new Lick Library from hard coded definitions
265  *
266  * @see `cphot_licks::lickdefs`
267  *
268  */
270  for (const auto index: cphot_licks::lickdefs){
271  this->licks.push_back(LickIndex(index));
272  }
273 }
274 
275 /**
276  * @brief returns the list of lick indices registered
277  *
278  * @return std::vector<std::string> list of the indices
279  */
280 std::vector<std::string> LickLibrary::get_content(){
281  std::vector<std::string> lst;
282  for (const auto & index: this->licks){
283  lst.push_back(index.get_name());
284  }
285  return lst;
286 }
287 
288 /**
289  * @brief Look for lick index names
290  *
291  * @param name name to look for
292  * @param case_sensitive set if the case needs to be respected (default true)
293  * @return std::vector<std::string> list of potential candidates
294  */
295 std::vector<std::string> LickLibrary::find (const std::string & name,
296  bool case_sensitive){
297 
298  std::vector<std::string> result;
299 
300  if (case_sensitive) {
301  for (const auto & lib_name: this->get_content()) {
302  if (lib_name.find(name) != std::string::npos)
303  result.push_back(lib_name);
304  }
305  } else {
306  std::string name_lower = tolower(name);
307  for (const auto & lib_name: this->get_content()) {
308  if (tolower(lib_name).find(name) != std::string::npos)
309  result.push_back(lib_name);
310  }
311  }
312  return result;
313 }
314 
315 /**
316  * @brief Load a given filter from the library
317  *
318  * @param filter_name normalized names according to the library
319  * @return Filter object
320  */
321 LickIndex LickLibrary::load_filter(const std::string& filter_name){
322  for (const auto & index: this->licks){
323  if (index.get_name() == filter_name){
324  return index;
325  }
326  }
327  throw std::runtime_error("Filter " + filter_name + " not found in library");
328 }
329 
330 /**
331  * @brief Nice representation of LickLibrary
332  *
333  * @param os stream to output the representation
334  * @param F Filter object
335  * @return std::ostream& same as os
336  */
337 std::ostream & operator<<(std::ostream &os,
338  LickLibrary &l){
339  os << "LicLibrary: " << " Contains " << l.size() << " registered indices." << "\n";
340  return os;
341 }
342 
343 }// namespace cphot
cphot::LickLibrary::find
std::vector< std::string > find(const std::string &name, bool case_sensitive=true)
Look for lick index names.
Definition: licks.hpp:295
licks_data.hpp
cphot_sun_theoretical::flux
const std::vector< double > flux
Definition: sun_data.hpp:17
cphot::LickIndex
Define a Lick Index similarily to a Filter object.
Definition: licks.hpp:101
cphot::operator<<
std::ostream & operator<<(std::ostream &os, Filter &F)
Nice representation of Filter objects.
Definition: filter.hpp:165
cphot::LickLibrary::size
size_t size()
Definition: licks.hpp:259
cphot::reduce_resolution
DMatrix reduce_resolution(const DMatrix &w, const DMatrix &flux, double fwhm0, double sigma_floor)
Adapt the resolution of the spectra to match the lick definitions.
Definition: licks.hpp:67
rquantities.hpp
cphot::LickIndex::info
void info() const
display some information about the index
Definition: licks.hpp:183
cphot::LickIndex::is_mag
bool is_mag()
Definition: licks.hpp:126
cphot::DMatrix
xt::xarray< double, xt::layout_type::row_major > DMatrix
Definition: filter.hpp:32
cphot::LickLibrary::load_filter
LickIndex load_filter(const std::string &filter_name)
Load a given filter from the library.
Definition: licks.hpp:321
cphot::LickLibrary::get_content
std::vector< std::string > get_content()
returns the list of lick indices registered
Definition: licks.hpp:280
angstrom
constexpr QLength angstrom
Definition: rquantities.hpp:274
cphot_licks::lickdefs
static const std::vector< lickdata > lickdefs({ {"CN_1", 4142.125, 4177.125, 4080.125, 4117.625, 4244.125, 4284.125, angstrom, true}, {"CN_2", 4142.125, 4177.125, 4083.875, 4096.375, 4244.125, 4284.125, angstrom, true}, {"Ca4227", 4222.250, 4234.750, 4211.000, 4219.750, 4241.000, 4251.000, angstrom, false}, {"G4300", 4281.375, 4316.375, 4266.375, 4282.625, 4318.875, 4335.125, angstrom, false}, {"Fe4383", 4369.125, 4420.375, 4359.125, 4370.375, 4442.875, 4455.375, angstrom, false}, {"Ca4455", 4452.125, 4474.625, 4445.875, 4454.625, 4477.125, 4492.125, angstrom, false}, {"Fe4531", 4514.250, 4559.250, 4504.250, 4514.250, 4560.500, 4579.250, angstrom, false}, {"Fe4668", 4634.000, 4720.250, 4611.500, 4630.250, 4742.750, 4756.500, angstrom, false}, {"H_beta", 4847.875, 4876.625, 4827.875, 4847.875, 4876.625, 4891.625, angstrom, false}, {"Fe5015", 4977.750, 5054.000, 4946.500, 4977.750, 5054.000, 5065.250, angstrom, false}, {"Mg_1", 5069.125, 5134.125, 4895.125, 4957.625, 5301.125, 5366.125, angstrom, true}, {"Mg_2", 5154.125, 5196.625, 4895.125, 4957.625, 5301.125, 5366.125, angstrom, true}, {"Mg_b", 5160.125, 5192.625, 5142.625, 5161.375, 5191.375, 5206.375, angstrom, false}, {"Fe5270", 5245.650, 5285.650, 5233.150, 5248.150, 5285.650, 5318.150, angstrom, false}, {"Fe5335", 5312.125, 5352.125, 5304.625, 5315.875, 5353.375, 5363.375, angstrom, false}, {"Fe5406", 5387.500, 5415.000, 5376.250, 5387.500, 5415.000, 5425.000, angstrom, false}, {"Fe5709", 5696.625, 5720.375, 5672.875, 5696.625, 5722.875, 5736.625, angstrom, false}, {"Fe5782", 5776.625, 5796.625, 5765.375, 5775.375, 5797.875, 5811.625, angstrom, false}, {"Na_D", 5876.875, 5909.375, 5860.625, 5875.625, 5922.125, 5948.125, angstrom, false}, {"TiO_1", 5936.625, 5994.125, 5816.625, 5849.125, 6038.625, 6103.625, angstrom, true}, {"TiO_2", 6189.625, 6272.125, 6066.625, 6141.625, 6372.625, 6415.125, angstrom, true}, {"Hdelta_A", 4083.500, 4122.250, 4041.600, 4079.750, 4128.500, 4161.000, angstrom, false}, {"Hgamma_A", 4319.750, 4363.500, 4283.500, 4319.750, 4367.250, 4419.750, angstrom, false}, {"Hdelta_F", 4091.000, 4112.250, 4057.250, 4088.500, 4114.750, 4137.250, angstrom, false}, {"Hgamma_F", 4331.250, 4352.250, 4283.500, 4319.750, 4354.750, 4384.750, angstrom, false}, {"OIIEW", 3716.3, 3738.3, 3696.3, 3716.3, 3738.3, 3758.3, angstrom, false}, {"bTiO", 4758.500, 4800.000, 4742.750, 4756.500, 4827.875, 4847.875, angstrom, false}, {"HdeltaEW", 4083.5, 4122.250, 4017.0, 4057.0, 4153.0, 4193.0, angstrom, false}, {"NaI", 8163.500, 8229.125, 8140.000, 8163.500, 8230.250, 8250.000, angstrom, false}, {"HgammaEW", 4319.750, 4363.5, 4242.0, 4282.0, 4404.0, 4444.0, angstrom, false}, {"CaH_1", 6357.500, 6401.750, 6342.125, 6356.500, 6408.500, 6429.750, angstrom, true}, {"HbetaEW", 4847.875, 4876.625, 4799.0, 4839.0, 4886.0, 4926.0, angstrom, false}, {"CaH_2", 6775.000, 6900.000, 6510.000, 6539.250, 7017.000, 7064.000, angstrom, true}, {"TiO_3", 7123.750, 7162.500, 7017.000, 7064.000, 7234.000, 7269.000, angstrom, true}, {"TiO_4", 7643.250, 7717.250, 7527.000, 7577.750, 7735.500, 7782.750, angstrom, true}, {"TiOCaH", 6600.000, 6817.000, 6520.000, 6545.000, 7035.000, 7050.000, angstrom, false}, {"TiO3", 6600.000, 6723.000, 6520.000, 6545.000, 7035.000, 7050.000, angstrom, false}, {"CaH", 6775.000, 6817.000, 6520.000, 6545.000, 7035.000, 7050.000, angstrom, false}, {"aTiO", 5445.000, 5600.000, 5420.000, 5442.000, 5630.000, 5655.000, angstrom, true}, {"NaI_V12", 8180.000, 8200.000, 8164.000, 8173.000, 8233.000, 8244.000, angstrom, false}, {"NaI_F13", 8180.000, 8200.000, 8137.000, 8147.000, 8233.000, 8244.000, angstrom, false}, {"CaHK_LB13", 3899.47, 4003.47, 3806.50, 3833.82, 4020.69, 4052.36, angstrom, false}, {"Mg4780", 4760.78, 4798.80, 4738.91, 4757.31, 4819.78, 4835.51, angstrom, false}, {"TiO2SDSS_LB13", 6189.625, 6272.125, 6066.625, 6141.625, 6442.0, 6455.0, angstrom, true}, {"NaI_LB13", 8180.000, 8200.000, 8143.000, 8153.000, 8233.000, 8244.000, angstrom, false}, {"Ca1_LB13", 8484.0, 8513.0, 8474.0, 8484.0, 8563.0, 8577.0, angstrom, false}, {"Ca2_LB13", 8522.0, 8562.0, 8474.0, 8484.0, 8563.0, 8577.0, angstrom, false}, {"Ca3_LB13", 8642.0, 8682.0, 8619.0, 8642.0, 8700.0, 8725.0, angstrom, false}, {"Hbeta0", 4839.275, 4877.097, 4821.175, 4838.404, 4897.445, 4915.845, angstrom, false} })
cphot::LickLibrary::LickLibrary
LickLibrary()
Construct a new Lick Library from hard coded definitions.
Definition: licks.hpp:269
cphot::LickIndex::get
double get(const DMatrix &w, const DMatrix &flux, const QLength &wavelength_unit)
compute spectral index after continuum subtraction
Definition: licks.hpp:220
cphot::LickIndex::get_name
std::string get_name() const
Get the name object.
Definition: licks.hpp:174
cphot::LickIndex::LickIndex
LickIndex(const std::string &name, double index_band_min, double index_band_max, double blue_continuum_min, double blue_continuum_max, double red_continuum_min, double red_continuum_max, const QLength &wavelength_unit, const std::string &description, bool is_mag)
Construct a new Lick Index:: Lick Index object.
Definition: licks.hpp:149
cphot
Definition: filter.hpp:30
tolower
std::string tolower(std::string s)
Returns the lower case of a string.
Definition: helpers.hpp:190
cphot::LickLibrary
Collection of Lick indices.
Definition: licks.hpp:249
nm
constexpr QLength nm
Definition: rquantities.hpp:273
cphot_sun_theoretical::wavelength_unit
const QLength wavelength_unit
Definition: sun_data.hpp:19
cphot_licks::lickdata
Stores the definition of a Lick index.
Definition: licks_data.hpp:26