|
1 | 1 | # This code is a part of X-ray: Generate and Analyse (XGA), a module designed for the XMM Cluster Survey (XCS).
|
2 |
| -# Last modified by David J Turner (turne540@msu.edu) 21/02/2024, 09:57. Copyright (c) The Contributors |
| 2 | +# Last modified by David J Turner (turne540@msu.edu) 01/05/2024, 15:27. Copyright (c) The Contributors |
3 | 3 |
|
4 | 4 | from functools import wraps
|
5 | 5 | from multiprocessing.dummy import Pool
|
@@ -32,58 +32,74 @@ def execute_cmd(cmd: str, p_type: str, p_path: list, extra_info: dict, src: str)
|
32 | 32 | :return: The product object, and the string representation of the associated source object.
|
33 | 33 | :rtype: Tuple[BaseProduct, str]
|
34 | 34 | """
|
35 |
| - out, err = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).communicate() |
36 |
| - out = out.decode("UTF-8", errors='ignore') |
37 |
| - err = err.decode("UTF-8", errors='ignore') |
38 |
| - |
39 |
| - # This part for defining an image object used to make sure that the src wasn't a NullSource, as defining product |
40 |
| - # objects is wasteful considering the purpose of a NullSource, but generating exposure maps requires a |
41 |
| - # pre-existing image |
42 |
| - if p_type == "image": |
43 |
| - # Maybe let the user decide not to raise errors detected in stderr |
44 |
| - prod = Image(p_path[0], extra_info["obs_id"], extra_info["instrument"], out, err, cmd, extra_info["lo_en"], |
45 |
| - extra_info["hi_en"]) |
46 |
| - if "psf_corr" in extra_info and extra_info["psf_corr"]: |
47 |
| - prod.psf_corrected = True |
48 |
| - prod.psf_bins = extra_info["psf_bins"] |
49 |
| - prod.psf_model = extra_info["psf_model"] |
50 |
| - prod.psf_iterations = extra_info["psf_iter"] |
51 |
| - prod.psf_algorithm = extra_info["psf_algo"] |
52 |
| - elif p_type == "expmap": |
53 |
| - prod = ExpMap(p_path[0], extra_info["obs_id"], extra_info["instrument"], out, err, cmd, |
54 |
| - extra_info["lo_en"], extra_info["hi_en"]) |
55 |
| - elif p_type == "ccf" and "NullSource" not in src: |
56 |
| - # ccf files may not be destined to spend life as product objects, but that doesn't mean |
57 |
| - # I can't take momentarily advantage of the error parsing I built into the product classes |
58 |
| - prod = BaseProduct(p_path[0], "", "", out, err, cmd) |
59 |
| - elif (p_type == "spectrum" or p_type == "annular spectrum set components") and "NullSource" not in src: |
60 |
| - prod = Spectrum(p_path[0], extra_info["rmf_path"], extra_info["arf_path"], extra_info["b_spec_path"], |
61 |
| - extra_info['central_coord'], extra_info["inner_radius"], extra_info["outer_radius"], |
62 |
| - extra_info["obs_id"], extra_info["instrument"], extra_info["grouped"], extra_info["min_counts"], |
63 |
| - extra_info["min_sn"], extra_info["over_sample"], out, err, cmd, extra_info["from_region"], |
64 |
| - extra_info["b_rmf_path"], extra_info["b_arf_path"]) |
65 |
| - elif p_type == "psf" and "NullSource" not in src: |
66 |
| - prod = PSFGrid(extra_info["files"], extra_info["chunks_per_side"], extra_info["model"], |
67 |
| - extra_info["x_bounds"], extra_info["y_bounds"], extra_info["obs_id"], |
68 |
| - extra_info["instrument"], out, err, cmd) |
69 |
| - elif p_type == 'light curve' and "NullSource" not in src: |
70 |
| - prod = LightCurve(p_path[0], extra_info["obs_id"], extra_info["instrument"], out, err, cmd, |
71 |
| - extra_info['central_coord'], extra_info["inner_radius"], extra_info["outer_radius"], |
72 |
| - extra_info["lo_en"], extra_info["hi_en"], extra_info['time_bin'], extra_info['pattern'], |
73 |
| - extra_info["from_region"]) |
74 |
| - elif p_type == "cross arfs": |
75 |
| - prod = BaseProduct(p_path[0], extra_info['obs_id'], extra_info['inst'], out, err, cmd, extra_info) |
76 |
| - elif "NullSource" in src: |
77 |
| - prod = None |
78 |
| - else: |
79 |
| - raise NotImplementedError("Not implemented yet") |
80 |
| - |
81 |
| - # An extra step is required for annular spectrum set components |
82 |
| - if p_type == "annular spectrum set components": |
83 |
| - prod.annulus_ident = extra_info["ann_ident"] |
84 |
| - prod.set_ident = extra_info["set_ident"] |
85 |
| - |
86 |
| - return prod, src |
| 35 | + try: |
| 36 | + out, err = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).communicate() |
| 37 | + out = out.decode("UTF-8", errors='ignore') |
| 38 | + err = err.decode("UTF-8", errors='ignore') |
| 39 | + |
| 40 | + # This part for defining an image object used to make sure that the src wasn't a NullSource, as defining product |
| 41 | + # objects is wasteful considering the purpose of a NullSource, but generating exposure maps requires a |
| 42 | + # pre-existing image |
| 43 | + if p_type == "image": |
| 44 | + # Maybe let the user decide not to raise errors detected in stderr |
| 45 | + prod = Image(p_path[0], extra_info["obs_id"], extra_info["instrument"], out, err, cmd, extra_info["lo_en"], |
| 46 | + extra_info["hi_en"]) |
| 47 | + if "psf_corr" in extra_info and extra_info["psf_corr"]: |
| 48 | + prod.psf_corrected = True |
| 49 | + prod.psf_bins = extra_info["psf_bins"] |
| 50 | + prod.psf_model = extra_info["psf_model"] |
| 51 | + prod.psf_iterations = extra_info["psf_iter"] |
| 52 | + prod.psf_algorithm = extra_info["psf_algo"] |
| 53 | + elif p_type == "expmap": |
| 54 | + prod = ExpMap(p_path[0], extra_info["obs_id"], extra_info["instrument"], out, err, cmd, |
| 55 | + extra_info["lo_en"], extra_info["hi_en"]) |
| 56 | + elif p_type == "ccf" and "NullSource" not in src: |
| 57 | + # ccf files may not be destined to spend life as product objects, but that doesn't mean |
| 58 | + # I can't take momentarily advantage of the error parsing I built into the product classes |
| 59 | + prod = BaseProduct(p_path[0], "", "", out, err, cmd) |
| 60 | + elif (p_type == "spectrum" or p_type == "annular spectrum set components") and "NullSource" not in src: |
| 61 | + prod = Spectrum(p_path[0], extra_info["rmf_path"], extra_info["arf_path"], extra_info["b_spec_path"], |
| 62 | + extra_info['central_coord'], extra_info["inner_radius"], extra_info["outer_radius"], |
| 63 | + extra_info["obs_id"], extra_info["instrument"], extra_info["grouped"], |
| 64 | + extra_info["min_counts"], extra_info["min_sn"], extra_info["over_sample"], out, err, cmd, |
| 65 | + extra_info["from_region"], extra_info["b_rmf_path"], extra_info["b_arf_path"]) |
| 66 | + elif p_type == "psf" and "NullSource" not in src: |
| 67 | + prod = PSFGrid(extra_info["files"], extra_info["chunks_per_side"], extra_info["model"], |
| 68 | + extra_info["x_bounds"], extra_info["y_bounds"], extra_info["obs_id"], |
| 69 | + extra_info["instrument"], out, err, cmd) |
| 70 | + elif p_type == 'light curve' and "NullSource" not in src: |
| 71 | + prod = LightCurve(p_path[0], extra_info["obs_id"], extra_info["instrument"], out, err, cmd, |
| 72 | + extra_info['central_coord'], extra_info["inner_radius"], extra_info["outer_radius"], |
| 73 | + extra_info["lo_en"], extra_info["hi_en"], extra_info['time_bin'], extra_info['pattern'], |
| 74 | + extra_info["from_region"]) |
| 75 | + elif p_type == "cross arfs": |
| 76 | + prod = BaseProduct(p_path[0], extra_info['obs_id'], extra_info['inst'], out, err, cmd, extra_info) |
| 77 | + elif "NullSource" in src: |
| 78 | + prod = None |
| 79 | + else: |
| 80 | + raise NotImplementedError("Not implemented yet") |
| 81 | + |
| 82 | + # An extra step is required for annular spectrum set components |
| 83 | + if p_type == "annular spectrum set components": |
| 84 | + prod.annulus_ident = extra_info["ann_ident"] |
| 85 | + prod.set_ident = extra_info["set_ident"] |
| 86 | + |
| 87 | + return prod, src |
| 88 | + |
| 89 | + # This is deliberately an all encompassing except - as I want to modify the message of what error may get thrown |
| 90 | + # and then I will re-raise it, just it will include the source, ObsID, and instrument that caused the issue |
| 91 | + except Exception as err: |
| 92 | + # Some possible errors (I'm looking at you OSError) tend to have a number as their first argument and then |
| 93 | + # the actual message as the second. In most cases though, I think just the first is populated |
| 94 | + if len(err.args) == 1: |
| 95 | + err.args = (err.args[0] + "- {s} is the associated source, the specific data used is " \ |
| 96 | + "{o}-{i}.".format(s=src, o=extra_info["obs_id"], i=extra_info["instrument"]), ) |
| 97 | + # But if there are two we do want to include them both |
| 98 | + else: |
| 99 | + err.args = (err.args[0], err.args[1] + "- {s} is the associated source, the specific data used is " |
| 100 | + "{o}-{i}.".format(s=src, o=extra_info["obs_id"], |
| 101 | + i=extra_info["instrument"])) |
| 102 | + raise err |
87 | 103 |
|
88 | 104 |
|
89 | 105 | def sas_call(sas_func):
|
@@ -178,8 +194,22 @@ def err_callback(err):
|
178 | 194 | """
|
179 | 195 | nonlocal raised_errors
|
180 | 196 | nonlocal gen
|
| 197 | + nonlocal src_lookup |
| 198 | + nonlocal sources |
181 | 199 |
|
182 | 200 | if err is not None:
|
| 201 | + # We used a memory address laden source name representation when we adjusted the error |
| 202 | + # message in execute_cmd, so we'll replace it with an actual name here |
| 203 | + # Again it matters how many arguments the error has |
| 204 | + if len(err.args) == 1: |
| 205 | + err_src_rep = err.args[0].split(' is the associated source')[0].split('- ')[-1].strip() |
| 206 | + act_src_name = sources[src_lookup[err_src_rep]].name |
| 207 | + err.args = (err.args[0].replace(err_src_rep, act_src_name),) |
| 208 | + else: |
| 209 | + err_src_rep = err.args[1].split(' is the associated source')[0].split('- ')[-1].strip() |
| 210 | + act_src_name = sources[src_lookup[err_src_rep]].name |
| 211 | + err.args = (err.args[0], err.args[1].replace(err_src_rep, act_src_name)) |
| 212 | + |
183 | 213 | # Rather than throwing an error straight away I append them all to a list for later.
|
184 | 214 | raised_errors.append(err)
|
185 | 215 | gen.update(1)
|
@@ -270,8 +300,8 @@ def err_callback(err):
|
270 | 300 | sources[ind].update_products(ann_spec)
|
271 | 301 |
|
272 | 302 | # Errors raised here should not be to do with SAS generation problems, but other purely pythonic errors
|
273 |
| - for error in raised_errors: |
274 |
| - raise error |
| 303 | + if len(raised_errors) != 0: |
| 304 | + raise Exception(raised_errors) |
275 | 305 |
|
276 | 306 | # And here are all the errors during SAS generation, if any
|
277 | 307 | if len(all_to_raise) != 0:
|
|
0 commit comments