33 namespace algorithms {
37 struct CoaddInputData {
45 class CoaddTransmissionCurve :
public afw::image::TransmissionCurve {
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,
67 (
boost::format(
"No Wcs for input with ID %d") % record.getId()).str());
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(),
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;
192 "archive ID for this sensor's TransmissionCurve")),
195 "archive ID for this sensor's ValidPolygon")),
199 "relative weight for this sensor in the average")) {}
203 auto const&
keys = PersistenceHelper::get();
204 auto mainCat = handle.makeCatalog(
keys.mainSchema);
205 auto mainRecord = mainCat.addNew();
206 mainRecord->set(
keys.coaddWcs, handle.put(_coaddWcs));
207 mainRecord->set(
keys.wavelengthMin, _wavelengthBounds.first);
208 mainRecord->set(
keys.wavelengthMax, _wavelengthBounds.second);
209 mainRecord->set(
keys.throughputAtMin, _throughputAtBounds.first);
210 mainRecord->set(
keys.throughputAtMax, _throughputAtBounds.second);
211 handle.saveCatalog(mainCat);
212 auto inputDataCat = handle.makeCatalog(
keys.inputDataSchema);
213 for (
auto const& input : _inputs) {
214 auto inputDataRecord = inputDataCat.addNew();
215 inputDataRecord->set(
keys.transmission, handle.put(input.transmission));
216 inputDataRecord->set(
keys.sensorWcs, handle.put(input.sensorWcs));
217 inputDataRecord->set(
keys.validPolygon, handle.put(input.validPolygon));
218 inputDataRecord->set(
keys.bbox, input.bbox);
219 inputDataRecord->set(
keys.weight, input.weight);
221 handle.saveCatalog(inputDataCat);
227 CatalogVector
const& catalogs)
const override {
228 auto const&
keys = PersistenceHelper::get();
232 auto const& mainRecord = catalogs.front().front();
234 inputs.
reserve(catalogs.back().size());
235 for (
auto const& inputDataRecord : catalogs.back()) {
236 CoaddInputData input = {
237 archive.get<afw::image::TransmissionCurve>(inputDataRecord.get(
keys.transmission)),
238 archive.get<afw::geom::SkyWcs>(inputDataRecord.get(
keys.sensorWcs)),
239 archive.get<afw::geom::polygon::Polygon>(inputDataRecord.get(
keys.validPolygon)),
240 inputDataRecord.get(
keys.bbox), inputDataRecord.get(
keys.weight)};
243 return std::make_shared<CoaddTransmissionCurve>(
244 archive.get<afw::geom::SkyWcs>(mainRecord.get(
keys.coaddWcs)),
259 return std::make_shared<CoaddTransmissionCurve>(
coaddWcs, inputSensors);