Program Listing for File source.cpp

Return to documentation for file (src/source.cpp)

#include <cmath>
#include <algorithm>
#include "source.h"
#include <CCfits/CCfits>
#include <helper.h>
#include <source.h>
#define FMT_HEADER_ONLY

#include <fmt/format.h>


// integration routine
template<typename Method, typename F, typename Float>
double integrate(F f, Float a, Float b, int steps, Method m) {
    double s = 0;
    double h = (b - a) / steps;
    for (int i = 0; i < steps; ++i)
        s += m(f, a + h * i, h);
    return h * s;
}

// methods
class rectangular {
public:
    enum position_type {
        left, middle, right
    };

    rectangular(position_type pos) : position(pos) {}

    template<typename F, typename Float>
    double operator()(F f, Float x, Float h) const {
        switch (position) {
            case left:
                return f(x);
            case middle:
                return f(x + h / 2);
            case right:
                return f(x + h);
        }
    }

private:
    const position_type position;
};

Source::Source() : integration_steps(10), shift(1.), list_like(true) {
}

Source::~Source() {

}

std::vector<double> Source::get_interpolated_spectral_density(std::vector<double> wavelength) {
    if (!this->list_like) {
        std::vector<double> spectrum;
        std::vector<double> diff;

        diff.reserve(wavelength.size());
        for (int i = 0; i < wavelength.size() - 2; i++)
            diff.push_back(this->shift * (wavelength[i + 1] - wavelength[i]));
        diff.push_back(diff.back());
        diff.push_back(diff.back());

        auto f = [this](double wl) { return this->get_spectral_density(wl); };

        for (int i = 0; i < wavelength.size(); i++) {
            double a = this->shift * wavelength[i] - diff[i] / 2.;
            double b = this->shift * wavelength[i] + diff[i] / 2.;
//            double result2 = this->integral_s(a, b, this->integration_steps) / diff[i];
            double result = s_val * integrate(f, a, b, integration_steps, rectangular(rectangular::middle)) / diff[i];
            spectrum.push_back(result);
        }

        return spectrum;
    } else {
        std::vector<double> spectrum;

        spectrum.reserve(wavelength.size());
        for (double wl : wavelength) {
            spectrum.push_back(get_spectral_density(wl));
        }

        return spectrum;
    }
}

std::vector<double> Source::get_photon_flux(std::vector<double> wavelength) {
    if (!this->list_like) {
        // convert microwatts / micrometer to photons / s per wavelength intervall
        double ch_factor = 5.03E12;
        std::vector<double> flux;
        std::vector<double> diff;

        for (int i = 0; i < wavelength.size() - 2; i++) {
            diff.push_back(wavelength[i + 1] - wavelength[i]);
        }
        diff.push_back(diff.back());
        diff.push_back(diff.back());
        // expected to be microwatts/micrometer
        std::vector<double> spectrum = this->get_interpolated_spectral_density(wavelength);

        for (int i = 0; i < wavelength.size(); i++) {
            flux.push_back(spectrum[i] * wavelength[i] * ch_factor * diff[i]);
        }

        return flux;
    } else {
        std::vector<double> spectrum;

        for (int i = 0; i < wavelength.size(); i++) {
            // expected to be photons / s for list like sources
            spectrum.push_back(get_spectral_density(wavelength[i]));
        }

        return spectrum;
    }
}

void Source::set_integration_steps(int n) {
    this->integration_steps = n;
}

double Source::integral_s(double a, double b, int n) {
    double step = (b - a) / n;  // width of each small rectangle
    double area = 0.0;  // signed area
    for (int i = 0; i < n; i++) {
        area += this->get_spectral_density(a + (i + 0.5) * step) * step; // sum up each small rectangle
    }
    return area;
}

void Source::set_doppler_shift(double shift) {
    this->shift = 1. + shift / 2.99792458E8;
}


double Constant::get_spectral_density(double wavelength) {
    return this->value;
}

Constant::Constant(double value) : value(value) {
    list_like = false;
    name = fmt::format("Constant {0}", value);
}

Constant::Constant() {
    this->value = 0.01; // uW per um (micro watts per micro meter)
}

Blackbody::Blackbody(double T, double magnitude, double telescope_area) : StellarSource(magnitude, telescope_area),
                                                                          T(T) {
    this->list_like = false;
    name = fmt::format("Blackbody T: {0}, mag: {1}, telescope: {2}", T, magnitude, telescope_area);
    min_w = 0; // Maybe set a cutoff based off intensity?
    max_w = 10;

    calc_flux_scale();

}

double Blackbody::planck(const double &T, const double &wavelength) {
    const double hPlanck = 6.62606896e-34; //J * s;
    const double speedOfLight = 2.99792458e8; //m / s;
    const double kBoltzmann = 1.3806504e-23; //J / K
    double a = 2.0 * hPlanck * speedOfLight * speedOfLight;
    double b = hPlanck * speedOfLight / (wavelength * kBoltzmann * T);
    double c = 1E0; // Conversion factor for intensity
    double intensity = c * a / (pow(wavelength, 5) * (exp(b) - 1.0)); // (J / s ) / m^3 -> (uW) / ( m^2 * um )
    return intensity;
}

