What’s New in Astropy 6.0?#

Overview#

Astropy 6.0 is a major release that adds significant new functionality since the 5.3 release.

In particular, this release includes:

In addition to these major changes, Astropy v6.0 includes a large number of smaller improvements and bug fixes, which are described in the Full Changelog. By the numbers:

  • 1044 commits have been added since 5.3

  • 157 issues have been closed since 5.3

  • 313 pull requests have been merged since 5.3

  • 57 people have contributed since 5.3

  • 24 of which are new contributors

Define Geodetic and Bodycentric Representations via their geometric parameters#

The user may now define custom spheroidal models for the Earth or other planetary bodies by subclassing BaseGeodeticRepresentation or BaseBodycentricRepresentation and defining _equatorial_radius and _flattening attributes:

>>> from astropy.coordinates import (BaseGeodeticRepresentation,
...     WGS84GeodeticRepresentation, BaseBodycentricRepresentation)
>>> from astropy import units as u
>>> class IAU1976EarthGeodeticRepresentation(BaseGeodeticRepresentation):
...     _equatorial_radius = 6378140 * u.m
...     _flattening = 0.3352805 * u.percent
>>> representation = IAU1976EarthGeodeticRepresentation(lon=45.8366*u.deg,
...     lat=56.1499*u.deg, height=367*u.m)
>>> representation.to_cartesian() 
<CartesianRepresentation (x, y, z) in m
    (2481112.60371134, 2554647.09482601, 5274064.55958489)>
>>> representation.represent_as(WGS84GeodeticRepresentation) 
<WGS84GeodeticRepresentation (lon, lat, height) in (rad, rad, m)
    (0.79999959, 0.98000063, 370.01796023)>
>>> class IAU1976EarthBodycentricRepresentation(BaseBodycentricRepresentation):
...     _equatorial_radius = 6378140 * u.m
...     _flattening = 0.3352805 * u.percent
>>> representation.represent_as(IAU1976EarthBodycentricRepresentation) 
<IAU1976EarthBodycentricRepresentation (lon, lat, height) in (rad, rad, m)
    (0.79999959, 0.9768896, 336.12620429)>

See Creating Your Own Geodetic and Bodycentric Representations for more details.

Body-fixed planetary reference frames and their WCS description#

Body-fixed planetary reference frames for Solar System planetary bodies can be read and written via WCS structures in FITS images, following Definitions, Applications, and Best Practices for Planetary FITS.

See Creating a planetary WCS structure for an example.

Planetary images or spectral cube WCS description can be manipulated using the wcs module.

Support for Numpy broadcasting over frame data and attributes#

Frames in astropy.coordinates now support Numpy broadcasting rules over both frame data and frame attributes. Previously, broadcasting was only supported over framed data. This makes it much easier and faster to do positional astronomy calculations and transformations on sweeps of parameters.

For example, the user can now create frame objects with scalar data but vector frame attributes, such as:

from astropy.coordinates import FK4
from astropy import units as u

FK4(1 * u.deg, 2 * u.deg, obstime=["J2000", "J2001"])

Where this really shines is doing fast observability calculations over arrays. The following example constructs an EarthLocation array of length L, a SkyCoord array of length M, and a Time array of length N. It uses Numpy broadcasting rules to evaluate a boolean array of shape (L, M, N) that is True for those observing locations, times, and sky coordinates, for which the target is above an altitude limit:

>>> from astropy.coordinates import EarthLocation, AltAz, SkyCoord
>>> from astropy.coordinates.angles import uniform_spherical_random_surface
>>> from astropy.time import Time
>>> from astropy import units as u
>>> import numpy as np

>>> L = 25
>>> M = 100
>>> N = 50

>>> # Earth locations of length L
>>> c = uniform_spherical_random_surface(L)
>>> locations = EarthLocation.from_geodetic(c.lon, c.lat)

>>> # Celestial coordinates of length M
>>> coords = SkyCoord(uniform_spherical_random_surface(M))

>>> # Observation times of length N
>>> obstimes = Time('2023-08-04') + np.linspace(0, 24, N) * u.hour

>>> # AltAz coordinates of shape (L, M, N)
>>> frame = AltAz(
...     location=locations[:, np.newaxis, np.newaxis],
...     obstime=obstimes[np.newaxis, np.newaxis, :])
>>> altaz = coords[np.newaxis, :, np.newaxis].transform_to(frame)  

