Skip to content

ned-transients API

Generated from the docstrings in python/nedtransients/. For how to run the tool, see Preparing a SNe, FRB and GRB load.

Python 3.9 and the standard library only, because that is what the NED team's machines have.

Coordinates

nedtransients.coords

Sexagesimal coordinates in the form NED's ptable files want.

The conversion is smaller than it looks. Both upstream sources already publish positions in sexagesimal, so there is no arithmetic to do: strip the colons, make the declination's sign explicit, and leave every digit alone.

That last part matters. Checking the real FRB.2026.03.31.mod and GRB.2026.03.31.mod against their sources, all 97 rows carry exactly the precision the source published, which is three decimals of right ascension from TNS and two from Swift. Rounding to a fixed number of places would have been the obvious guess and would have been wrong for one of the two files whichever number was picked.

ra_to_ned

ra_to_ned(value)

Convert a right ascension to NED's compact form.

Parameters:

Name Type Description Default
value str

Sexagesimal right ascension, HH:MM:SS.sss. TNS and Swift both publish this, at different precisions.

required

Returns:

Type Description
str

The same value without colons, e.g. "203106.360".

Raises:

Type Description
ValueError

If the input holds anything other than digits, colons and a decimal point. A right ascension is never signed, so a leading + or - means the declination and the right ascension have been swapped.

Examples:

>>> ra_to_ned("20:31:06.360")
'203106.360'
>>> ra_to_ned("13:40:25.49")
'134025.49'

dec_to_ned

dec_to_ned(value)

Convert a declination to NED's compact form, with an explicit sign.

A declination between 0 and -1 degrees is the case worth care: the degrees field is 00 and the sign is the only thing distinguishing north from south. Sources are not consistent about writing + on a positive value, so this adds one when it is missing rather than passing the input through.

Parameters:

Name Type Description Default
value str

Sexagesimal declination, [+-]DD:MM:SS.ss.

required

Returns:

Type Description
str

The same value without colons and always signed, e.g. "+535056.40".

Raises:

Type Description
ValueError

If the input is not a signed or unsigned run of digits and colons.

Examples:

>>> dec_to_ned("+53:50:56.40")
'+535056.40'
>>> dec_to_ned("-00:21:48.71")
'-002148.71'
>>> dec_to_ned("01:55:50.7")
'+015550.7'

Refcodes, loadstatus and the Jira ticket

nedtransients.refcodes

NED-only refcodes, the loadstatus file that registers them, and the Jira text.

Before any transient can be loaded, the three sources it came from need refcodes, and those refcodes do not exist at ADS: NED makes them up. The shape is documented in SNeFRBGRBProcedures.pdf and confirmed by the real 2026a.TNS.FRB.GRB.ls.

The month is encoded twice over, differently:

============ ================================== ======================= Source Pattern March 2025 ============ ================================== ======================= TNS YYYYTNS...M......0. 2025TNS...C......0. FRB YYYYFRB...M...0000. 2025FRB...C...0000. GRB YYYYGRBmm.C...0000. 2025GRB03.C...0000. ============ ================================== =======================

M is the month as a letter with A for January, so March is C. GRB uses the two-digit month instead, and the C sitting in its refcode is a literal standing for "Catalog" rather than a month. Getting those two confused produces a refcode that looks right and points at the wrong month.

month_letter

month_letter(month)

Return the letter TNS and FRB refcodes use for a month.

Parameters:

Name Type Description Default
month int

Month number, 1 through 12.

required

Returns:

Type Description
str

A single upper-case letter, A for January through L for December.

Raises:

Type Description
ValueError

If month is outside 1 to 12.

Examples:

>>> month_letter(3)
'C'
>>> month_letter(7)
'G'

refcode

refcode(kind, year, month)

Build the NED-only refcode for one source and month.

Parameters:

Name Type Description Default
kind str

One of "TNS", "FRB" or "GRB".

required
year int

Four-digit year.

required
month int

Month number, 1 through 12.

required

Returns:

Type Description
str

A 19-character refcode. The trailing character is . rather than the : a bibcode would carry, which is what marks it NED-made.

Raises:

Type Description
ValueError

If kind is not one of the three, or the month is out of range, or the result somehow came out the wrong length.

Examples:

>>> refcode("TNS", 2025, 3)
'2025TNS...C......0.'
>>> refcode("FRB", 2025, 3)
'2025FRB...C...0000.'
>>> refcode("GRB", 2025, 3)
'2025GRB03.C...0000.'

loadstatus_file

loadstatus_file(year, month, kinds=KINDS)

Build the contents of a .ls loadstatus file.

Parameters:

Name Type Description Default
year int

Four-digit year the refcodes belong to.

required
month int

Month number the refcodes belong to.

required
kinds tuple of str

Which sources to register, defaulting to all three.

KINDS

Returns:

Type Description
str

The whole file, ending in a newline. The heading line is not padded; every data row is, and comes to exactly 92 characters.

Examples:

>>> print(loadstatus_file(2026, 3, ("GRB",)))
bibcode|atype|ack|Date|basic|phot|diam|class|astro|kinem|image|spec|otype|msg
2026GRB03.C...0000.|O    |   |          |Y    |...

loadstatus_name

loadstatus_name(year, batch, kinds=KINDS)

Return the filename a .ls file goes under.

The batch letter is load sequence rather than month: the real 2026a file carries March refcodes.

Parameters:

Name Type Description Default
year int

Four-digit year.

required
batch str

The batch letter, a for the year's first load.

required
kinds tuple of str

Which sources the file covers. They appear in the name in order.

KINDS

Returns:

Type Description
str

For example "2026a.TNS.FRB.GRB.ls".

Examples:

>>> loadstatus_name(2026, "a")
'2026a.TNS.FRB.GRB.ls'

jira_body

jira_body(year, month, obtained, kinds=KINDS)

Build the description for the author-information Jira ticket.

These refcodes are NED-made and have no match at ADS, so the author field has to be filled in by hand afterwards. The ticket is the same every month apart from the refcodes and the date.

Parameters:

Name Type Description Default
year int

Four-digit year the refcodes belong to.

required
month int

Month number the refcodes belong to.

required
obtained str

The date the lists were downloaded, as YYYY.MM.DD.

required
kinds tuple of str

Which sources are covered.

KINDS

Returns:

Type Description
str

The ticket body, ready to paste.

Sources

nedtransients.sources

Fetch and parse the two upstream lists.