double Blackbody::get_spectral_density(double wavelength) {
    return this->planck(this->T, wavelength / 1E6);
}

PhoenixSpectrum::PhoenixSpectrum(std::string spectrum_file, std::string wavelength_file, double magnitude,
                                 double telescope_area) : StellarSource(magnitude, telescope_area) {
    list_like = false;
    name = fmt::format("Phoenix file: {0}, mag: {1}, telescope: {2}", spectrum_file, magnitude, telescope_area);
    this->read_spectrum(spectrum_file, wavelength_file);
    calc_flux_scale();
}

void PhoenixSpectrum::read_spectrum(std::string spectrum_file, std::string wavelength_file) {
// read in wavelength file
    std::unique_ptr<CCfits::FITS> ptr_FITS_wl(new CCfits::FITS(wavelength_file, CCfits::Read, true));
    CCfits::PHDU &wl = ptr_FITS_wl->pHDU();
    std::valarray<double> contents_wl;
    wl.readAllKeys();
    wl.read(contents_wl);

    std::unique_ptr<CCfits::FITS> ptr_FITS_spectrum(new CCfits::FITS(spectrum_file, CCfits::Read, true));
    CCfits::PHDU &spec = ptr_FITS_spectrum->pHDU();
    std::valarray<double> contents_spec;
    spec.readAllKeys();
    spec.read(contents_spec);
    // find wavelength limits
    // convert contents_spec from erg/s/cm^2/cm to uW/m^2/um
    for (long j = 0; j < contents_wl.size() - 1; ++j) {
        this->data[contents_wl[j] / 10000.] = 0.1 * contents_spec[j];
    }
}

double PhoenixSpectrum::get_spectral_density(double wavelength) {
    return interpolate(this->data, wavelength);
}

CoehloSpectrum::CoehloSpectrum(std::string spectrum_file, double magnitude, double telescope_area)
        : StellarSource(magnitude, telescope_area) {
    this->read_spectrum(std::move(spectrum_file));
    list_like = false;
    name = fmt::format("Coehlo spectrum: {0}, mag: {1}, telescope: {2}", spectrum_file, magnitude, telescope_area);
    calc_flux_scale();
}

void CoehloSpectrum::read_spectrum(std::string spectrum_file) {
    std::unique_ptr<CCfits::FITS> ptr_FITS_spectrum(new CCfits::FITS(spectrum_file, CCfits::Read, true));
    CCfits::PHDU &spec = ptr_FITS_spectrum->pHDU();
    std::valarray<double> contents_spec;
    spec.readAllKeys();
    spec.read(contents_spec);
    long ax1(spec.axis(0));
    long max_idx = ax1;
    long min_idx = 0;
    std::unique_ptr<CCfits::FITS> pInfile(new CCfits::FITS(spectrum_file, CCfits::Read));
    double minwl;
    pInfile->pHDU().readKey("CRVAL1", minwl);

    double wld;
    pInfile->pHDU().readKey("CDELT1", wld);

    // find wavelength limits
    // convert contents_spec from erg/s/cm^2/A to uW/s/m^2/um
    for (long j = min_idx; j < max_idx; ++j) {
        this->data[(minwl + j * wld) / 10000.] = (1E7) * contents_spec[j];
    }

}

double CoehloSpectrum::get_spectral_density(double wavelength) {

    return interpolate(this->data, wavelength);

}

CustomSpectrum::CustomSpectrum(double magnitude, double telescope_area, const std::string spectrum_file,
                               std::string wave_file) : StellarSource(magnitude, telescope_area) {
    list_like = false;
    name = fmt::format("custom : {0}, mag: {1}, telescope: {2}", spectrum_file, magnitude, telescope_area);
    read_spectrum(spectrum_file, wave_file);
    calc_flux_scale();

}

void CustomSpectrum::read_spectrum(const std::string spectrum_file, std::string wave_file) {
    std::unique_ptr<CCfits::FITS> ptr_FITS_wl(new CCfits::FITS(wave_file, CCfits::Read, true));
    CCfits::PHDU &wl = ptr_FITS_wl->pHDU();
    std::valarray<double> contents_wl;
    wl.readAllKeys();
    wl.read(contents_wl);

    long ax1(wl.axis(0));
    long max_idx = ax1;
    long min_idx = 0;
    double min_wavelength_angstrom = min_w * 10000.;
    double max_wavelength_angstrom = max_w * 10000.;

    // find wavelength limits
    for (long j = 0; j < ax1; ++j) {
        if (contents_wl[j] < min_wavelength_angstrom)
            min_idx = j;
        if (contents_wl[j] > max_wavelength_angstrom)
            max_idx = j;
    }

    std::unique_ptr<CCfits::FITS> ptr_FITS_spectrum(new CCfits::FITS(spectrum_file, CCfits::Read, true));
    CCfits::PHDU &spec = ptr_FITS_spectrum->pHDU();
    std::valarray<double> contents_spec;
    spec.readAllKeys();
    spec.read(contents_spec);
    std::unique_ptr<CCfits::FITS> pInfile(new CCfits::FITS(spectrum_file, CCfits::Read));

    // find wavelength limits
    // convert contents_spec from erg/s/cm^2/A to uW/s/m^2/um
    for (long j = min_idx; j < max_idx; ++j) {
        this->data[contents_wl[j] / 10000.] = (1E7) * contents_spec[j];
    }
}

