aboutsummaryrefslogtreecommitdiffstats
path: root/bin/clean_evt2.py
blob: ee93f367aa361364aa227e30beea677435b47911 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#!/usr/bin/env python3
#
# Copyright (c) 2017 Weitian LI <liweitianux@live.com>
# MIT license

"""
Further process the reprocessed evt2 file to make a cleaned one
for later scientific analysis.

The following steps are carried out:
1. Filter out the chips of interest;
2. Detect (point) sources, visually check and manually update;
3. Remove the updated sources;
4. Extract light curve of the background regions (away from the object);
5. Check and clip the light curve to create a GTI;
6. Remove flares by filtering the GTI to create the finally cleaned evt2.
"""

import os
import argparse
import subprocess
import shutil
import logging

from _context import acispy
from acispy.manifest import get_manifest
from acispy.pfiles import setup_pfiles
from acispy.acis import ACIS
from acispy.ds9 import ds9_view
from acispy.header import write_keyword


logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


def filter_chips(infile, outfile, chips, clobber=False):
    """
    Filter out the chips of interest, e.g., ``ccd_id=7`` for ACIS-S
    observation, and ``ccd_id=0:3`` for ACIS-I observation.
    """
    chips = chips.replace("-", ":")
    clobber = "yes" if clobber else "no"
    logger.info("Filter out chips of interest: %s" % chips)
    logger.info("Outfile: %s" % outfile)
    subprocess.check_call(["punlearn", "dmcopy"])
    subprocess.check_call([
        "dmcopy", "infile=%s[ccd_id=%s]" % (infile, chips),
        "outfile=%s" % outfile, "clobber=%s" % clobber
    ])
    logger.info("Done!\n")


def detect_sources(infile, outfile, clobber=False):
    """
    Detect (point) sources using ``celldetect``; examine the detected
    sources in DS9, and update the source regions and save.
    """
    src_fits = os.path.splitext(outfile)[0] + ".fits"
    clobber = "yes" if clobber else "no"
    logger.info("Detect sources using 'celldetect' ...")
    logger.info("Outfile: %s" % outfile)
    subprocess.check_call(["punlearn", "celldetect"])
    subprocess.check_call([
        "celldetect", "infile=%s" % infile,
        "outfile=%s" % src_fits, "regfile=%s" % outfile,
        "clobber=%s" % clobber
    ])
    os.remove(src_fits)
    shutil.copy(outfile, outfile+".orig")
    logger.warning("Check and update detected source regions; " +
                   "and save/overwrite file: %s" % outfile)
    ds9_view(infile, regfile=outfile)
    logger.info("Done!\n")


def remove_sources(infile, outfile, srcfile, clobber=False):
    """
    Remove detected sources
    """
    clobber = "yes" if clobber else "no"
    logger.info("Remove detected sources ...")
    logger.info("Outfile: %s" % outfile)
    subprocess.check_call(["punlearn", "dmcopy"])
    subprocess.check_call([
        "dmcopy", "infile=%s[exclude sky=region(%s)]" % (infile, srcfile),
        "outfile=%s" % outfile, "clobber=%s" % clobber
    ])
    logger.info("Done!\n")


def extract_lightcurve(infile, outfile, bintime=200, clobber=False):
    """
    Extract the light curve from regions away from the object.
    """
    clobber = "yes" if clobber else "no"
    logger.info("Extract light curve for GTI generation ...")
    logger.info("Outfile: %s" % outfile)
    regfile = os.path.splitext(outfile)[0] + ".reg"
    # Credit: https://stackoverflow.com/a/12654798/4856091
    open(regfile, "a").close()
    logger.warning("Select a large region containing most of the object, " +
                   "but also leaving enough area outside as background; " +
                   "and save as file: %s" % regfile)
    ds9_view(infile)
    fsky = "exclude sky=region(%s)" % regfile
    fbintime = "bin time=::%s" % bintime
    subprocess.check_call(["punlearn", "dmextract"])
    subprocess.check_call([
        "dmextract", "infile=%s[%s][%s]" % (infile, fsky, fbintime),
        "outfile=%s" % outfile, "opt=ltc1", "clobber=%s" % clobber
    ])
    logger.info("Done!\n")