Two very different sources behind one record type:

  • TNS (<https://www.wis-tns.org/>_) covers supernovae and fast radio bursts. It exports the search page as CSV, filtered by discovery date.
  • Swift XRT (<https://www.swift.ac.uk/xrt_positions/>_) covers gamma-ray bursts, as one pipe-delimited ASCII table with no filtering at all.

Neither needs an account. TNS blocks a short list of well-known tool names by User-Agent, which an honest identifier is not on. See :data:USER_AGENT.

Transient

Bases: NamedTuple

One object from one of the upstream lists.

Attributes:

Name Type Description
kind str

"TNS", "FRB" or "GRB", matching the refcode it loads under.

name str

The object designation, e.g. "FRB 20250924A".

ra str

Right ascension, sexagesimal, exactly as the source published it.

dec str

Declination, sexagesimal, exactly as the source published it.

discovered date

Discovery date, used for the window filter.

tns_id str

TNS's numeric id, or "" for a GRB. This is what lands in the FRB ptable's skip column, and what makes bulk-upload runs detectable.

obj_type str

The classification, e.g. "SN Ia". Empty for a GRB.

redshift str

Redshift if the source published one, otherwise empty.

host str

Host galaxy name if the source published one, otherwise empty.

group str

Reporting group, e.g. "CHIMEFRB". Empty for a GRB.

uncertainty str

Positional uncertainty. For a GRB this is Swift's 90% error radius in arcseconds; the other kinds carry theirs in the ptable header instead.

Cluster

Bases: NamedTuple

A run of consecutive TNS ids from one reporting group.

TNS hands out ids in insertion order, so a catalogue uploaded in one go lands as a contiguous block. Collapsing twenty-seven near-identical CHIME entries into one line is what makes a 140-row candidate list scannable.

This groups, it does not decide. The real FRB.2026.03.31.mod keeps 33 of the 142 candidates in its window, and none of these separates the 33 from the 109:

========================================== =========================== Rule tried Why it is not the rule ========================================== =========================== Discovery-date window Kept and dropped interleave TNS id above a threshold Same Sits in a contiguous id run 23 kept objects sit in one Reporting group Both sets are mostly CHIME Internal name lacks chimefrb_ Misses 20 of the 33 Has a discovery bibcode Keeps 82 that were dropped ========================================== ===========================

So the decision draws on something the TNS export does not carry, and the tool must not pretend otherwise. Every candidate is written to the ptable and a human deletes rows. A tool confident enough to drop three quarters of the list on a rule inferred from one sample would be wrong the first month CHIME changed how it reports, and wrong silently.

size property
size

int : How many objects the run holds.

fetch

fetch(url, timeout=60)

Retrieve a URL as text, looking enough like a browser for TNS.

Parameters:

Name Type Description Default
url str

The address to fetch.

required
timeout int

Seconds to wait, default 60.

60

Returns:

Type Description
str

The response body, decoded as UTF-8 with replacement. NED data is not reliably UTF-8 and a mangled character is better than a crash here, because the raw response is cached to disk for inspection either way.

Raises:

Type Description
RuntimeError

On 429, with what to do about it. Left as a plain HTTPError this reads as a site outage rather than as a quota, and the fix is to wait rather than to investigate.

tns_url

tns_url(
    since,
    until,
    frb=False,
    page=0,
    page_size=TNS_PAGE_SIZE,
    as_csv=True,
)

Build the TNS search URL for one window, kind and page.

Parameters:

Name Type Description Default
since date

Inclusive discovery-date bounds. TNS applies these server-side.

required
until date

Inclusive discovery-date bounds. TNS applies these server-side.

required
frb bool

Ask for fast radio bursts rather than classified supernovae.

False
page int

Zero-based page number.

0
page_size int

Rows per page.

TNS_PAGE_SIZE
as_csv bool

Ask for the CSV export. With False the same query returns the ordinary results page, which :func:parse_tns can also read. See :func:fetch_tns.

True

Returns:

Type Description
str

A URL whose response is CSV, or HTML if as_csv is false.

fetch_tns

fetch_tns(
    since,
    until,
    frb=False,
    page_size=TNS_PAGE_SIZE,
    as_csv=True,
)

Fetch every page of a TNS search and return them as one document.

TNS paginates, and a window wider than a month easily exceeds one page. Taking only the first page would silently drop the rest, which reads exactly like there being nothing more to load.

There is no API key here, so this reads the site the way a person would. That means two routes to the same query, and :func:fetch_tns_resilient uses the second when the first stops working.

Parameters:

Name Type Description Default
since date

Inclusive discovery-date bounds.

required
until date

Inclusive discovery-date bounds.

required
frb bool

Ask for fast radio bursts rather than classified supernovae.

False
page_size int

Rows per page.

TNS_PAGE_SIZE
as_csv bool

Use the CSV export. With False, collect the results pages instead.

True

Returns:

Type Description
str

One CSV with the heading row once, or the concatenated results pages. Either is something :func:parse_tns can read.

Raises:

Type Description
RuntimeError

If the page limit is reached, which means the window is far wider than this tool expects and the result would be truncated.

fetch_tns_resilient

fetch_tns_resilient(
    since,
    until,
    frb=False,
    page_size=TNS_PAGE_SIZE,
    on_fallback=None,
)

Fetch a TNS search, falling back to the results page if the CSV fails.

Without an API key the only way in is to read the site, so the guard against it changing is to know two ways in rather than one. The CSV export is tried first because it is structured and cheap; the results page is the same query rendered for a person, and its cells carry the same fields. Both were checked against each other and agree row for row.

A fallback is not silent. It means the primary route has broken and somebody should look, even though the run itself succeeded.

Parameters:

Name Type Description Default
since date

Inclusive discovery-date bounds.

required
until date

Inclusive discovery-date bounds.

required
frb bool

Ask for fast radio bursts rather than classified supernovae.

False
page_size int

Rows per page.

TNS_PAGE_SIZE
on_fallback callable

Called with a message when the CSV route fails and the page route is used instead.

None

Returns:

Type Description
str

Whichever document was obtained.

Raises:

Type Description
Exception

If both routes fail, the second route's error is raised. A rate limit is not retried on the other route, since the quota covers both.

parse_tns

parse_tns(text, kind)

Parse a TNS response into records, in either format it comes in.

TNS serves the same query two ways, and this reads both: the CSV export, and the ordinary results page. Which one arrived is sniffed from the content rather than tracked alongside it, so a cached response is self-describing and --tns-csv keeps working whichever a person saved.

Parameters:

Name Type Description Default
text str

A CSV body including its heading row, or a search results page.

required
kind str

"TNS" for supernovae or "FRB" for fast radio bursts. TNS itself does not distinguish these in the export; the caller knows which query it ran.

required

Returns:

Type Description
list of Transient

In the order TNS returned them, which is newest first.

Raises:

Type Description
ValueError

If the text is neither format, or the expected columns are missing. That is how a change at TNS surfaces, rather than as an empty list that reads like a quiet month.

parse_swift

parse_swift(text)

Parse the Swift XRT position table into records.

The table has no date column. Every GRB designation encodes its own date, which is what :func:grb_date reads.

Parameters:

Name Type Description Default
text str

The ASCII table, one burst per line, five pipe-delimited fields.

required

Returns:

Type Description
list of Transient

In the order Swift returned them, which is newest first. Rows whose position could not be determined are skipped, since there is nothing to load for them.

grb_date

grb_date(name)

Read the burst date out of a GRB designation.

Parameters:

Name Type Description Default
name str

A designation such as "GRB 260204A".

required

Returns:

Type Description
date or None

The burst date, or None if the name does not have the standard shape.

Examples:

>>> grb_date("GRB 260204A")
datetime.date(2026, 2, 4)
>>> grb_date("GRB 991231A")
datetime.date(1999, 12, 31)

in_window

in_window(records, since, until)

Keep the records discovered within an inclusive date window.

Parameters:

Name Type Description Default
records sequence of Transient

What came back from a source.

required
since date

Inclusive bounds.

required
until date

Inclusive bounds.

required

Returns:

Type Description
list of Transient

In the input order.

without

without(records, already)

Drop records whose names are already loaded.

Parameters:

Name Type Description Default
records sequence of Transient

Candidates.

required
already sequence of str

Names found in the ptable files already on disk.

required

Returns:

Type Description
list of Transient

In the input order.

clusters

clusters(records, minimum=CLUSTER_MINIMUM)

Find runs of consecutive TNS ids sharing a reporting group.

Parameters:

Name Type Description Default
records sequence of Transient

Candidates. Records without a TNS id, such as GRBs, are ignored.

required
minimum int

Smallest run worth reporting.

CLUSTER_MINIMUM

Returns:

Type Description
list of Cluster

Ordered by first id.

Ptable files

nedtransients.ptable

Write the .mod ptable files the NED loaders read.

The format is the one samples/A13L.mod.after demonstrates: a ##refcode line, a block of ## metadata, a pipe-delimited heading row, then the data.

The three sources do not share a layout, and the differences are not cosmetic. Each kind's real file dictates its own metadata block, its own columns, and its own padding, so each gets an entry in :data:LAYOUTS copied from a file that actually loaded rather than from a template that looked reasonable.

Verified against the real files:

  • Every one of the 33 FRB rows and 66 GRB rows reproduces exactly from live source data.
  • The GRB file is a contiguous slice of the Swift table with no gaps and in the same order, so GRBs need no selection at all. FRBs do; see :class:~nedtransients.sources.Cluster.

Column

Bases: NamedTuple

One column of a ptable.

Attributes:

Name Type Description
name str

The heading, e.g. "coordx1".

value str

Which :class:~nedtransients.sources.Transient attribute fills it, or a derived name handled by :func:_cell.

Layout

Bases: NamedTuple

Everything that differs between one source's ptable and another's.

Attributes:

Name Type Description
meta tuple of str

The ## lines, in the order the real file has them. Order is preserved rather than normalised, because a diff against last year's file should come out empty.

columns tuple of Column

The columns, in order.

padded bool

Whether data rows are aligned into columns. The real GRB file is; the real FRB file is not.

Padding puts one space on each side of every |, so a GRB row reads GRB 260204A | 134025.49 | .... The first column has no space in front of it because no delimiter precedes it, which is why its cells are one character narrower than the rule would otherwise give.

render

render(kind, refcode, records)

Build a complete ptable file.

Parameters:

Name Type Description Default
kind str

"TNS", "FRB" or "GRB".

required
refcode str

The NED-only refcode the objects load under.

required
records sequence of Transient

The objects, in the order they should appear.

required

Returns:

Type Description
str

The whole file, ending in a newline.

Raises:

Type Description
KeyError

If kind has no layout.

ValueError

If records is empty. An empty ptable is never wanted, and for FRBs it is the case the procedure warns about.

name_for

name_for(kind, obtained)

Return the filename a ptable goes under.

Parameters:

Name Type Description Default
kind str

"TNS", "FRB" or "GRB".

required
obtained date

The date the list was downloaded.

required

Returns:

Type Description
str

For example "FRB.2026.03.31.mod".

loaded_names

loaded_names(text)

Read the object names out of an existing ptable.

Used to skip objects already loaded, so the tree on disk is the record of what has been done and no separate state file is needed.

Parameters:

Name Type Description Default
text str

The contents of a .mod file.

required

Returns:

Type Description
list of str

Every value found in the name1 column.

Directory layout

nedtransients.layout

The SIP3 directory tree a batch gets prepared in.

Mirrors what the procedure says to build by hand::

YYYY/SNe+FRB+GRB/SNe+FRB+GRB-<batch>-SIP3/
    flt/
    lbl/
    _raw/
    TNS/  FRB/  GRB/

flt and lbl are what the second-pass checks read. _raw is ours: it holds each upstream response exactly as it arrived, so a re-run costs nothing and anything surprising in the output can be traced back to what the server actually said.

raw_name

raw_name(kind, text)

Return the filename a freshly fetched response should be saved under.

Parameters:

Name Type Description Default
kind str

"TNS", "FRB" or "GRB".

required
text str

The response, so the extension can describe it.

required

Returns:

Type Description
str

For example "tns-frb.csv" or, when the fallback route answered, "tns-frb.html".

raw_dir

raw_dir(root, year, batch)

Return the directory holding a batch's cached responses.

write_window

write_window(root, year, batch, since, until)

Record the window a fetch covered.

Parameters:

Name Type Description Default
root str

The data.tables directory.

required
year int

Four-digit year.

required
batch str

Batch letter.

required
since date

Inclusive bounds.

required
until date

Inclusive bounds.

required

Returns:

Type Description
str

The path written.

read_window

read_window(root, year, batch)

Read back the window a fetch covered.

Parameters:

Name Type Description Default
root str

The data.tables directory.

required
year int

Four-digit year.

required
batch str

Batch letter.

required

Returns:

Type Description
tuple of datetime.date, or None

The bounds, or None if no fetch has run for this batch.

cached

cached(root, year, batch, kind)

Return the path of a cached response, or None if it is not there.

Looks under every extension, since which one is present records which route answered when the batch was fetched.

ptables

ptables(root, year, batch)

Return the sources a batch has already built a ptable for.

This is how loadstatus knows which refcodes to register without being told: a source with no ptable has nothing to load, and registering a refcode for it would point the reference database at an empty load.

Parameters:

Name Type Description Default
root str

The data.tables directory.

required
year int

Four-digit year.

required
batch str

Batch letter.

required

Returns:

Type Description
list of str

Source names, in KIND order.

batch_dir

batch_dir(root, year, batch)

Return the path of a batch's SIP3 directory.

Parameters:

Name Type Description Default
root str

The data.tables directory.

required
year int

Four-digit year.

required
batch str

Batch letter, a for the year's first load.

required

Returns:

Type Description
str

The full path. Nothing is created.

Examples:

>>> batch_dir("/data.tables", 2026, "a")
'/data.tables/2026/SNe+FRB+GRB/SNe+FRB+GRB-a-SIP3'

planned

planned(root, year, batch, kinds)

List every directory a batch needs, in creation order.

Parameters:

Name Type Description Default
root str

The data.tables directory.

required
year int

Four-digit year.

required
batch str

Batch letter.

required
kinds sequence of str

Which sources are being loaded. Each gets its own subdirectory.

required

Returns:

Type Description
list of str

Full paths, parents before children.

create

create(paths)

Create directories, skipping any that already exist.

Parameters:

Name Type Description Default
paths sequence of str

Full paths, parents first.

required

Returns:

Type Description
list of str

Only the paths that were actually created, so the caller can report the difference between a fresh batch and a resumed one.

existing_names

existing_names(root, year)

Collect object names from every ptable already under a year.

This is how a re-run avoids reloading what is already done. The tree is the record, so there is no separate state file to keep in step with it.

Parameters:

Name Type Description Default
root str

The data.tables directory.

required
year int

Four-digit year to search under.

required

Returns:

Type Description
list of str

Every name found, with duplicates left in.