28 #include "gsl/gsl_interp.h" 
   29 #include "gsl/gsl_interp2d.h" 
   30 #include "gsl/gsl_errno.h" 
   37 using namespace std::string_literals;
 
   39 #define LSST_CHECK_GSL(type, status) \ 
   40     if (status) throw LSST_EXCEPT(type, "GSL error: "s + ::gsl_strerror(status))
 
   57 class IdentityTransmissionCurve : 
public TransmissionCurve {
 
   59     static constexpr 
char const* NAME = 
"IdentityTransmissionCurve";
 
   73     void sampleAt(
lsst::geom::Point2D const&, ndarray::Array<double const, 1, 1> 
const& wavelengths,
 
   74                   ndarray::Array<double, 1, 1> 
const& out)
 const override {
 
   78     bool isPersistable() 
const noexcept 
override { 
return true; }
 
   84         return shared_from_this();
 
   95     void write(OutputArchiveHandle& handle)
 const override { handle.saveEmpty(); }
 
   97     class Factory : 
public table::io::PersistableFactory {
 
  100                                                      CatalogVector 
const& catalogs)
 const override {
 
  105         Factory() : table::io::PersistableFactory(NAME) {}
 
  111     IdentityTransmissionCurve() = 
default;
 
  137 template <
typename T>
 
  140 template <
typename T>
 
  141 GslPtr<T> makeGslPtr(
T* p, 
void (*
free)(
T*)) {
 
  145     return GslPtr<T>(p, 
free);
 
  156     static constexpr 
bool isSpatiallyConstant = 
true;
 
  157     static constexpr 
char const* NAME = 
"SpatiallyConstantTransmissionCurve";
 
  162     Impl1d(ndarray::Array<double, 1, 1> 
const& throughput, ndarray::Array<double, 1, 1> 
const& wavelengths)
 
  163             : _throughput(throughput),
 
  164               _wavelengths(wavelengths),
 
  165               _interp(makeGslPtr(::gsl_interp_alloc(::gsl_interp_linear, _wavelengths.size()),
 
  166                                  &::gsl_interp_free)) {
 
  167         int status = ::gsl_interp_init(_interp.get(), _wavelengths.getData(), _throughput.getData(),
 
  168                                        _wavelengths.size());
 
  176     static void setupPersistence(table::Schema& 
schema, ArrayKeyVector& 
keys) {
 
  177         keys.push_back(
schema.addField<table::Array<double>>(
"throughput", 
"array of known throughput values",
 
  179         keys.push_back(
schema.addField<table::Array<double>>(
 
  180                 "wavelengths", 
"array of known wavelength values", 
"angstrom", 0));
 
  183     void persist(table::BaseRecord& record, ArrayKeyVector 
const& 
keys)
 const {
 
  184         record.set(
keys[0], _throughput);
 
  185         record.set(
keys[1], _wavelengths);
 
  188     static Impl1d unpersist(table::BaseRecord& record, ArrayKeyVector 
const& 
keys) {
 
  189         return Impl1d(record[
keys[0]], record[
keys[1]]);
 
  196                 : _accel(makeGslPtr(::gsl_interp_accel_alloc(), &gsl_interp_accel_free)) {}
 
  198         double operator()(Impl1d 
const& parent, 
double wavelength) {
 
  200             int status = ::gsl_interp_eval_e(parent._interp.get(), parent._wavelengths.getData(),
 
  201                                              parent._throughput.getData(), wavelength, _accel.get(), &
result);
 
  207         GslPtr<::gsl_interp_accel> _accel;
 
  211     ndarray::Array<double, 1, 1> _throughput;
 
  212     ndarray::Array<double, 1, 1> _wavelengths;
 
  213     GslPtr<::gsl_interp> _interp;
 
  222     static constexpr 
bool isSpatiallyConstant = 
false;
 
  223     static constexpr 
char const* NAME = 
"RadialTransmissionCurve";
 
  225     Impl2d(ndarray::Array<double, 1, 1> 
const& throughput, ndarray::Array<double, 1, 1> 
const& wavelengths,
 
  226            ndarray::Array<double, 1, 1> 
const& radii)
 
  227             : _throughput(throughput),
 
  228               _wavelengths(wavelengths),
 
  231                       ::gsl_interp2d_alloc(::gsl_interp2d_bilinear, _wavelengths.size(), _radii.size()),
 
  232                       &::gsl_interp2d_free)) {
 
  233         int status = ::gsl_interp2d_init(_interp.get(), _wavelengths.getData(), _radii.getData(),
 
  234                                          _throughput.getData(), _wavelengths.size(), _radii.size());
 
  242     static void setupPersistence(table::Schema& 
schema, ArrayKeyVector& 
keys) {
 
  243         keys.push_back(
schema.addField<table::Array<double>>(
 
  245                 "flattenned 2-d array of known throughput values, with radius dimension fastest", 
"", 0));
 
  246         keys.push_back(
schema.addField<table::Array<double>>(
 
  247                 "wavelengths", 
"array of known wavelength values", 
"angstrom", 0));
 
  248         keys.push_back(
schema.addField<table::Array<double>>(
"radii", 
"array of known radius values", 
"", 0));
 
  251     void persist(table::BaseRecord& record, ArrayKeyVector 
const& 
keys)
 const {
 
  252         record.set(
keys[0], _throughput);
 
  253         record.set(
keys[1], _wavelengths);
 
  254         record.set(
keys[2], _radii);
 
  257     static Impl2d unpersist(table::BaseRecord& record, ArrayKeyVector 
const& 
keys) {
 
  258         return Impl2d(record[
keys[0]], record[
keys[1]], record[
keys[2]]);
 
  265                 : _radius(point.
asEigen().norm()),
 
  266                   _radiusAccel(makeGslPtr(::gsl_interp_accel_alloc(), &gsl_interp_accel_free)),
 
  267                   _wavelengthAccel(makeGslPtr(::gsl_interp_accel_alloc(), &gsl_interp_accel_free)) {
 
  268             _radius = 
std::max(_radius, parent._radii.front());
 
  269             _radius = 
std::min(_radius, parent._radii.back());
 
  272         double operator()(Impl2d 
const& parent, 
double wavelength) {
 
  275                     ::gsl_interp2d_eval_e(parent._interp.get(), parent._wavelengths.getData(),
 
  276                                           parent._radii.getData(), parent._throughput.getData(), wavelength,
 
  277                                           _radius, _radiusAccel.get(), _wavelengthAccel.get(), &
result);
 
  284         GslPtr<::gsl_interp_accel> _radiusAccel;
 
  285         GslPtr<::gsl_interp_accel> _wavelengthAccel;
 
  289     ndarray::Array<double, 1, 1> _throughput;
 
  290     ndarray::Array<double, 1, 1> _wavelengths;
 
  291     ndarray::Array<double, 1, 1> _radii;
 
  292     GslPtr<::gsl_interp2d> _interp;
 
  295 template <
typename Impl>
 
  296 class InterpolatedTransmissionCurve : 
public TransmissionCurve {
 
  299             : _atBounds(throughputAtBounds), _impl(
std::move(impl)) {
 
  301             throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
 
  302                               "Throughput values at bounds must be finite");
 
  310     void sampleAt(
lsst::geom::Point2D const& point, ndarray::Array<double const, 1, 1> 
const& wavelengths,
 
  311                   ndarray::Array<double, 1, 1> 
const& out)
 const override {
 
  312         LSST_THROW_IF_NE(wavelengths.getSize<0>(), out.getSize<0>(), pex::exceptions::LengthError,
 
  313                          "Length of wavelength array (%d) does not match size of output array (%d)");
 
  314         typename Impl::Functor functor(_impl, point);
 
  315         auto bounds = _impl.getWavelengthBounds();
 
  316         auto wlIter = wavelengths.begin();
 
  317         for (
auto outIter = out.begin(); outIter != out.end(); ++outIter, ++wlIter) {
 
  318             double& 
y = *outIter;
 
  319             if (*wlIter < bounds.first) {
 
  321             } 
else if (*wlIter > bounds.second) {
 
  322                 y = _atBounds.second;
 
  324                 y = functor(_impl, *wlIter);
 
  329     bool isPersistable() 
const noexcept 
override { 
return true; }
 
  334         if (_impl.isSpatiallyConstant) {
 
  335             return shared_from_this();
 
  337             return TransmissionCurve::_transformedByImpl(
transform);
 
  343     struct PersistenceHelper {
 
  349         static PersistenceHelper 
const& get() {
 
  350             static PersistenceHelper 
const instance;
 
  358                           schema.addField<double>(
"throughputAtMin", 
"throughput below minimum wavelength")),
 
  360                           schema.addField<double>(
"throughputAtMax", 
"throughput above minimum wavelength")) {
 
  365     void write(OutputArchiveHandle& handle)
 const override {
 
  366         auto const& 
keys = PersistenceHelper::get();
 
  367         auto catalog = handle.makeCatalog(
keys.schema);
 
  368         auto record = catalog.addNew();
 
  369         record->set(
keys.throughputAtMin, _atBounds.first);
 
  370         record->set(
keys.throughputAtMax, _atBounds.second);
 
  371         _impl.persist(*record, 
keys.arrays);
 
  372         handle.saveCatalog(catalog);
 
  375     class Factory : 
public table::io::PersistableFactory {
 
  378                                                      CatalogVector 
const& catalogs)
 const override {
 
  379             auto const& 
keys = PersistenceHelper::get();
 
  382             auto& record = catalogs.front().front();
 
  383             return std::make_shared<InterpolatedTransmissionCurve>(
 
  384                     Impl::unpersist(record, 
keys.arrays),
 
  388         Factory() : table::io::PersistableFactory(Impl::NAME) {}
 
  398 template <
typename Impl>
 
  401 template class InterpolatedTransmissionCurve<Impl1d>;
 
  402 template class InterpolatedTransmissionCurve<Impl2d>;
 
  410 class ProductTransmissionCurve : 
public TransmissionCurve {
 
  412     static constexpr 
char const* NAME = 
"ProductTransmissionCurve";
 
  419         auto aWavelengthBounds = _a->getWavelengthBounds();
 
  420         auto bWavelengthBounds = _b->getWavelengthBounds();
 
  421         auto aThroughputAtBounds = _a->getThroughputAtBounds();
 
  422         auto bThroughputAtBounds = _b->getThroughputAtBounds();
 
  424         auto determineWavelengthBound = [](
double aWavelength, 
double bWavelength, 
double aThroughput,
 
  425                                            double bThroughput, 
auto isFirstOuter) -> 
double {
 
  429             if (isFirstOuter(aWavelength, bWavelength)) {
 
  430                 return (bThroughput == 0.0) ? bWavelength : aWavelength;
 
  432                 return (aThroughput == 0.0) ? aWavelength : bWavelength;
 
  436         return std::make_pair(determineWavelengthBound(aWavelengthBounds.first, bWavelengthBounds.first,
 
  437                                                        aThroughputAtBounds.first, bThroughputAtBounds.first,
 
  439                               determineWavelengthBound(aWavelengthBounds.second, bWavelengthBounds.second,
 
  440                                                        aThroughputAtBounds.second, bThroughputAtBounds.second,
 
  445         auto aAtBounds = _a->getThroughputAtBounds();
 
  446         auto bAtBounds = _b->getThroughputAtBounds();
 
  447         return std::make_pair(aAtBounds.first * bAtBounds.first, aAtBounds.second * bAtBounds.second);
 
  450     void sampleAt(
lsst::geom::Point2D const& position, ndarray::Array<double const, 1, 1> 
const& wavelengths,
 
  451                   ndarray::Array<double, 1, 1> 
const& out)
 const override {
 
  452         _a->sampleAt(position, wavelengths, out);
 
  453         ndarray::Array<double, 1, 1> tmp = ndarray::allocate(wavelengths.getSize<0>());
 
  454         _b->sampleAt(position, wavelengths, tmp);
 
  458     bool isPersistable() const noexcept
 override { 
return _a->isPersistable() && _b->isPersistable(); }
 
  463     struct PersistenceHelper {
 
  468         static PersistenceHelper 
const& get() {
 
  469             static PersistenceHelper 
const instance;
 
  476                   a(
schema.addField<int>(
"a", 
"archive ID of first operand")),
 
  477                   b(
schema.addField<int>(
"b", 
"archive ID of second operand")) {}
 
  480     class Factory : 
public table::io::PersistableFactory {
 
  483                                                      CatalogVector 
const& catalogs)
 const override {
 
  484             auto const& 
keys = PersistenceHelper::get();
 
  487             auto const& record = catalogs.front().front();
 
  488             return std::make_shared<ProductTransmissionCurve>(
 
  489                     archive.get<TransmissionCurve>(record.get(
keys.a)),
 
  490                     archive.get<TransmissionCurve>(record.get(
keys.b)));
 
  493         Factory() : table::io::PersistableFactory(NAME) {}
 
  496     void write(OutputArchiveHandle& handle)
 const override {
 
  497         auto const& 
keys = PersistenceHelper::get();
 
  498         auto catalog = handle.makeCatalog(
keys.schema);
 
  499         auto record = catalog.addNew();
 
  500         record->set(
keys.a, handle.put(_a));
 
  501         record->set(
keys.b, handle.put(_b));
 
  502         handle.saveCatalog(catalog);
 
  520 class TransformedTransmissionCurve : 
public TransmissionCurve {
 
  522     static constexpr 
char const* NAME = 
"TransformedTransmissionCurve";
 
  531         return _nested->getThroughputAtBounds();
 
  534     void sampleAt(
lsst::geom::Point2D const& position, ndarray::Array<double const, 1, 1> 
const& wavelengths,
 
  535                   ndarray::Array<double, 1, 1> 
const& out)
 const override {
 
  536         return _nested->sampleAt(_transform->applyInverse(position), wavelengths, out);
 
  539     bool isPersistable() const noexcept
 override {
 
  540         return _nested->isPersistable() && _transform->isPersistable();
 
  547         return std::make_shared<TransformedTransmissionCurve>(_nested, 
transform->then(*_transform));
 
  552     struct PersistenceHelper {
 
  557         static PersistenceHelper 
const& get() {
 
  558             static PersistenceHelper 
const instance;
 
  565                   nested(
schema.addField<int>(
"nested", 
"archive ID of the nested TransmissionCurve")),
 
  566                   transform(
schema.addField<int>(
"transform", 
"archive ID of the coordinate transform")) {}
 
  569     void write(OutputArchiveHandle& handle)
 const override {
 
  570         auto const& 
keys = PersistenceHelper::get();
 
  571         auto catalog = handle.makeCatalog(
keys.schema);
 
  572         auto record = catalog.addNew();
 
  573         record->set(
keys.nested, handle.put(_nested));
 
  574         record->set(
keys.transform, handle.put(_transform));
 
  575         handle.saveCatalog(catalog);
 
  578     class Factory : 
public table::io::PersistableFactory {
 
  581                                                      CatalogVector 
const& catalogs)
 const override {
 
  582             auto const& 
keys = PersistenceHelper::get();
 
  585             auto const& record = catalogs.front().front();
 
  586             return std::make_shared<TransformedTransmissionCurve>(
 
  587                     archive.get<TransmissionCurve>(record.get(
keys.nested)),
 
  591         Factory() : table::io::PersistableFactory(NAME) {}
 
  610     return IdentityTransmissionCurve::get();
 
  614         ndarray::Array<double const, 1> 
const& throughput, ndarray::Array<double const, 1> 
const& wavelengths,
 
  616     ::gsl_set_error_handler_off();
 
  618                      "Length of wavelength array (%d) does not match size of throughput array (%d)");
 
  619     return std::make_shared<InterpolatedTransmissionCurve<Impl1d>>(
 
  620             Impl1d(ndarray::copy(throughput), ndarray::copy(wavelengths)),
 
  625         ndarray::Array<double const, 2> 
const& throughput, ndarray::Array<double const, 1> 
const& wavelengths,
 
  627     ::gsl_set_error_handler_off();
 
  630             "Length of wavelength array (%d) does not match first dimension of of throughput array (%d)");
 
  633             "Length of radii array (%d) does not match second dimension of of throughput array (%d)");
 
  637     ndarray::Array<double, 2, 2> throughputTransposed = ndarray::allocate(throughput.getShape().reverse());
 
  638     throughputTransposed.transpose() = throughput;
 
  639     ndarray::Array<double, 1, 1> throughputFlat = ndarray::flatten<1>(throughputTransposed);
 
  640     return std::make_shared<InterpolatedTransmissionCurve<Impl2d>>(
 
  641             Impl2d(throughputFlat, ndarray::copy(wavelengths), ndarray::copy(radii)),
 
  647     auto a = shared_from_this();
 
  649     auto result = 
a->_multipliedByImpl(
b);
 
  664 ndarray::Array<double, 1, 1> TransmissionCurve::sampleAt(
 
  665         lsst::geom::Point2D const& position, ndarray::Array<double const, 1, 1> 
const& wavelengths)
 const {
 
  666     ndarray::Array<double, 1, 1> out = ndarray::allocate(wavelengths.getSize<0>());
 
  667     sampleAt(position, wavelengths, out);
 
  673     return std::make_shared<TransformedTransmissionCurve>(shared_from_this(), 
std::move(
transform));
 
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
 
#define LSST_CHECK_GSL(type, status)
 
table::Key< double > throughputAtMax
 
table::Key< double > throughputAtMin
 
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
 
#define LSST_THROW_IF_NE(N1, N2, EXC_CLASS, MSG)
Check whether the given values are equal, and throw an LSST Exception if they are not.
 
A spatially-varying transmission curve as a function of wavelength.
 
EigenVector const  & asEigen() const noexcept(IS_ELEMENT_NOTHROW_COPYABLE)
Return a fixed-size Eigen representation of the coordinate object.
 
Reports attempts to exceed implementation-defined length limits for some classes.
 
Transform< Point2Endpoint, Point2Endpoint > TransformPoint2ToPoint2
 
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
 
std::string getPythonModule() const override
 
std::string getPersistenceName() const override
 
void write(OutputArchiveHandle &handle) const override
 
A base class for image defects.
 
T shared_from_this(T... args)
 
std::shared_ptr< table::io::Persistable > read(table::io::InputArchive const &archive, table::io::CatalogVector const &catalogs) const override