Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# (c) Stefan Countryman, 2019
3"""
4A registry of detectors providing uniform naming conventions.
6Available Detectors
7-------------------
9"""
11from collections import namedtuple
12from llama.classes import ImmutableDict
15DetectorTuple = namedtuple(
16 "DetectorTuple",
17 (
18 "name",
19 "abbrev",
20 "fullname",
21 "url",
22 "summary",
23 "description",
24 "citations",
25 )
26)
29class TableRowRepresentable(DetectorTuple):
30 """
31 A class for printing ``DetectorTuple`` rows in docstring format for a
32 table. *This superclass of* ``Detector`` *implements table printing for
33 ``DetectorTuple`` instances in such a way that it can be reused to generate
34 the header rows of the ``llama.detectors`` module docstring.*
35 """
37 TABLE_COLUMN_WIDTHS = DetectorTuple(name=13, abbrev=13, fullname=65,
38 url=None, summary=None,
39 description=None, citations=None)
41 def doctable_row(self):
42 """Get a row describing this ``DetectorTuple`` for inclusion in a table
43 of all detectors. *This method is defined in a separate class from*
44 ``Detector`` *because it is also used (in a hacky way) to generate the
45 table header.*"""
46 row = "|"
47 sep = "+"
48 for field in self.TABLE_COLUMN_WIDTHS._fields:
49 if getattr(self.TABLE_COLUMN_WIDTHS, field) is not None:
50 length = getattr(self.TABLE_COLUMN_WIDTHS, field)
51 string = getattr(self, field)
52 string = '``None``' if string is None else string
53 row += (f" {string[:length-3]: <{length-3}}"
54 f"{'...' if len(string) > length else ' '} |")
55 sep += '-'*(2+length)+'+'
56 return f"{row}\n{sep}\n"
59def doc_table_header():
60 """Get the header rows for the table of ``Detector`` instances in
61 ``llama.detectors.__doc__``."""
62 gen = TableRowRepresentable(**{f: f"``{f}``"
63 for f in DetectorTuple._fields})
64 head, sep, end = gen.doctable_row().split('\n')
65 return '\n'.join((sep, head, sep.replace('-', '='), end))
68class Detector(TableRowRepresentable):
69 """
70 An object describing some sort of detector (or, more generally,
71 a source of data). Use these instances to provide uniform naming
72 conventions, data source descriptions, and other information across the
73 pipeline. You shouldn't create ``Detector`` instances outside of the main
74 detectors file since they are meant to be stored in the same namespace for
75 shared use throughout the pipeline. On instantiation, an assertion runs to
76 ensure that there are no name collisions or inconsistencies and to register
77 the new detector name in the ``detectors`` namespace. This allows
78 the ``detectors`` submodule to provide a convenient, unified interface for
79 accessing all defined ``Detector`` isntances and avoid runtime
80 complications. This name disambiguation is important to keep
81 automatically-generated code and documentation uniform and unambiguous with
82 respect to detector
83 references.
85 Parameters
86 ----------
87 name : str
88 The name of the detector. Must be a valid name for a ``python``
89 variable, and should correspond to the name of the ``Detector`` in the
90 ``detectors`` submodule.
91 abbrev : str
92 A **very** short abbreviation (think 3 characters) for this detector.
93 Must be a valid name for a ``python`` variable. This will be used for
94 dynamically-generated subclasses and filenames.
95 fullname : str, optional
96 The full name of the detector (useful for longer detector names). If
97 ``name`` is an acronym or other abbreviation, then ``fullname`` should
98 spell out what ``name`` stands for. If not provided, this will take the
99 same value as ``name``.
100 url : str, optional
101 A URL pointing to this detector's homepage (or whatever page best
102 summarizes this detector/links to other good information about the
103 detector/offers data access). (default: ``None``)
104 summary : str, optional
105 A short (think: one or two lines) sentence that describes the detector.
106 If not provided, this will take the same value as ``name``.
107 description : str, optional
108 An extended (think: up to a few paragraphs) description of the
109 detector from which a new developer could gain cursory understanding of
110 the data source.
111 citations : ImmutableDict, optional
112 A dictionary of citations which a developer or user can use to learn
113 more about the detector. Map article titles/names (``str``) to URLs
114 (``str``) at which those articles can be found, e.g. NASA ADS or arXiv
115 links.
117 Raises
118 ------
119 TypeError
120 If this detector clashes in ``name`` or ``abbrev`` with another
121 existing detector.
122 """
124 def __new__(cls, name, abbrev, fullname=None, url=None, summary=None,
125 description="", citations=ImmutableDict({})):
126 """Initialize a new ``Detector``, providing default values for any
127 unspecified optional parameters."""
128 if not all(isinstance(v, str) for v in citations.values()):
129 raise TypeError(("Citation URLs must all be ``str`` type. Got: "
130 "{}").format(citations))
131 if not all(isinstance(k, str) for k in citations.keys()):
132 raise TypeError(("Citation names must all be ``str`` type. Got: "
133 "{}").format(citations))
134 new = DetectorTuple.__new__(
135 cls,
136 name=name,
137 abbrev=abbrev,
138 fullname=fullname if fullname is not None else name,
139 url=url,
140 summary=summary if summary is not None else name,
141 description=description,
142 citations=citations,
143 )
144 if not all(isinstance(a, str) for a in (new.name, new.abbrev,
145 new.fullname, new.summary,
146 new.description)):
147 raise TypeError(("``name``, ``abbrev``, ``fullname``, and "
148 "``summary`` must all be ``str`` type if "
149 "specified. Got: {}").format(new))
150 if not (isinstance(new.url, str) or new.url is None):
151 raise TypeError(("``url`` must be ``str`` type if specified. Got: "
152 "{}").format(new))
153 cls._register_new_detector(new)
154 return new
156 def _register_new_detector(self):
157 """Register a newly-created detector to the defining module's scope and
158 check for conflicts with other existing detectors. Raises a
159 ``TypeError`` if they conflict."""
160 det = [d for d in globals().values()
161 if isinstance(d, Detector)] + [self]
162 if not len({v for d in det for v in [d.name, d.abbrev]}) == 2*len(det):
163 raise TypeError(("Found conflicting existing detectors in the "
164 "``detectors`` module namespace: {}").format(det))
165 globals()[self.name] = self
168IceCube = Detector('IceCube', 'i3', url='http://wiki.icecube.wisc.edu')
169LVC = Detector('LVC', 'lvc', url='http://wiki.ligo.org')
170Fermi = Detector('Fermi', 'frm')
171ZTF = Detector(
172 'ZTF',
173 'ztf',
174 fullname='Zwicky Transient Facility',
175 url='https://www.ptf.caltech.edu/page/ztf_technical',
176 citations=ImmutableDict({
177 'The Zwicky Transient Facility (Bellm 2014)':
178 'http://adsabs.harvard.edu/abs/2014arXiv1410.8185B',
179 'The Zwicky transient facility observing system (Smith et al. 2014)':
180 'http://adsabs.harvard.edu/abs/2014SPIE.9147E..79S',
181 'The Zwicky Transient Facility Camera (Dekany et al. 2016)':
182 'http://adsabs.harvard.edu/abs/2016SPIE.9908E..5MD',
183 'The unblinking eye on the sky (Bellm and Kulkarni 2017)':
184 'http://adsabs.harvard.edu/abs/2017NatAs...1E..71B',
185 }),
186)
187LLAMA = Detector(
188 'LLAMA',
189 'lma',
190 url='http://multimessenger.science',
191 fullname='Low-Latency Algorithm for Multimessenger Astrophysics',
192 citations=ImmutableDict({
193 (
194 'Low-Latency Algorithm for Multi-messenger Astrophysics (LLAMA) '
195 'with Gravitational-Wave and High-Energy Neutrino Candidates'
196 ): 'https://arxiv.org/abs/1901.05486',
197 (
198 'Bayesian Multi-Messenger Search Method for Common Sources of '
199 'Gravitational Waves and High-Energy Neutrinos'
200 ): 'https://arxiv.org/abs/1810.11467',
201 }),
202)
205# add the detector parameters to a .rst table in the docstring
206__doc__ += doc_table_header()
207for detector in [d for d in locals().values() if isinstance(d, Detector)]:
208 __doc__ += detector.doctable_row()