Skip to content

Commit 012bec2

Browse files
committed
Merge develop (#942, #952) into feat/masked-pixels; regenerate params pin
# Conflicts: # tests/workflow/params_pin.json
2 parents 5df27c7 + 138490a commit 012bec2

27 files changed

Lines changed: 919 additions & 688 deletions

‎.github/workflows/cd.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ jobs:
8181
"
8282
8383
- name: Upload built HTML as artifact
84-
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
84+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
8585
with:
8686
name: docs-html-${{ steps.slug.outputs.slug }}
8787
path: docs/_build

‎Dockerfile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# syntax=docker/dockerfile:1.27
1+
# syntax=docker/dockerfile:1.28
22
#
33
# One image: CI tests it, canfar batch jobs run it, downstream stacks (e.g.
44
# sp_validation) build FROM it. Everyday CLI tools and all extras (test,

‎astra.yaml‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ inputs:
6969
outputs:
7070
- id: final_cat
7171
type: data
72-
format: fits
72+
format: hdf5
7373
description: >-
7474
Per-tile shear catalogue family, the terminal science product
7575
(make_cat_runner).
@@ -733,7 +733,9 @@ analyses:
733733
admits its upper endpoint, column 2080. [HARDCODED] The comparisons
734734
are strict in make_post_process; CCD_SIZE sets only the bounds. A
735735
WCS inversion failure skips that CCD, lowering N_EPOCH, which caps
736-
how many exposures enter each galaxy's multi-epoch fit.
736+
how many exposures enter each galaxy's multi-epoch fit. This geometric
737+
count remains internal; the final catalogue's N_EPOCH counts only
738+
epochs with a validated PSF.
737739
Values:
738740
SEXTRACTOR_RUNNER.CCD_SIZE = 33,2080,1,4612;
739741
SEXTRACTOR_RUNNER.MAKE_POST_PROCESS = True.
@@ -1173,6 +1175,12 @@ analyses:
11731175
22-star floor to that accepted count. In the science path the CCD's
11741176
epoch is dropped for every object on it, and an object left with no
11751177
epoch has no shape.
1178+
The final catalogue's N_EPOCH counts the epochs with an
1179+
interpolated PSF (for psfex, those passing this gate; for mccd,
1180+
those with a model). Only this count is written as N_EPOCH: one column
1181+
avoids downstream cuts on the geometric count by mistake.
1182+
PSF-gate rejections are studied per CCD in validation output and
1183+
the exposure maps (#887), rather than per object.
11761184
The pipeline has no minimum-epoch floor: NGMIX_N_EPOCH records what
11771185
survived and epoch-count cuts happen downstream. Guinot+22 discards
11781186
the CCD from PSF estimation rather than gating at interpolation.
@@ -2220,7 +2228,7 @@ analyses:
22202228
outputs:
22212229
- id: tile_final_cat
22222230
type: data
2223-
format: fits
2231+
format: hdf5
22242232
description: The assembled per-tile science catalogue (final_cat family).
22252233
inputs: [ngmix_chunks]
22262234
decisions:

‎example/cfis_image_sims/final_cat.param‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ NGMIX_MCAL_TYPES_FAIL
3131
NGMIX_G1_PSF_ORIG_NOSHEAR
3232
NGMIX_G2_PSF_ORIG_NOSHEAR
3333

34-
# Number of epochs (exposures)
34+
# Number of epochs: N_EPOCH with a validated PSF model,
35+
# NGMIX_N_EPOCH fitted by ngmix
3536
N_EPOCH
3637
NGMIX_N_EPOCH
3738

‎scripts/python/create_final_cat.py‎

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
"""Script create_final_cat.py
44
5-
Create and update hdf5 file of all final ShapePipe output FITS files, runs of
6-
ShapePipe module ``make_catalogue_runner``. Supercedes `merge_final_cat.py`.
5+
Create and update hdf5 file of all per-tile final catalogues, the output of
6+
ShapePipe module ``make_cat_runner`` (hdf5, one dataset per column; FITS for
7+
catalogues written before that format). Supercedes `merge_final_cat.py`.
78
89
Usage: in parent dir of patches:
910
create_final_cat.py -p ~/shapepipe/workflow/config/cfis/final_cat.param -i . -P 7 -v -m final_cat_P7.hdf5
@@ -21,11 +22,12 @@
2122
import tqdm
2223
import glob
2324
import h5py
24-
from astropy.io import fits
2525

2626
from cs_util import args as cs_args
2727
from cs_util import logging
2828

29+
from shapepipe.utilities.final_cat import read_final_cat
30+
2931

3032
def params_from_run_config(params, defaults):
3133
"""Fill unset paths from a workflow run config.
@@ -374,9 +376,14 @@ def get_patch_group(hdf5_file, patch, verbose=False):
374376
return patch_group
375377

376378

377-
def read_data(fits_file, params):
379+
def read_data(cat_file, params):
378380
"""Read the parameter list's columns out of one catalogue.
379381
382+
The catalogue is HDF5 or FITS, decided per file
383+
(``shapepipe.utilities.final_cat.read_final_cat``); a FITS table is read
384+
at HDU ``params["hdu_num"]`` (default 1). Returns the requested columns
385+
and a structured dtype describing them.
386+
380387
@sc [label:schema] read-data-raises-on-missing-column
381388
A requested column the catalogue lacks raises `KeyError` naming it; it is
382389
never skipped or filled. `copy_data` keeps only columns present in the
@@ -385,33 +392,17 @@ def read_data(fits_file, params):
385392
silently narrower, with that slot's exposure identity gone. Enforced by
386393
tests/unit/test_final_cat_merge_invariants.py.
387394
"""
388-
with fits.open(fits_file) as hdu_list:
389-
try:
390-
data = hdu_list[params["hdu_num"]].data
391-
except:
392-
print(f"Error with ID {id}, file{fits_file}")
393-
raise
395+
data = read_final_cat(
396+
cat_file, params["param_list"], hdu=params.get("hdu_num", 1)
397+
)
394398

395399
# If columns not given on input: read all column names
396400
if params["param_list"] is None:
397-
params["param_list"] = [col for col in data.keys()]
398-
399-
# RAISE, do not print and fall through. The bare `except:` this replaces
400-
# left extracted_data and dtype unbound, so the caller's own error was an
401-
# UnboundLocalError from the return statement below, naming neither the
402-
# file nor the column that was actually missing.
403-
present = set(data.dtype.names or ())
404-
missing = [col for col in params["param_list"] if col not in present]
405-
if missing:
406-
raise KeyError(
407-
f"{fits_file}: missing {len(missing)} of the "
408-
f"{len(params['param_list'])} requested column(s): "
409-
f"{' '.join(missing)}"
410-
)
401+
params["param_list"] = list(data.dtype.names)
402+
411403
extracted_data = {col: data[col] for col in params["param_list"]}
412-
dtype = data.dtype
413404

414-
return extracted_data, dtype
405+
return extracted_data, data.dtype
415406

416407

417408
def copy_data(param_list, extracted_data, dtype):
@@ -496,26 +487,36 @@ def collect_tile_ids_image_sims(patch_path):
496487
return result
497488

498489

490+
# make_cat_runner writes hdf5; catalogues made before that are FITS.
491+
FINAL_CAT_SUFFIXES = (".hdf5", ".fits")
492+
493+
499494
def find_final_cat(id, id_path, run_prefix):
500495
"""Path of this tile's final catalogue, or None.
501496
502497
Flat layout first: the unified workflow copies one catalogue per tile to
503-
<products>/tiles/<shard>/<tile>/final_cat-<tile>.fits, in DOT form and with
504-
no run sub-tree. Then the legacy layout, where the catalogue sits under the
505-
tile's own shapepipe run dir in DASH form, newest run wins.
498+
<products>/tiles/<shard>/<tile>/final_cat-<tile>.hdf5, in DOT form and
499+
with no run sub-tree. Then the run-dir layout, where the catalogue sits
500+
under the tile's own shapepipe run dir in DASH form, newest run wins.
501+
Either layout may hold an hdf5 or a FITS catalogue; hdf5 is preferred.
506502
"""
507-
flat = os.path.join(id_path, f"final_cat-{id}.fits")
508-
if os.path.exists(flat):
509-
return flat
503+
for suffix in FINAL_CAT_SUFFIXES:
504+
flat = os.path.join(id_path, f"final_cat-{id}{suffix}")
505+
if os.path.exists(flat):
506+
return flat
510507

511508
base_pattern = os.path.join(id_path, "output", run_prefix)
512509
all_matches = [d for d in glob.glob(base_pattern) if os.path.isdir(d)]
513510
if not all_matches:
514511
return None
515512
newest_dir = max(all_matches, key=os.path.getmtime)
516513
id_dash = re.sub(r"\.", "-", id)
517-
legacy = f"{newest_dir}/make_cat_runner/output/final_cat-{id_dash}.fits"
518-
return legacy if os.path.exists(legacy) else None
514+
for suffix in FINAL_CAT_SUFFIXES:
515+
in_run = (f"{newest_dir}/make_cat_runner/output/"
516+
f"final_cat-{id_dash}{suffix}")
517+
if os.path.exists(in_run):
518+
return in_run
519+
return None
519520

520521

521522
def process(params):
@@ -577,13 +578,13 @@ def process(params):
577578
print(f"Skipping {id} (already processed)")
578579
continue
579580

580-
fits_file = find_final_cat(id, id_path, run_prefix)
581-
if fits_file is None:
581+
cat_file = find_final_cat(id, id_path, run_prefix)
582+
if cat_file is None:
582583
if params["verbose"]:
583584
print(f"Final cat for {id} not found, continuing")
584585
continue
585586

586-
extracted_data, dtype = read_data(fits_file, params)
587+
extracted_data, dtype = read_data(cat_file, params)
587588

588589
structured_data = copy_data(params["param_list"], extracted_data, dtype)
589590

‎scripts/python/merge_final_cat.py‎

Lines changed: 8 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
"""Script merge_final_cat.py
66
77
Merge all final catalogues, created by ShapePipe module
8-
``make_catalogue_runner``, into a joined numpy binary file.
8+
``make_cat_runner`` (HDF5 or FITS, decided per file), into a joined numpy
9+
binary file.
910
1011
:Authors: Axel Guinot, Martin Kilbinger
1112
1213
"""
1314

14-
from astropy.io import fits
1515
import numpy as np
1616
import os
1717
import sys
@@ -21,6 +21,7 @@
2121
from optparse import OptionParser
2222

2323
from shapepipe.utilities import cfis
24+
from shapepipe.utilities.final_cat import read_final_cat
2425

2526

2627
class param:
@@ -118,7 +119,7 @@ def parse_options(p_def):
118119
dest="hdu_num",
119120
type="int",
120121
default=p_def.hdu_num,
121-
help=f"input HDU number, default='{p_def.hdu_num}'",
122+
help=f"input HDU number of FITS catalogues, default='{p_def.hdu_num}'",
122123
)
123124

124125
parser.add_option(
@@ -247,43 +248,6 @@ def read_param_file(path, verbose=False):
247248
return param_list
248249

249250

250-
def get_data(path, hdu_num, param_list):
251-
"""Get Data.
252-
253-
Return data of selected columns from FITS file.
254-
255-
Parameters
256-
----------
257-
path: str
258-
input file name
259-
hdu_num: int
260-
HDU number
261-
param_list: list of str
262-
parameters to be extracted. If none, copy
263-
all columns
264-
265-
Returns
266-
-------
267-
numpy array
268-
data columns
269-
270-
"""
271-
hdu_list = fits.open(path)
272-
hdu = hdu_list[hdu_num]
273-
274-
if param_list:
275-
cols = []
276-
for p in param_list:
277-
cols.append(hdu.columns[p])
278-
coldefs = fits.ColDefs(cols)
279-
hdu_new = fits.BinTableHDU.from_columns(coldefs)
280-
d = hdu_new.data
281-
else:
282-
d = hdu.data
283-
284-
return d
285-
286-
287251
def main(argv=None):
288252

289253

@@ -317,9 +281,9 @@ def main(argv=None):
317281
tile_ID_list = cfis.read_list(param.tile_ID_list_path)
318282

319283
if param.verbose:
320-
print("Find input catalogue FITS files")
284+
print("Find input catalogue files")
321285
l = os.listdir(path=path)
322-
ext = "fits"
286+
ext = (".hdf5", ".fits")
323287
lpath = []
324288
for this_l in l:
325289

@@ -348,7 +312,7 @@ def main(argv=None):
348312
count = 0
349313

350314
# Determine number of columns and keys from first catalogue file
351-
d_tmp = get_data(lpath[0], param.hdu_num, param.param_list)
315+
d_tmp = read_final_cat(lpath[0], param.param_list, hdu=param.hdu_num)
352316
d = np.zeros(d_tmp.shape, dtype=d_tmp.dtype)
353317
for key in d_tmp.dtype.names:
354318
d[key] = d_tmp[key]
@@ -360,7 +324,7 @@ def main(argv=None):
360324
for fname in lpath[1:]:
361325

362326
try:
363-
d_tmp = get_data(fname, param.hdu_num, param.param_list)
327+
d_tmp = read_final_cat(fname, param.param_list, hdu=param.hdu_num)
364328
dd = np.zeros(d_tmp.shape, dtype=d.dtype)
365329

366330
for key in d_tmp.dtype.names:

‎src/shapepipe/modules/make_cat_package/__init__.py‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
previous module runs. This gathers all relevant information on the measured
2222
galaxies for weak-lensing post-processing. This includes galaxy detection and
2323
basic measurement parameters, the PSF model at galaxy positions, and the
24-
shape measurement. Every detected object is kept: the catalogue carries no
24+
shape measurement. ``N_EPOCH`` counts the object's epochs with a validated
25+
PSF model, the epochs ngmix can fit.
26+
The geometric count of covering CCDs is used internally, not written out. Every detected object is kept: the catalogue carries no
2527
star/galaxy classification, which is done downstream. Each object is
2628
tagged with its source tile via the ``TILE_ID`` column and carries the
2729
survey-wide object ID ``TILE_UNIQUE_ID = tile_id * 10**6 + NUMBER``
@@ -43,7 +45,8 @@
4345
(``HSM_*_PSF_n``, ``EXP_ID_n``, ``CCD_n``) when ``SAVE_PSF_DATA`` is
4446
``True``; unfilled slots hold the family's sentinel, and an object with
4547
more epochs than slots raises an error. A fixed count gives every tile
46-
the same schema. Default is the tile's maximum ``N_EPOCH`` plus one
48+
the same schema. Default is the sexcat's maximum geometric ``N_EPOCH`` plus
49+
one
4750
4851
"""
4952

0 commit comments

Comments
 (0)