>>> min_altitude = 30 * u.deg
>>> is_above_altitude_limit = (altaz.alt > min_altitude)  
>>> is_above_altitude_limit.shape  
(25, 100, 50)

Updates to cosmology#

Writing to LaTeX#

The Cosmology class in cosmology now supports the latex format in its write() method, allowing users to export a cosmology object to a LaTeX table.:

>>> from astropy.cosmology import Planck18
>>> Planck18.write("example_cosmology.tex", format="ascii.latex")

This will write the cosmology object to a file in LaTeX format, with appropriate formatting of units and table alignment.

Renaming fields in I/O#

Most I/O methods in cosmology (accessed by read(), write(), from_format(), to_format()) now support renaming fields.

For example, to rename the H0 field to Hubble when converting to a table format:

>>> from astropy.cosmology import Planck18
>>> Planck18.to_format("astropy.table")  # No renaming
<QTable length=1>
  name        H0        Om0    Tcmb0    Neff      m_nu      Ob0
         km / (Mpc s)            K                 eV
  str8     float64    float64 float64 float64  float64[3] float64
-------- ------------ ------- ------- ------- ----------- -------
Planck18        67.66 0.30966  2.7255   3.046 0.0 .. 0.06 0.04897

>>> Planck18.to_format("astropy.table", rename={"H0": "Hubble"})
<QTable length=1>
  name      Hubble      Om0    Tcmb0    Neff      m_nu      Ob0
         km / (Mpc s)            K                 eV
  str8     float64    float64 float64 float64  float64[3] float64
-------- ------------ ------- ------- ------- ----------- -------
Planck18        67.66 0.30966  2.7255   3.046 0.0 .. 0.06 0.04897

New properties to access Cosmology parameters#

The Cosmology class now has a new property to access the parameters of the cosmology: parameters. This property return a MappingProxyType object, which is a read-only dictionary of all the non-derived parameter values on the Cosmology instance. For example:

>>> from astropy.cosmology import Planck18
>>> Planck18.parameters["H0"]
<Quantity 67.66 km / (Mpc s)>

When accessed from the cosmology class itself, the returned dictionary is not the parameter values but Parameter objects with information about the parameter used when setting up the cosmology:

>>> from astropy.cosmology import FlatLambdaCDM
>>> FlatLambdaCDM.parameters["H0"]
Parameter(derived=False, unit=Unit("km / (Mpc s)"), equivalencies=[], ...)

Parameter as a dataclass()#

The Parameter class is now a dataclass(). This means that the dataclasses machinery can be used to work with Parameter objects. For example:

>>> from dataclasses import replace
>>> from astropy.cosmology import FlatLambdaCDM
>>> m_nu = FlatLambdaCDM.parameters["m_nu"]
>>> m_nu
Parameter(default=<Quantity 0. eV>, derived=False, unit=Unit("eV"), ...)
>>> replace(m_nu, derived=True)
Parameter(default=<Quantity 0. eV>, derived=True, unit=Unit("eV"), ...)

>>> from dataclasses import asdict
>>> asdict(m_nu)
{'default': <Quantity 0. eV>, 'derived': False, 'unit': Unit("eV"), ...}

It’s also much easier to create new Parameter subclasses

>>> from dataclasses import make_dataclass, field, fields
>>> from astropy.cosmology import Parameter
>>> NewP = make_dataclass("NewP", [("newfield", float, field(default=None))], bases=(Parameter,), frozen=True)
>>> tuple(f.name for f in fields(NewP))
(..., 'newfield')

Updates to how IERS data are handled#

Some parts of astropy, such as coordinate and time transformations, rely on tables from the International Earth Rotation and Reference Systems (IERS) service (these are the IERS-A, IERS-B, and leap second tables). IERS-A used to always be automatically downloaded, whereas the IERS-B and leap second tables were bundled with astropy. All tables are now bundled in the standalone astropy-iers-data package which is regularly updated and installed automatically when astropy is installed.

The main benefit of moving the files to this package is to make it easier to use astropy without an internet connection, and to facilitate updating the tables if needed. Users that want to ensure they have the latest available IERS data can now install the latest version of the astropy-iers-data package using pip or conda, or alternatively download the package manually and transfer it to a computer that has no public internet connection.

Masked Time values now use Masked arrays internally#

