11# This code is part of X-ray: Generate and Analyse (XGA), a module designed for the XMM Cluster Survey (XCS).
2- # Last modified by David J Turner (djturner@umbc.edu) 5/21/26, 3:19 PM. Copyright (c) The Contributors.
2+ # Last modified by David J Turner (djturner@umbc.edu) 5/21/26, 3:53 PM. Copyright (c) The Contributors.
33
44import contextlib
55import gc
@@ -310,36 +310,6 @@ def __init__(self, ra: float, dec: float, redshift: float = None, name: str = No
310310 # actual event list/image/expmap/region files - those initial products are loaded into XGA products
311311 self ._products , region_dict = self ._initial_products (obs , load_regions , null_load_products )
312312
313- # # We need to make sure that we only keep instruments (and ObsIDs) that actually have an
314- # # event list associated with them - it is possible for initial_products to have not been
315- # # able to load them
316- # new_prods = {t: {o: {i: self._products[t][o][i] for i in self._products[t][o]
317- # if 'events' in self._products[t][o][i]}
318- # for o in self._products[t]} for t in self._products}
319- # # Then we remove any ObsIDs that no longer have any instruments
320- # new_prods = {t: {o: new_prods[t][o] for o in new_prods[t]
321- # if len(new_prods[t][o]) != 0}
322- # for t in new_prods}
323- # # Replicate the new_prods structure for the 'obs' and 'region_dict' variables
324- # new_obs = {t: {o: [i for i in obs[t][o] if i in new_prods[t][o]] for o in new_prods[t]} for t in new_prods}
325- # new_regs = {t: {o: region_dict[t][o] for o in new_prods[t]} for t in new_prods}
326- #
327- # # Warn the user about removals
328- # # Aggregate all removed combinations for a single warning
329- # all_removed = ["{t}: {o}({i})".format(t=t, o=o, i=", ".join([i for i in obs[t][o] if o not in new_obs[t]
330- # or i not in new_obs[t][o]]))
331- # for t in obs for o in obs[t] if o not in new_obs[t]
332- # or any(i not in new_obs[t][o] for i in obs[t][o])]
333- #
334- # if all_removed:
335- # warn(f"Some observations/instruments were removed as their event lists could not "
336- # f"be read: {"; ".join(all_removed)}", stacklevel=2)
337-
338- # # Assign the altered dictionaries
339- # self._products = new_prods
340- # obs = new_obs
341- # region_dict = new_regs
342-
343313 # Now we do ANOTHER check just like the one above, but on the product attribute, as it is possible that
344314 # all those files cannot be found
345315 cur_obs_nums = {tel : len (self ._products [tel ]) for tel in self ._products }
@@ -352,10 +322,36 @@ def __init__(self, ra: float, dec: float, redshift: float = None, name: str = No
352322 new_obs = {tel : obs [tel ] for tel , num in cur_obs_nums .items () if num != 0 }
353323 new_prods = {tel : self ._products [tel ] for tel , num in cur_obs_nums .items () if num != 0 }
354324 new_regs = {tel : region_dict [tel ] for tel , num in cur_obs_nums .items () if num != 0 }
355- # Then assign the new cut down dictionaries to their original names
356- obs = new_obs
357- self ._products = new_prods
358- region_dict = new_regs
325+
326+ else :
327+ new_obs = obs
328+ new_prods = self ._products
329+ new_regs = region_dict
330+
331+ # Identify any observations or instruments that were removed because their event lists
332+ # could not be read in successfully
333+ removed_obs = {t : {o : [i for i in obs [t ][o ] if o not in new_prods [t ] or i not in new_prods [t ][o ]]
334+ for o in obs [t ]} for t in obs }
335+ # Clean up the dictionary so it only contains entries that actually have removed instruments
336+ removed_obs = {t : {o : i for o , i in removed_obs [t ].items () if len (i ) != 0 } for t in removed_obs }
337+ removed_obs = {t : o for t , o in removed_obs .items () if len (o ) != 0 }
338+
339+ # If there are any removed observations/instruments, we raise a warning
340+ if len (removed_obs ) != 0 :
341+ # We construct a message to warn the user
342+ warn_msg = "The following observations/instruments could not be read in and have been removed: "
343+ # This nested join formats the dictionary into Telescope: ObsID(inst1, inst2); ...
344+ warn_msg += "; " .join (["{t}: {o}" .format (t = t , o = ", " .join (["{o}({i})" .format (o = o , i = ", " .join (i ))
345+ for o , i in removed_obs [t ].items ()]))
346+ for t in removed_obs ])
347+ warn (warn_msg , stacklevel = 2 )
348+
349+ # Assign the new, potentially cut down, dictionaries to their original names - this is separated from the
350+ # checking logic above so that we can raise a warning by comparing obs and new_prods before obs
351+ # gets overwritten
352+ obs = new_obs
353+ self ._products = new_prods
354+ region_dict = new_regs
359355
360356 # This is somewhat inelegant, but oh well; it ensures that there are individual instrument entries for each
361357 # ObsID in the products dictionary, for cases where the data are shipped with all instruments combined
@@ -1133,16 +1129,13 @@ def read_default_products(en_lims: tuple) -> Tuple[str, dict]:
11331129 # the copy so that any modifications don't harm the original file.
11341130 reg_file = rel_sec ["region_file" ].format (obs_id = obs_id )
11351131
1136-
1137-
1138-
1132+ # Load the event list - if this doesn't work then there isn't any point continuing to the
1133+ # rest of the initial product loading process for the current telescope-ObsID-instrument
11391134 evt_list = EventList (evt_file , obs_id = obs_id , instrument = inst , stdout_str = "" , stderr_str = "" ,
11401135 gen_cmd = "" , telescope = tel )
11411136 if not evt_list .usable :
11421137 continue
11431138
1144-
1145-
11461139 # Attitude file is a special type of data product, we shouldn't ever deal with it directly so it
11471140 # doesn't have a product object. It also isn't guaranteed to be a separate thing for all
11481141 # telescopes, so we do check that the configuration file actually has an entry for it.
@@ -1184,14 +1177,10 @@ def read_default_products(en_lims: tuple) -> Tuple[str, dict]:
11841177 else :
11851178 mask_prod = None
11861179
1180+ # If we've gotten to this part of the method we know that the EventList exists and is usable, so
1181+ # to proceed to populating the obs_dict we check that the attitude file (if one is meant to
1182+ # exist for this telescope) actually exists.
11871183 if (att_prod is not None and att_prod .usable ) or att_prod is None :
1188- # An instrument subsection of an observation will ONLY be populated if the events file exists
1189- # Otherwise nothing can be done with it.
1190- # evt_list = EventList(evt_file, obs_id=obs_id, instrument=inst, stdout_str="", stderr_str="",
1191- # gen_cmd="", telescope=tel)
1192- # if not evt_list.usable:
1193- # continue
1194-
11951184 # Start off with the events list going in - make sure there is an inst entry first
11961185 obs_dict [tel ][obs_id ].setdefault (inst , {})
11971186 obs_dict [tel ][obs_id ][inst ]["events" ] = evt_list
0 commit comments