def make_gti(infile, outfile, scale=1.2, clobber=False):
    """
    Examine the light curve for flares, and clip it to make the GTI.
    """
    logger.info("Examine the light curve and create GTI ...")
    logger.info("Outfile: %s" % outfile)

    chipsfile = os.path.splitext(outfile)[0] + ".chips"
    if (not clobber) and (os.path.exists(outfile) or
                          os.path.exists(chipsfile)):
        raise OSError("'%s' or '%s' already exists" % (outfile, chipsfile))

    outimg = os.path.splitext(outfile)[0] + "_lc.jpg"
    lines = [
        "from lightcurves import lc_clean",
        "lc_clean('%s')" % infile,
        "lc_clean('%s', scale=%s, outfile='%s')" % (infile, scale, outfile),
        "print_window('%s', ['format', 'jpg', 'clobber', 'True'])" % outimg
    ]
    open(chipsfile, "w").write("\n".join(lines) + "\n")
    subprocess.check_call(["chips", "-x", chipsfile])

    if not os.path.exists(outfile):
        # workaround the problem that ``chips`` sometimes just failed
        logger.warning("*** Failed to create GTI: %s ***" % outfile)
        logger.warning("You need to create the GTI manually.")
        input("When finished GTI creation, press Enter to continue ...")
    logger.info("Done!\n")


def filter_gti(infile, outfile, gti, clobber=False):
    """
    Removing flares by filtering on GTI to create the finally cleaned
    evt2 file.
    """
    clobber = "yes" if clobber else "no"
    logger.info("Filter on GTI to create cleaned evt2 file ...")
    logger.info("Outfile: %s" % outfile)
    subprocess.check_call(["punlearn", "dmcopy"])
    subprocess.check_call([
        "dmcopy", "infile=%s[@%s]" % (infile, gti),
        "outfile=%s" % outfile, "clobber=%s" % clobber
    ])
    logger.info("Done!\n")


def main():
    parser = argparse.ArgumentParser(
        description="Make a cleaned evt2 for scientific analysis")
    parser.add_argument("-i", "--infile", dest="infile",
                        help="input evt2 produced by 'chandra_repro' " +
                        "(default: request from manifest)")
    parser.add_argument("-C", "--clobber", dest="clobber", action="store_true",
                        help="overwrite existing file")
    args = parser.parse_args()

    setup_pfiles(["dmkeypar", "dmcopy", "celldetect", "dmextract"])

    manifest = get_manifest()
    if args.infile:
        infile = args.infile
    else:
        infile = manifest.getpath("evt2", relative=True)
    chips = ACIS.get_chips_str(infile, sep="-")
    chips_all = ACIS.get_chips_str(infile)
    logger.info("infile: %s" % infile)
    logger.info("chips: %s" % chips)

    evt2_chips = "evt2_c{chips}_orig.fits".format(chips=chips)
    evt2_rmsrc = "evt2_c{chips}_rmsrc.fits".format(chips=chips)
    evt2_clean = "evt2_c{chips}_clean.fits".format(chips=chips)
    srcfile = "sources_celld.reg"
    lcfile = "ex_bkg.lc"
    gtifile = os.path.splitext(lcfile)[0] + ".gti"

    filter_chips(infile, evt2_chips, chips, clobber=args.clobber)
    write_keyword(evt2_chips, keyword="DETNAM",
                  value="ACIS-{0}".format(chips_all))
    detect_sources(evt2_chips, srcfile, clobber=args.clobber)
    remove_sources(evt2_chips, evt2_rmsrc, srcfile, clobber=args.clobber)
    extract_lightcurve(evt2_rmsrc, lcfile, clobber=args.clobber)
    make_gti(lcfile, gtifile, clobber=args.clobber)
    filter_gti(evt2_rmsrc, evt2_clean, gtifile, clobber=args.clobber)

    # Add cleaned evt2 and other products to manifest
    key = "evt2_clean"
    manifest.setpath(key, evt2_clean)
    logger.info("Added '%s' to manifest: %s" % (key, manifest.get(key)))
    key = "reg_sources"
    manifest.setpath(key, srcfile)
    logger.info("Added '%s' to manifest: %s" % (key, manifest.get(key)))
    key = "gti"
    manifest.setpath(key, gtifile)
    logger.info("Added '%s' to manifest: %s" % (key, manifest.get(key)))

    # Remove useless intermediate files
    os.remove(evt2_chips)
    os.remove(evt2_rmsrc)


if __name__ == "__main__":
    main()