Time can now be initialized with masked input that either uses Masked, from astropy’s astropy.utils.masked package, or numpy.ma.MaskedArray, from numpy, and will now use Masked jd1 and jd2 internally to represent the mask. As a result, all output from masked Time instances will now be masked as well. For instance, converting a TimeDelta to a Quantity will give a masked quantity (instead of a regular quantity with masked entries set to np.nan).

Small example:

>>> from astropy.time import Time
>>> from astropy.utils.masked import Masked
>>> t = Time(Masked([52000., 52001, 52002], mask=[False, True, False]), format='mjd')
>>> t
<Time object: scale='utc' format='mjd' value=[52000.      ——— 52002. ]>
>>> t.isot
MaskedNDArray(['2001-04-01T00:00:00.000',                       ———,
               '2001-04-03T00:00:00.000'], dtype='<U23')
>>> (t-t[0]).to('s')
<MaskedQuantity [     0.,     ———, 172800.] s>

Note

The type of masked output will now be astropy’s Masked. For backward compatibility, a configuration item, masked_array_type, allows one to choose the type of masked array, with “astropy” (default) to always use Masked, and “numpy” to use MaskedArray when possible.

Reading and writing VO model annotations#

Model Instances in VOTables (MIVOT) defines a syntax to map VOTable data to any model serialised in VO-DML (Virtual Observatory Data Modeling Language). The data model elements are grouped in an independent annotation block complying with the MIVOT XML schema which is added as an extra resource above the table element. In Astropy, the MIVOT block is implemented as a new component of the Resource element (MivotBlock class). MivotBlock instances can only be held by resources with “type=meta”. In this new feature, Astropy is able to read and write MIVOT annotations from and within VOTables. There is no function processing data models, they will be delegated to affiliated packages such as PyVO.

See Reading and writing VO model annotations for more details.

TimeDelta string format “quantity_str”#

A new TimeDelta format "quantity_str" is now available that represents the time delta as a string with one or more Quantity components. This format provides a human-readable multi-scale string representation of a time delta. It is convenient for applications like a configuration file or a command line option.

Warning

The default output format is not yet finalized and may change in version 6.1 of astropy. The input format is stable. Please see issue 15485 for more details.

The format is a string with one or more time Quantity components separated by optional whitespace, for example "1yr 2d 3hr 4min 5.6s". In more detail:

  • The string is a sequence of one or more components.

  • Each component is a number followed by an astropy unit of time.

  • For input, whitespace within the string is allowed but optional.

  • For output, there is a single space between components.

  • The order (yr, d, hr, min, s) is fixed but individual components are optional.

The allowed component units are shown below and correspond to scaling relations defined by the astropy units:

  • “yr”: years (365.25 days)

  • “d”: days (24 hours)

  • “hr”: hours (60 minutes)

  • “min”: minutes (60 seconds)

  • “s”: seconds

Note

These definitions correspond to physical units of time and are NOT calendar date intervals. Thus adding “1yr” to “2000-01-01 00:00:00” will give “2000-12-31 06:00:00” instead of “2001-01-01 00:00:00”.

See TimeDeltaQuantityString for more details.

VOTable now supports PARQUET serialization#

The PARQUET file format allows a more efficient handling of large data amounts. However, one problem of PARQUET is that it only provides a limited number of column metadata keywords. A way to make it consistent with VO standards is to embed it into a VOTable file.

This serialization works similar to the VOTable FITS serialization that already existed. It basically creates two files, on VOTable file and one PARQUET file, which are linked together. The advantage of this method is that any column metadata can be saved along with the PARQUET file, following VO standards.

Reading and writing of the VOTable PARQUET serialization is fully supported by astropy.io.votable and the unified Table read/write interface. This serialization can be used by setting the format argument to 'votable.parquet', while 'votable' can be used for reading in such a file. The method works for both absolute and relative parquet file paths.

Example for writing:

>>> import numpy as np
>>> from astropy.table import Table
>>>
>>> # Create some fake data
>>> number_of_objects = 10
>>> ids = [f"COSMOS_{ii:03g}" for ii in range(number_of_objects)]
>>> redshift = np.random.uniform(low=0, high=3, size=number_of_objects)
>>> mass = np.random.uniform(low=1e8, high=1e10, size=number_of_objects)
>>> sfr = np.random.uniform(low=1, high=100, size=number_of_objects)
>>> cosmos = Table([ids, redshift, mass, sfr], names=["id", "z", "mass", "sfr"])
>>>
>>> # Create Column metadata
>>> column_metadata = {
...    "id": {"unit": "", "ucd": "meta.id", "utype": "none"},
...    "z": {"unit": "", "ucd": "src.redshift", "utype": "none"},
...    "mass": {"unit": "solMass", "ucd": "phys.mass", "utype": "none"},
...    "sfr": {"unit": "solMass / yr", "ucd": "phys.SFR", "utype": "none"},
... }
>>>
>>> # Write VOTable with Parquet serialization
>>> filename = "votable_with_parquet.vot"
>>> cosmos.write(filename, column_metadata=column_metadata, format="votable.parquet")

