209 def run(self, visitSummaries, mpcorb, de440s, sb441_n16, obsCodes, linux_p1550p2650, pck00010,
210 earth_latest_high_prec, earth_620120_250826, earth_2025_250826_2125_predict, naif0012):
211 """Parse the information on the current visit and retrieve the
212 observable solar system objects from Sorcha.
216 visitSummary : `lsst.afw.table.ExposureCatalog`
217 Has rows with .getVisitInfo, which give the center and time of exposure
219 mpcorb, de440s, sb441_n16, obsCodes, linux_p1550p2650, pck00010,
220 earth_latest_high_prec, earth_620120_250826, earth_2025_250826_2125_predict,
221 naif0012 : `lsst.pipe.tasks.sspAuxiliaryFile.SSPAuxiliaryFile`s
222 Minor Planet Center orbit table used for association
226 result : `lsst.pipe.base.Struct`
227 Results struct with components:
229 - ``ssObjects`` : `pandas.DataFrame`
230 DataFrame containing Solar System Objects near the detector
231 footprint as retrieved by Sorcha. The columns are as follows:
236 RA in decimal degrees (`float`)
238 DEC in decimal degrees (`float`)
241 if len(visitSummaries) == 0:
242 raise NoWorkFound(
"No visits input!")
243 visitInfos = [vs[0].getVisitInfo()
for vs
in visitSummaries]
244 fieldRA = np.array([vi.getBoresightRaDec().getRa().asDegrees()
for vi
in visitInfos])
245 fieldDec = np.array([vi.getBoresightRaDec().getDec().asDegrees()
for vi
in visitInfos])
246 epochMJD = np.array([vi.date.toAstropy().tai.mjd
for vi
in visitInfos])
247 visits = [vi.id
for vi
in visitInfos]
251 visitTime = np.ones(n) * 30.0
252 inputVisits = pd.DataFrame({
253 "observationMidpointMJD": epochMJD,
255 "fieldDec": fieldDec,
256 "observationId": visits,
257 "visitTime": np.ones(n) * visitTime,
258 "observationStartMJD": epochMJD - (visitTime / 2) / 86400.0,
259 "visitExposureTime": visitTime,
263 "seeingFwhmGeom": [0.01] * n,
264 "seeingFwhmEff": [0.01] * n,
265 "fiveSigmaDepth": [9999] * n,
266 "rotSkyPos": [0] * n,
269 desig_dict = {un: pack
for pack, un
in mpcorb[[
'packed_primary_provisional_designation',
270 'unpacked_primary_provisional_designation']].values}
271 inputOrbits = mpcorb[
272 [
'unpacked_primary_provisional_designation',
'q',
'e',
'i',
273 'node',
'argperi',
'peri_time',
'epoch_mjd']
274 ].rename(columns={
'unpacked_primary_provisional_designation':
'ObjID',
'epoch_mjd':
'epochMJD_TDB',
275 'i':
'inc',
'argperi':
'argPeri',
'peri_time':
't_p_MJD_TDB'})
276 inputOrbits[
'FORMAT'] =
'COM'
278 inputColors = inputOrbits[[
"ObjID"]].copy()
279 inputColors[
"H_r"] = mpcorb[
'h']
280 inputColors[
"GS"] = 0.15
282 eph_str = resources.files(
lsst.pipe.tasks).parents[3].joinpath(
"data/eph.ini").read_text()
283 eph_str = eph_str.replace(
"{OBSCODE}", self.config.observatoryCode)
284 eph_str = eph_str.replace(
"{FOV}", str(self.config.observatoryFOVRadius))
286 with TemporaryDirectory()
as tmpdirname:
287 self.log.info(f
'temp dir: {tmpdirname}')
290 conn = sqlite_connect(f
'{tmpdirname}/pointings.db')
291 inputVisits.to_sql(
'observations', conn, if_exists=
'replace', index=
False)
294 with open(f
'{tmpdirname}/eph.ini',
'w')
as ephFile:
295 ephFile.write(eph_str)
297 cache = f
'{tmpdirname}/sorcha_cache/'
298 self.log.info(
'making cache')
300 for filename, fileref
in [
301 (
'de440s.bsp', de440s),
302 (
'sb441-n16.bsp', sb441_n16),
303 (
'ObsCodes.json', obsCodes),
304 (
'linux_p1550p2650.440', linux_p1550p2650),
305 (
'pck00010.pck', pck00010),
306 (
'earth_latest_high_prec.bpc', earth_latest_high_prec),
307 (
'earth_620120_250826.bpc', earth_620120_250826),
308 (
'earth_2025_250826_2125_predict.bpc', earth_2025_250826_2125_predict),
309 (
'naif0012.tls', naif0012),
311 self.log.info(f
'writing {filename}')
312 with open(cache + filename,
'wb')
as file:
313 file.write(fileref.fileContents.read())
315 abspath = f
'{tmpdirname}/sorcha_cache/'
317 n_iter = int(len(str(abspath)) / split)
318 for n
in range(n_iter, 0, -1):
319 abspath = abspath[:split * n] +
"+' '" + abspath[split * n:]
321 meta_kernel_text = dedent(f
"""\
324 PATH_VALUES = ('{abspath}')
330 '$A/earth_620120_250826.bpc',
331 '$A/earth_2025_250826_2125_predict.bpc',
334 '$A/earth_latest_high_prec.bpc',
339 with open(f
'{tmpdirname}/sorcha_cache/meta_kernel.txt',
'w')
as meta_kernel_file:
340 meta_kernel_file.write(meta_kernel_text)
341 self.log.info(
'Sorcha process begun')
344 inputOrbits.to_csv(f
'{tmpdirname}/orbits.csv', index=
False)
345 inputColors.to_csv(f
'{tmpdirname}/colors.csv', index=
False)
350 import sbpy.photometry
351 sorcha_run = [
"sorcha",
"run"]
354 self.log.info(
"WARNING: Running sorcha-run patched for sbpy issues.")
355 sorcha_run = [
"python",
"-c", textwrap.dedent(
"""
357 print("WARNING: Running sorcha-run patched for sbpy issues.", file=sys.stderr)
359 import astropy.units.utils
360 import astropy.units.docgen
361 astropy.units.utils.generate_unit_summary = \
362 astropy.units.docgen.generate_unit_summary
364 from sorcha_cmdline.run import main
371 "-c", f
"{tmpdirname}/eph.ini",
372 "-o", f
"{tmpdirname}/",
373 "--ob", f
"{tmpdirname}/orbits.csv",
374 "-p", f
"{tmpdirname}/colors.csv",
375 "--pd", f
"{tmpdirname}/pointings.db",
376 "--ew", f
"{tmpdirname}/ephemeris",
377 "--ar", f
"{tmpdirname}/sorcha_cache/",
378 "-t",
"sorcha_output",
387 while proc.poll()
is None:
388 sleep(self.config.loggingSleepTime)
391 outfile = open(glob.glob(f
'{tmpdirname}/sorcha_output*.log')[0],
'r')
392 new_text = outfile.read()
393 if len(new_text) > 0:
394 self.log.info(new_text)
396 seconds_elapsed = n_iter * self.config.loggingSleepTime
397 self.log.info(f
"Sorcha is still running ({seconds_elapsed} seconds)")
399 stdout, stderr = proc.communicate()
400 self.log.info(f
"Sorcha STDOUT:\n {stdout}")
401 self.log.info(f
"Sorcha STDERR:\n {stderr}")
403 eph_path = f
'{tmpdirname}/sorcha_output.csv'
404 if not os.path.exists(eph_path):
405 raise FileNotFoundError(
406 " Sorcha did not create sorcha_output. Check STDOUT/STDERR above. "
407 f
"Directory contents:\n{os.listdir(tmpdirname)}"
411 ephemeris = pd.read_csv(eph_path)
412 ephemeris[
'packed_desig'] = ephemeris[
'ObjID'].map(desig_dict)
413 perVisitSsObjects = {FieldID: group
for FieldID, group
in ephemeris.groupby(
"FieldID")}
415 if v
not in perVisitSsObjects:
416 perVisitSsObjects[v] = ephemeris.iloc[:0]
417 return Struct(ssObjects=perVisitSsObjects)