21
21
from __future__ import annotations
22
22
23
23
import argparse
24
+ import collections
24
25
import gc
25
26
import sys
26
27
import textwrap
@@ -82,14 +83,14 @@ def _maps_alms_internal(
82
83
* ,
83
84
maps_path : str | None = None ,
84
85
alms_path : str | None = None ,
85
- config : str | None = None ,
86
+ config_path : str | None = None ,
86
87
parallel : bool = False ,
87
88
) -> None :
88
89
"""
89
90
Compute maps and/or alms from catalogues.
90
91
"""
91
92
92
- config_loaded = read_config (config )
93
+ config = read_config (config_path )
93
94
94
95
if maps_path is not None :
95
96
maps_out = heracles .io .MapFits (maps_path , clobber = True )
@@ -101,7 +102,7 @@ def _maps_alms_internal(
101
102
else :
102
103
alms_out = None
103
104
104
- for catalog_config in config_loaded .catalogs :
105
+ for catalog_config in config .catalogs :
105
106
base_catalog = heracles .FitsCatalog (catalog_config .source )
106
107
base_catalog .label = catalog_config .label
107
108
@@ -114,7 +115,7 @@ def _maps_alms_internal(
114
115
catalogs [selection .key ] = base_catalog [selection .selection ]
115
116
visibilities [selection .key ] = selection .visibility
116
117
117
- fields = {key : config_loaded .fields [key ] for key in catalog_config .fields }
118
+ fields = {key : config .fields [key ] for key in catalog_config .fields }
118
119
119
120
for key , catalog in catalogs .items ():
120
121
if visibilities [key ] is not None :
@@ -166,21 +167,25 @@ def _maps_alms_internal(
166
167
167
168
def maps (
168
169
path : str ,
169
- config : str | None = None ,
170
+ config_path : str | None = None ,
170
171
parallel : bool = False ,
171
172
) -> None :
172
173
"""map catalogues
173
174
174
175
Create maps from input catalogues.
175
176
176
177
"""
177
- _maps_alms_internal (maps_path = path , config = config , parallel = parallel )
178
+ return _maps_alms_internal (
179
+ maps_path = path ,
180
+ config_path = config_path ,
181
+ parallel = parallel ,
182
+ )
178
183
179
184
180
185
def alms (
181
186
path : str ,
182
- maps : list [str ] | None = None ,
183
- config : str | None = None ,
187
+ maps : list [str ],
188
+ config_path : str | None = None ,
184
189
parallel : bool = False ,
185
190
) -> None :
186
191
"""compute alms from catalogues or maps
@@ -189,12 +194,15 @@ def alms(
189
194
190
195
"""
191
196
# if no maps are given, process directly from catalogues
192
- if maps is None :
193
- _maps_alms_internal (alms_path = path , config = config , parallel = parallel )
194
- return
197
+ if not maps :
198
+ return _maps_alms_internal (
199
+ alms_path = path ,
200
+ config_path = config_path ,
201
+ parallel = parallel ,
202
+ )
195
203
196
204
# load configuration to get fields
197
- config_loaded = read_config (config )
205
+ config = read_config (config_path )
198
206
199
207
# open output FITS
200
208
alms_out = heracles .io .AlmFits (path , clobber = True )
@@ -208,12 +216,46 @@ def alms(
208
216
data = heracles .io .MapFits (maps_path )
209
217
210
218
# transform this fits
211
- heracles .transform (config_loaded .fields , data , out = alms_out )
219
+ heracles .transform (config .fields , data , out = alms_out )
212
220
213
221
# clean up before next iteration
214
222
del data
215
223
216
224
225
+ def spectra (
226
+ path : str ,
227
+ alms : list [str ],
228
+ * ,
229
+ config_path : str | None = None ,
230
+ ) -> None :
231
+ """compute angular power spectra
232
+
233
+ Compute angular power spectra from sets of alms.
234
+
235
+ """
236
+
237
+ # load configuration to get requested spectra
238
+ config = read_config (config_path )
239
+
240
+ # make sure two-point statistics are defined in config
241
+ if not config .spectra :
242
+ raise ValueError (f"{ config_path } : no 'spectra' in config" )
243
+
244
+ # lazy-load all input FITS into a combined mapping
245
+ all_alms = collections .ChainMap ({})
246
+ for alms_path in alms :
247
+ # quick check to see if file is readable
248
+ with open (alms_path ) as _fp :
249
+ pass
250
+
251
+ # add new lazy-loaded FITS to the ChainMap
252
+ # keys will be sorted in the order the files are passed
253
+ all_alms = all_alms .new_child (heracles .io .AlmFits (alms_path , clobber = False ))
254
+
255
+ # compute all spectra combinations
256
+ spectra = {}
257
+
258
+
217
259
def main () -> int :
218
260
"""Main method of the `heracles` command.
219
261
@@ -240,7 +282,12 @@ def add_command(func):
240
282
241
283
# common parser for all subcommands
242
284
cmd_parser = argparse .ArgumentParser (add_help = False )
243
- cmd_parser .add_argument ("-c" , "--config" , help = "configuration file" )
285
+ cmd_parser .add_argument (
286
+ "-c" ,
287
+ "--config" ,
288
+ help = "configuration file" ,
289
+ dest = "config_path" ,
290
+ )
244
291
245
292
# main parser for CLI invokation
246
293
main_parser = argparse .ArgumentParser (
@@ -294,11 +341,26 @@ def add_command(func):
294
341
help = "output FITS file for alms" ,
295
342
)
296
343
alms_parser .add_argument (
297
- "-- maps" ,
298
- action = "append " ,
344
+ "maps" ,
345
+ nargs = "* " ,
299
346
help = "transform pre-computed maps" ,
300
347
)
301
348
349
+ ###########
350
+ # spectra #
351
+ ###########
352
+
353
+ spectra_parser = add_command (spectra )
354
+ spectra_parser .add_argument (
355
+ "path" ,
356
+ help = "output FITS file for spectra" ,
357
+ )
358
+ spectra_parser .add_argument (
359
+ "alms" ,
360
+ nargs = "+" ,
361
+ help = "input FITS file(s) with sets of alms" ,
362
+ )
363
+
302
364
#######
303
365
# run #
304
366
#######
0 commit comments