Line data Source code
1 : // -*- mode: c++ -*-
2 : //# SynthesisImagerMixin.h: Mixin for using SynthesisImager class in parallel
3 : //# imaging framework (ParallelImagerMixin)
4 : //# Copyright (C) 2016
5 : //# Associated Universities, Inc. Washington DC, USA.
6 : //#
7 : //# This library is free software; you can redistribute it and/or modify it
8 : //# under the terms of the GNU Library General Public License as published by
9 : //# the Free Software Foundation; either version 2 of the License, or (at your
10 : //# option) any later version.
11 : //#
12 : //# This library is distributed in the hope that it will be useful, but WITHOUT
13 : //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 : //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
15 : //# License for more details.
16 : //#
17 : //# You should have received a copy of the GNU Library General Public License
18 : //# along with this library; if not, write to the Free Software Foundation,
19 : //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
20 : //#
21 : //# Correspondence concerning AIPS++ should be addressed as follows:
22 : //# Internet email: casa-feedback@nrao.edu.
23 : //# Postal address: AIPS++ Project Office
24 : //# National Radio Astronomy Observatory
25 : //# 520 Edgemont Road
26 : //# Charlottesville, VA 22903-2475 USA
27 : //#
28 : #ifndef SYNTHESIS_IMAGER_MIXIN_H_
29 : #define SYNTHESIS_IMAGER_MIXIN_H_
30 :
31 : #include <casacore/casa/Containers/Record.h>
32 : #include <casacore/casa/Arrays/Array.h>
33 : #include <synthesis/ImagerObjects/MPIGlue.h>
34 : #include <synthesis/ImagerObjects/SynthesisImager.h>
35 : #include <sys/types.h>
36 : #include <sys/stat.h>
37 : #include <algorithm>
38 : #include <memory>
39 : #include <vector>
40 : #include <unistd.h>
41 : #include <cerrno>
42 : #include <cstring>
43 : #include <string>
44 : #include <dirent.h>
45 : #include <system_error>
46 :
47 : namespace casa {
48 :
49 : /**
50 : * Simple mixin class to put SynthesisImager into ParallelImagerMixin framework.
51 : */
52 : template <class T>
53 : class SynthesisImagerMixin
54 : : public T {
55 :
56 : private:
57 : std::unique_ptr<SynthesisImager> si;
58 :
59 : static casacore::Quantity asQuantity(const casacore::Record &rec, const char *field_name);
60 :
61 : static casacore::Quantity asQuantity(const casacore::String &field_name);
62 :
63 : static bool haveCFCache(const std::string &dirname);
64 :
65 : static int isCFS(const struct dirent *d);
66 :
67 : static std::vector<std::string> getCFCacheList(
68 : const SynthesisParamsGrid &grid_pars, int size, int rank);
69 :
70 : void
71 0 : set_weighting(const casacore::Record &weight_pars,
72 : const std::vector<SynthesisParamsImage> &image_pars) {
73 0 : casacore::String type =
74 0 : ((weight_pars.fieldNumber("type") != -1)
75 : ? weight_pars.asString("type")
76 : : casacore::String("natural"));
77 0 : casacore::String rmode =
78 0 : ((weight_pars.fieldNumber("rmode") != -1)
79 : ? weight_pars.asString("rmode")
80 : : casacore::String("norm"));
81 0 : casacore::Double robust =
82 0 : ((weight_pars.fieldNumber("robust") != -1)
83 0 : ? weight_pars.asDouble("robust")
84 : : 0.0);
85 0 : casacore::Int npixels =
86 0 : ((weight_pars.fieldNumber("npixels") != -1)
87 0 : ? weight_pars.asInt("npixels")
88 : : 0);
89 0 : casacore::Bool multifield =
90 0 : ((weight_pars.fieldNumber("multifield") != -1)
91 0 : ? weight_pars.asBool("multifield")
92 : : false);
93 0 : casacore::Bool usecubebriggs =
94 0 : ((weight_pars.fieldNumber("usecubebrigss") != -1)
95 0 : ? weight_pars.asBool("usecubebriggs")
96 : : false);
97 0 : casacore::Quantity noise =
98 0 : ((weight_pars.fieldNumber("noise") != -1)
99 : ? asQuantity(weight_pars, "noise")
100 0 : : casacore::Quantity(0.0, "Jy"));
101 0 : casacore::Quantity field_of_view =
102 0 : ((weight_pars.fieldNumber("fieldofview") != -1)
103 : ? asQuantity(weight_pars, "fieldofview")
104 0 : : casacore::Quantity(0.0, "arcsec"));
105 0 : const casacore::Array<casacore::String> &uv_taper_pars =
106 0 : ((weight_pars.fieldNumber("uvtaper") != -1)
107 : ? weight_pars.asArrayString("uvtaper")
108 : : casacore::Array<casacore::String>());
109 0 : casacore::Quantity bmaj(0.0, "deg"), bmin(0.0, "deg"), bpa(0.0, "deg");
110 0 : casacore::String filter_type("");
111 0 : if (uv_taper_pars.nelements() > 0) {
112 0 : bmaj = asQuantity(uv_taper_pars(casacore::IPosition(1, 0)));
113 0 : filter_type = casacore::String("gaussian");
114 0 : if (uv_taper_pars.nelements() > 1) {
115 0 : bmin = asQuantity(uv_taper_pars(casacore::IPosition(1, 1)));
116 0 : if (uv_taper_pars.nelements() > 2)
117 0 : bpa = asQuantity(uv_taper_pars(casacore::IPosition(1, 2)));
118 : } else /* uv_taper_pars.nelements() == 1 */ {
119 0 : bmin = bmaj;
120 : }
121 : }
122 : // TODO: the following is the logic for setting 'filter_type' in
123 : // synthesisimager_cmpt.cc...verify that the check on uv_taper_pars[0]
124 : // length is not required here
125 : //
126 : // if (uv_taper_pars.nelements() > 0 && uv_taper_pars[0].length() > 0)
127 : // filter_type = casacore::String("gaussian");
128 0 : si->weight(type, rmode, noise, robust, field_of_view, npixels,
129 : multifield, usecubebriggs, filter_type, bmaj, bmin, bpa);
130 0 : if (image_pars.size() == 1
131 0 : && image_pars[0].stokes == casacore::String("I")
132 0 : && weight_pars.asString("type") != casacore::String("natural")) {
133 0 : si->getWeightDensity();
134 0 : T::reduce_weight_density();
135 0 : si->setWeightDensity();
136 : }
137 0 : };
138 :
139 : protected:
140 : void
141 0 : setup_imager(MPI_Comm comm,
142 : std::vector<SynthesisParamsSelect> &select_pars,
143 : std::vector<SynthesisParamsImage> &image_pars,
144 : std::vector<SynthesisParamsGrid> &grid_pars,
145 : casacore::Record &weight_pars) {
146 : // Create a single imager component for every rank in comm.
147 :
148 0 : teardown_imager();
149 0 : int imaging_rank = T::effective_rank(comm);
150 0 : if (imaging_rank == 0) {
151 0 : si = std::unique_ptr<SynthesisImager>(new SynthesisImager());
152 0 : for (auto s : select_pars)
153 0 : si->selectData(s);
154 0 : for (size_t i = 0;
155 0 : i < std::min(image_pars.size(), grid_pars.size());
156 : ++i)
157 0 : si->defineImage(image_pars[i], grid_pars[i]);
158 : }
159 0 : int imaging_size = T::effective_size(comm);
160 0 : if (imaging_rank >= 0 && imaging_size > 1) {
161 0 : SynthesisParamsGrid &grid_pars0 = grid_pars.at(0);
162 0 : if (!haveCFCache(grid_pars0.cfCache)) {
163 0 : if (imaging_rank == 0) {
164 0 : Vector<String> const empty;
165 0 : si->dryGridding(empty);
166 0 : }
167 0 : std::vector<std::string> cf_list =
168 : getCFCacheList(grid_pars0, imaging_size, imaging_rank);
169 0 : if (cf_list.size() > 0) {
170 0 : if (si == nullptr)
171 0 : si = std::unique_ptr<SynthesisImager>(
172 0 : new SynthesisImager());
173 0 : casacore::Vector<casacore::String> const cf_list_casavec(cf_list);
174 0 : si->fillCFCache(
175 0 : cf_list_casavec, grid_pars0.ftmachine, grid_pars0.cfCache,
176 0 : grid_pars0.psTermOn, grid_pars0.aTermOn,grid_pars0.conjBeams);
177 0 : }
178 0 : }
179 : // create new imager instance, scrapping any that already exists
180 0 : si = std::unique_ptr<SynthesisImager>(new SynthesisImager());
181 0 : si->selectData(select_pars.at(imaging_rank));
182 0 : si->defineImage(image_pars.at(imaging_rank),
183 0 : grid_pars.at(imaging_rank));
184 : }
185 0 : if (imaging_rank >= 0)
186 0 : set_weighting(weight_pars, image_pars);
187 0 : };
188 :
189 0 : void teardown_imager() {
190 0 : si.reset();
191 0 : };
192 :
193 : public:
194 : void
195 0 : make_psf() {
196 : // TODO: verify this is correct for all ranks
197 0 : if (si != nullptr) si->makePSF();
198 0 : };
199 :
200 : void
201 0 : execute_major_cycle() {
202 0 : casacore::Record rec;
203 0 : rec.define("lastcycle", T::is_clean_complete());
204 : // TODO: verify this is correct for all ranks
205 0 : if (si != nullptr) si->executeMajorCycle(rec);
206 0 : T::end_major_cycle();
207 0 : };
208 :
209 : void
210 0 : predict_model() {
211 : // TODO: verify this is correct for all ranks
212 0 : if (si != nullptr) si->predictModel();
213 0 : };
214 : };
215 :
216 : // TODO: this method is a utility function...move it into another module?
217 : template <class T>
218 : casacore::Quantity
219 0 : SynthesisImagerMixin<T>::asQuantity(
220 : const casacore::Record &rec, const char *field_name) {
221 0 : casacore::Bool success = false;
222 0 : casacore::QuantumHolder qh;
223 0 : casacore::String err_str;
224 0 : switch (rec.dataType(field_name)) {
225 0 : case casacore::DataType::TpRecord:
226 0 : success = qh.fromRecord(err_str, rec.subRecord(field_name));
227 0 : break;
228 0 : case casacore::DataType::TpString:
229 0 : success = qh.fromString(err_str, rec.asString(field_name));
230 0 : break;
231 0 : default:
232 0 : break;
233 : }
234 0 : if (!(success && qh.isQuantity())) {
235 0 : ostringstream oss;
236 0 : oss << "Error in converting quantity: " << err_str;
237 0 : throw (casacore::AipsError(oss.str()));
238 0 : }
239 0 : return qh.asQuantity();
240 0 : };
241 :
242 : // TODO: this method is a utility function...move it into another module?
243 : template <class T>
244 : casacore::Quantity
245 0 : SynthesisImagerMixin<T>::asQuantity(const casacore::String &field_name) {
246 0 : casacore::QuantumHolder qh;
247 0 : casacore::String err_str;
248 0 : casacore::Bool success = qh.fromString(err_str, field_name);
249 0 : if (!(success && qh.isQuantity())) {
250 0 : ostringstream oss;
251 0 : oss << "Error in converting quantity: " << err_str;
252 0 : throw (casacore::AipsError(oss.str()));
253 0 : }
254 0 : return qh.asQuantity();
255 0 : };
256 :
257 : template <class T>
258 : bool
259 0 : SynthesisImagerMixin<T>::haveCFCache(const std::string &dirname) {
260 : struct stat stat_buf;
261 0 : return (stat(dirname.c_str(), &stat_buf) == 0
262 0 : && S_ISDIR(stat_buf.st_mode));
263 : };
264 :
265 : template <class T>
266 : int
267 0 : SynthesisImagerMixin<T>::isCFS(const struct dirent *d) {
268 0 : std::string name(d->d_name);
269 0 : return name.find("CFS") == 0;
270 0 : };
271 :
272 : template <class T>
273 : std::vector<std::string>
274 0 : SynthesisImagerMixin<T>::getCFCacheList(
275 : const SynthesisParamsGrid &grid_pars, int size, int rank) {
276 : // return vector for all ranks, even if it's empty
277 0 : std::vector<std::string> result;
278 : struct dirent **namelist;
279 0 : int nCFS = scandir(grid_pars.cfCache.c_str(), &namelist,
280 : SynthesisImagerMixin::isCFS, alphasort);
281 0 : if (nCFS >= 0) {
282 0 : size = std::min(size, nCFS);
283 : // Note that with size having been redefined as the minimum of the
284 : // original size value and nCFS, if rank >= size, then no strings
285 : // are added to the result vector in the following loop.
286 0 : for (int n = rank; n < nCFS; n += size) {
287 0 : std::string name(namelist[n]->d_name);
288 0 : result.push_back(name);
289 : }
290 0 : free(namelist);
291 : } else {
292 : // errno == ENOMEM
293 0 : std::error_condition ec(errno, std::generic_category());
294 0 : throw casacore::AipsError(casacore::String("Failed to scan cf cache directory '")
295 0 : + grid_pars.cfCache + casacore::String("': ")
296 0 : + casacore::String(ec.message()));
297 : }
298 0 : return result;
299 0 : };
300 :
301 : } // namespace casa
302 :
303 : #endif // SYNTHESIS_IMAGER_MIXIN_H_
|