double CustomSpectrum::get_spectral_density(double wavelength) {
    return interpolate(this->data, wavelength);
}

LineList::LineList(const std::string linelist_file) {
    name = fmt::format("LineList file: {0}", linelist_file);
    list_like = true;
    this->read_spectrum(linelist_file);
}

void LineList::read_spectrum(std::string linelist_file) {
    std::ifstream file(linelist_file.c_str());
    for (CSVIterator loop(file); loop != CSVIterator(); ++loop) {
        std::cout << (*loop)[0] << std::endl;
        event.push_back(stod((*loop)[0]));
        intensity.push_back(stod((*loop)[1]));
        this->data.insert(std::pair<double, double>(stod((*loop)[0]), stod((*loop)[1])));
    }
}

std::vector<double> LineList::get_interpolated_spectral_density(std::vector<double> wavelength) {
    std::vector<double> spectrum;
    for (auto const &wl: wavelength) {
        spectrum.push_back(double(data[wl]));
    }
    return spectrum;
}

double LineList::get_spectral_density(double wavelength) {
    return data[wavelength];
}

std::vector<double> LineList::get_wavelength() {
    std::vector<double> wavelength;
    for (auto m: this->data) {
        wavelength.push_back(m.first * shift);
    }
    return wavelength;
}

CalibrationSource::CalibrationSource() {
    stellar_source = false;
}

StellarSource::StellarSource(double magnitude, double telescope_area) : mag(magnitude), telescope_area(telescope_area) {

}

void StellarSource::calc_flux_scale() {
    // V-band filter
    std::vector<double> v_filter_wl{0.47, 0.48, 0.49, 0.5, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.6,
                                    0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.7};
    std::vector<double> v_filter_tp{0, 0.03, 0.163, 0.458, 0.78, 0.967, 1, 0.973, 0.898, 0.792, 0.684, 0.574, 0.461,
                                    0.359, 0.27, 0.197, 0.135, 0.081, 0.045, 0.025, 0.017, 0.013, 0.009, 0};
    std::map<double, double> v_filter;
    for (size_t i = 0; i < v_filter_wl.size(); ++i)
        v_filter[v_filter_wl[i]] = v_filter_tp[i];
    s_val = 1.0;

    // get total flux in filter range
    std::vector<double> wl;
    double lower_wl_limit = std::max(min_w, v_filter_wl.front());
    double upper_wl_limit = std::min(max_w, v_filter_wl.back());

    int n = 1000000;
    double step = (upper_wl_limit - lower_wl_limit) / n;
    for (int i = 0; i < n; ++i)
        wl.push_back(lower_wl_limit + step * i);

    std::vector<double> spec = this->get_interpolated_spectral_density(wl);

    // get flux * V-filter
    double total_flux = 0.;
    for (int i = 0; i < n; i++) {
        total_flux += spec[i] * interpolate(v_filter, lower_wl_limit + step * i) * step;
    }

    s_val = pow(10, mag / (-2.5)) * v_zp / total_flux * telescope_area;
}


IdealEtalon::IdealEtalon(double d, double n, double theta, double R, double I) : d(d / 1000.), n(n), theta(theta),
                                                                                 R(R), I(I) {
    this->cF = this->coefficient_of_finesse(R);
    this->integration_steps = 10;
    this->list_like = false;
}

double IdealEtalon::coefficient_of_finesse(double R) {
    return 4. * R / ((1. - R) * (1. - R));
}

double IdealEtalon::T(double wl, double theta, double d, double n, double cF) {
    //delta = (2. * math.pi / wl) * 2. * n * math.cos(theta) * d
    //return 1. / (1. + cF * np.sin(0.5 * delta) ** 2)

    double delta = (2. * M_PI * n * cos(theta) * d) / wl;
    double sind = sin(delta);
    return 1. / (1. + cF * sind * sind);
}

double IdealEtalon::get_local_efficiency(double wavelength) {
    return this->T(wavelength / 1E6, theta, d, n, cF);
}

double IdealEtalon::get_spectral_density(double wavelength) {
    return I * get_local_efficiency(wavelength);
}