33 namespace algorithms {
37 struct CoaddInputData {
45 class CoaddTransmissionCurve :
public afw::image::TransmissionCurve {
49 : _coaddWcs(coaddWcs),
52 _throughputAtBounds(0.0, 0.0) {
53 _inputs.reserve(inputSensors.size());
54 afw::table::Key<double> weightKey = inputSensors.getSchema()[
"weight"];
55 double weightSum = 0.0;
56 for (
auto const& record : inputSensors) {
57 double const weight = record.get(weightKey);
62 pex::exceptions::InvalidParameterError,
63 (
boost::format(
"No TransmissionCurve for input with ID %d") % record.getId()).
str());
65 if (record.getWcs() ==
nullptr) {
66 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
70 auto const inputWavelengthBounds =
transmission->getWavelengthBounds();
71 _wavelengthBounds.first =
std::min(_wavelengthBounds.first, inputWavelengthBounds.first);
72 _wavelengthBounds.second =
std::max(_wavelengthBounds.second, inputWavelengthBounds.second);
74 auto const inputThroughputAtBounds =
transmission->getThroughputAtBounds();
75 _throughputAtBounds.first += inputThroughputAtBounds.first *
weight;
76 _throughputAtBounds.second += inputThroughputAtBounds.second *
weight;
78 CoaddInputData input = {
transmission, record.getWcs(), record.getValidPolygon(),
79 geom::Box2D(record.getBBox()), weight};
82 _throughputAtBounds.first /= weightSum;
83 _throughputAtBounds.second /= weightSum;
91 _wavelengthBounds(
std::move(wavelengthBounds)),
92 _throughputAtBounds(
std::move(throughputAtBounds)),
96 CoaddTransmissionCurve(CoaddTransmissionCurve
const&) =
delete;
97 CoaddTransmissionCurve(CoaddTransmissionCurve&&) =
delete;
98 CoaddTransmissionCurve& operator=(CoaddTransmissionCurve
const&) =
delete;
99 CoaddTransmissionCurve& operator=(CoaddTransmissionCurve&&) =
delete;
105 void sampleAt(
geom::Point2D const& position, ndarray::Array<double const, 1, 1>
const& wavelengths,
106 ndarray::Array<double, 1, 1>
const& out)
const override {
107 auto const coord = _coaddWcs->pixelToSky(position);
108 ndarray::Array<double, 1, 1> tmp = ndarray::allocate(out.getShape());
111 double weightSum = 0.0;
112 for (
auto const& input : _inputs) {
113 geom::Point2D const inputPosition = input.sensorWcs->skyToPixel(coord);
114 if (!input.bbox.contains(inputPosition)) {
117 if (input.validPolygon && !input.validPolygon->contains(inputPosition)) {
121 input.transmission->sampleAt(inputPosition, wavelengths, tmp);
122 tmp.deep() *= input.weight;
124 weightSum += input.weight;
126 if (weightSum == 0.0) {
127 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
128 (
boost::format(
"No input TransmissionCurves at point (%s, %s)") %
129 position.getX() % position.getY())
132 out.deep() /= weightSum;
135 bool isPersistable()
const noexcept
override {
136 for (
auto const& input : _inputs) {
137 if (!input.transmission->isPersistable()) {
145 std::string getPersistenceName()
const override {
return "CoaddTransmissionCurve"; }
147 std::string getPythonModule()
const override {
return "lsst.meas.algorithms"; }
149 struct PersistenceHelper;
153 void write(OutputArchiveHandle& handle)
const override;
163 struct CoaddTransmissionCurve::PersistenceHelper {
175 afw::table::Key<double>
weight;
177 static PersistenceHelper
const&
get() {
178 static PersistenceHelper
const instance;
185 coaddWcs(mainSchema.addField<
int>(
"coaddWcs",
"archive ID for the coadd's WCS")),
186 wavelengthMin(mainSchema.addField<
double>(
"wavelengthMin",
"getWavelengthBounds().min")),
187 wavelengthMax(mainSchema.addField<
double>(
"wavelengthMax",
"getWavelengthBounds().max")),
188 throughputAtMin(mainSchema.addField<
double>(
"throughputAtMin",
"throughputAtBounds().first")),
189 throughputAtMax(mainSchema.addField<
double>(
"throughputAtMax",
"throughputAtBounds().second")),
191 transmission(inputDataSchema.addField<
int>(
"transmission",
192 "archive ID for this sensor's TransmissionCurve")),
193 sensorWcs(inputDataSchema.addField<
int>(
"sensorWcs",
"archive ID for this sensor's WCS")),
194 validPolygon(inputDataSchema.addField<
int>(
"validPolygon",
195 "archive ID for this sensor's ValidPolygon")),
198 weight(inputDataSchema.addField<
double>(
"weight",
199 "relative weight for this sensor in the average")) {
200 mainSchema.getCitizen().markPersistent();
201 inputDataSchema.getCitizen().markPersistent();
205 void CoaddTransmissionCurve::write(OutputArchiveHandle& handle)
const {
206 auto const&
keys = PersistenceHelper::get();
207 auto mainCat = handle.makeCatalog(
keys.mainSchema);
208 auto mainRecord = mainCat.addNew();
209 mainRecord->set(
keys.coaddWcs, handle.put(_coaddWcs));
210 mainRecord->set(
keys.wavelengthMin, _wavelengthBounds.first);
211 mainRecord->set(
keys.wavelengthMax, _wavelengthBounds.second);
212 mainRecord->set(
keys.throughputAtMin, _throughputAtBounds.first);
213 mainRecord->set(
keys.throughputAtMax, _throughputAtBounds.second);
214 handle.saveCatalog(mainCat);
215 auto inputDataCat = handle.makeCatalog(
keys.inputDataSchema);
216 for (
auto const& input : _inputs) {
217 auto inputDataRecord = inputDataCat.addNew();
218 inputDataRecord->set(
keys.transmission, handle.put(input.transmission));
219 inputDataRecord->set(
keys.sensorWcs, handle.put(input.sensorWcs));
220 inputDataRecord->set(
keys.validPolygon, handle.put(input.validPolygon));
221 inputDataRecord->set(
keys.bbox, input.bbox);
222 inputDataRecord->set(
keys.weight, input.weight);
224 handle.saveCatalog(inputDataCat);
230 CatalogVector
const& catalogs)
const override {
231 auto const&
keys = PersistenceHelper::get();
235 auto const& mainRecord = catalogs.front().front();
237 inputs.
reserve(catalogs.back().size());
238 for (
auto const& inputDataRecord : catalogs.back()) {
239 CoaddInputData input = {
240 archive.get<afw::image::TransmissionCurve>(inputDataRecord.get(
keys.transmission)),
241 archive.get<afw::geom::SkyWcs>(inputDataRecord.get(
keys.sensorWcs)),
242 archive.get<afw::geom::polygon::Polygon>(inputDataRecord.get(
keys.validPolygon)),
243 inputDataRecord.get(
keys.bbox), inputDataRecord.get(
keys.weight)};
246 return std::make_shared<CoaddTransmissionCurve>(
247 archive.get<afw::geom::SkyWcs>(mainRecord.get(
keys.coaddWcs)),
262 return std::make_shared<CoaddTransmissionCurve>(
coaddWcs, inputSensors);
BoxKey< lsst::geom::Box2D > Box2DKey
std::shared_ptr< afw::geom::SkyWcs const > sensorWcs
std::shared_ptr< afw::image::TransmissionCurve const > makeCoaddTransmissionCurve(std::shared_ptr< afw::geom::SkyWcs const > coaddWcs, afw::table::ExposureCatalog const &inputSensors)
Create a TransmissionCurve that represents the effective throughput on a coadd.
afw::table::Key< int > coaddWcs
afw::table::Schema mainSchema
afw::table::Key< double > wavelengthMin
afw::table::Key< double > wavelengthMax
afw::table::Schema inputDataSchema
std::shared_ptr< afw::image::TransmissionCurve const > transmission
Point< double, 2 > Point2D
A base class for image defects.
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
std::shared_ptr< afw::geom::polygon::Polygon const > validPolygon
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
afw::table::Key< double > throughputAtMin
afw::table::Key< double > throughputAtMax
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Custom catalog class for ExposureRecord/Table.
ExposureCatalogT< ExposureRecord > ExposureCatalog
static BoxKey addFields(Schema &schema, std::string const &name, std::string const &doc, std::string const &unit)
Add _min_x, _min_y, _max_x, _max_y fields to a Schema, and return a BoxKey that points to them...