Example for reading a votable with a separate parquet file. Note the metadata is accessible on the column level:

>>> from astropy.table import Table
>>>
>>> # Open VOTable with PARQUET serialization
>>> cosmos_table = Table.read("votable_with_parquet.vot", format='votable')
>>>
>>> cosmos_table
<Table length=10>
    id             z                 mass               sfr
   ---            ---              solMass          solMass / yr
  str10         float64            float64            float64
---------- ------------------ ------------------ ------------------
COSMOS_000 0.2399334343209477 3777315779.8348713  31.82322447540133
COSMOS_001 0.0647935880275512   9392519748.07293  4.532295061239315
COSMOS_002  2.738748364941223 4411572229.2340555  86.54423711854747
COSMOS_003  2.013180712201346  2813958500.958293 11.142967938935586
COSMOS_004 2.5044578163101794   1533373563.21987    48.320129287388
COSMOS_005 2.1113936608027988  7431616021.640879  29.67334486542601
COSMOS_006 1.5525290310888193   8700624063.99011  36.19567476784732
COSMOS_007 2.5879551130469074  6501853315.057587  54.19908247198407
COSMOS_008 0.3978276727610941 1865149084.3401675  76.53909767648796
COSMOS_009 1.5021072916190177  4394424029.923725  91.68600618578257
>>>
>>> # Check out fields and column metadata
>>> cosmos_table['sfr'].meta
OrderedDict([('ucd', 'phys.SFR'), ('utype', 'none')])

Faster FITS file decompression#

By default compressed FITS files (with gzip, bzip2 or zip) are decompressed progressively depending on what data is needed. This allows to limit the memory usage when accessing only some part of a file but it can be much slower than decompressing the whole file at once. With the new keyword fits.open(..., decompress_in_memory=True) it is now possible to decompress the whole file in memory, which will be faster in some cases.

New GeneralSersic2D model#

A new GeneralSersic2D model has been added to the modeling package. This model is a generalized two dimensional Sersic surface brightness profile that allows for “boxy” or “disky” (kite-like) isophote shapes.

Full change log#

To see a detailed list of all changes in version v6.0, including changes in API, please see the Full Changelog.

Contributors to the v6.0 release#

The people who have contributed to the code for this release are:

  • AMHermansen *

  • Alexandre R. Bomfim Junior *

  • Alpha-Ursae-Minoris *

  • Andreas Faisst *

  • arthurxvtv *

  • Benjamin Alan Weaver

  • Bill Cleveland *

  • Brett Morris

  • Brigitta Sipőcz

  • Caden Gobat

  • CaioCoutinhoP *

  • Chiara Marmo

  • Clément Robert

  • Dany Vohl *

  • David Stansby

  • Derek Homeier

  • Doron Behar *

  • E.C. Herenz *

  • Eero Vaher

  • Erik Tollerud

  • Hans Moritz Günther

  • Heinz-Alexander Fuetterer *

  • Hélvio Peixoto *

  • JP Maia *

  • James O’Keeffe *

  • Jero Bado

  • Larry Bradley

  • Laurent MICHEL *

  • Leo Singer

  • Manon Marchand

  • Marcello Nascif *

  • Marten van Kerkwijk

  • MatCat776 *

  • Matteo Bachetti

  • Matthew Craig

  • Michele Peresano *

  • Mihai Cara

  • Mubin Manasia

  • Nadia Dencheva

  • Nathaniel Starkman

  • Ole Streicher

      1. Lim

  • Prajwel Joseph *

  • Raghuram Devarakonda *

  • Roy Smart

  • Saransh Chopra *

  • Simon Conseil

  • Somia Floret *

  • Stuart Mumford

  • Thais Borges *

  • Thomas Robitaille

  • Timothy P. Ellsworth Bowers

  • Tom Aldcroft

  • William Jamieson

Where a * indicates that this release contains their first contribution to astropy.