.. _whatsnew-8.0:

**************************
What's New in Astropy 8.0?
**************************

Overview
========

Astropy 8.0 is a release that adds significant new functionality since
the 7.2 release.

In particular, this release includes:

* :ref:`whatsnew-8.0-cosmology`
* :ref:`whatsnew-8.0-codata2022`
* :ref:`whatsnew-8.0-table-formats`
* :ref:`whatsnew-8.0-paths-discovery`
* :ref:`whatsnew-8.0-write-table-indices`
* :ref:`whatsnew-8.0-vla-logical`
* :ref:`whatsnew-8.0-timeseries-lra`
* :ref:`whatsnew-8.0-logical-as-bytes`
* :ref:`whatsnew-8.0-table-display`
* :ref:`whatsnew-8.0-fits-strings`
* :ref:`whatsnew-8.0-visualization`
* :ref:`whatsnew-8.0-quantity-strings`
* :ref:`whatsnew-8.0-deprecations`
* :ref:`whatsnew-8.0-dependencies`

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

.. release-summary-start

* 1014 commits have been added since v7.2
* 179 issues have been closed since v7.2
* 364 pull requests have been merged since v7.2
* 70 people have contributed since v7.2
* 37 of which are new contributors

.. release-summary-end

Full change log
===============

To see a detailed list of all changes in version v8.0, including changes in
API, please see the :ref:`changelog`.


.. _whatsnew-8.0-cosmology:

Cosmology
=========

The ``astropy.cosmology`` module has several updates in this release.

Module Simplification
---------------------

The deprecated module-level shim files in ``astropy.cosmology`` have been removed. These
compatibility modules were originally deprecated in v7.1. With this release, all
cosmology classes and functions must be imported directly from ``astropy.cosmology``.

The following deprecated modules have been removed:

- ``astropy.cosmology.connect``
- ``astropy.cosmology.core``
- ``astropy.cosmology.flrw``
- ``astropy.cosmology.funcs``
- ``astropy.cosmology.parameter``

**Migration Guide**

If your code was still using the old import paths, you need to update them to import
directly from ``astropy.cosmology``.

Before:

.. code-block:: python

    from astropy.cosmology.flrw import FlatLambdaCDM
    from astropy.cosmology.core import Cosmology
    from astropy.cosmology.funcs import z_at_value

After:

.. code-block:: python

    from astropy.cosmology import Cosmology, FlatLambdaCDM, z_at_value

Cosmology Traits
----------------

.. currentmodule:: astropy.cosmology.traits

The :mod:`~astropy.cosmology.traits` module provides reusable components, called
:term:`traits <trait type>`, that encapsulate specific cosmological properties or
behaviors. For example, the :class:`~astropy.cosmology.traits.NeutrinoComponent` trait
provides the neutrino component and related methods.
By combining these traits, you can easily construct custom cosmology classes with
precisely the features you need, without having to reimplement common functionality.
Here is an example of how to use the
:class:`~astropy.cosmology.traits.NeutrinoComponent` trait in custom cosmology classes:

>>> import numpy as np
>>> from astropy import units as u
>>> from astropy.cosmology import Cosmology
>>> from astropy.cosmology.traits import TemperatureCMB, NeutrinoComponent
>>> NEUTRINO_FERMI_DIRAC_CORRECTION = 0.22710731766  # 7/8 (4/11)^4/3
>>>
>>> class SimpleNeutrinoCosmology(Cosmology, TemperatureCMB, NeutrinoComponent):
...     '''Minimal cosmology demonstrating NeutrinoComponent trait.'''
...     def __init__(self, Tcmb0=2.7255, Neff=3.046, Ogamma0=5e-5):
...         self.Tcmb0 = u.Quantity(Tcmb0, "K")
...         self.Neff = Neff
...         self.Ogamma0 = Ogamma0
...         super().__init__()
...     @property
...     def has_massive_nu(self):
...         return False
...     @property
...     def Onu0(self):
...         return NEUTRINO_FERMI_DIRAC_CORRECTION * self.Neff * self.Ogamma0
...     def nu_relative_density(self, z):
...         return NEUTRINO_FERMI_DIRAC_CORRECTION * self.Neff * np.ones_like(np.asarray(z))
...     def Ogamma(self, z):
...         return self.Ogamma0 * (np.asarray(z) + 1.0) ** 4
...     @property
...     def is_flat(self):
...         return True
>>>
>>> cosmo = SimpleNeutrinoCosmology()
>>> cosmo.Tnu0
<Quantity 1.94... K>
>>> cosmo.has_massive_nu
False
>>> cosmo.Onu0
3.4...e-05
>>> cosmo.Onu(0)
np.float64(3.4...e-05)
>>> cosmo.Tnu(1)
<Quantity 3.89... K>

Machine Readable Table (MRT) I/O
--------------------------------

The ``astropy.cosmology`` module now supports reading and writing cosmological
objects in the Machine Readable Table (MRT) format. This adds MRT to the
existing I/O options for cosmology, which include YAML, ECSV, HTML, and LaTeX
formats.

MRT format is commonly used in astronomical publications and data archives. You
can now save and load cosmological parameters using the ``.read()`` and
``.write()`` methods with ``format="ascii.mrt"``:

.. code-block:: python

    from astropy.cosmology import Planck18

    # Write to MRT file
    Planck18.write("cosmology.mrt")

    # Read from MRT file
    cosmo = Cosmology.read("cosmology.mrt")

Note that since MRT format does not preserve table metadata, the cosmology class
is included as a column in the table rather than in metadata (similar to HTML
and LaTeX formats). Multi-dimensional columns (such as neutrino masses) are
automatically JSON-encoded when writing and decoded when reading.

Angular diameter distance between two redshifts
-----------------------------------------------

The :meth:`~astropy.cosmology.FLRW.angular_diameter_distance` method now accepts
either one or two redshift arguments. When two redshifts are supplied, it
returns the angular diameter distance from an object at ``z1`` to an object at
``z2``, replacing the use of the now-deprecated
``angular_diameter_distance_z1z2`` method:

.. code-block:: python

    from astropy.cosmology import Planck18

    Planck18.angular_diameter_distance(0.5)        # observer to z=0.5
    Planck18.angular_diameter_distance(0.5, 2.0)   # z=0.5 to z=2.0


.. _whatsnew-8.0-codata2022:

CODATA 2022 is now default in constants
=======================================

CODATA 2022 has replaced CODATA 2018 as default. ``astropyconst80`` is
available for ``constants`` science state, combining CODATA 2022 with
IAU 2015. To use previous version of CODATA, see :ref:`astropy-constants-prior`.

.. _whatsnew-8.0-table-formats:

Table formats
=============
A new table reader has been added to read history and profile output files from `MESA
<https://mesastar.org>`_, a well-known code for stellar evolution calculations.

.. _whatsnew-8.0-paths-discovery:

Overhaul of cache and configuration path discovery mechanism
============================================================

Cache and configuration directories now adhere to the XDG specification
by default. For example, the default cache location was changed from
``$HOME/.astropy/cache`` to ``$XDG_CACHE_HOME/astropy``, where
``XDG_CACHE_HOME`` itself defaults to ``$HOME/.cache``.
Legacy cache and configuration default locations are still silently selected
if present and in case the new default locations do not exist.

In addition, temporary directories set through ``set_temp_cache``
or ``set_temp_config`` will now not be symlinked to default locations.
The new behavior is also meant as 100% cross platform: Windows isn't
special cased like it used to.

``set_temp_cache`` and ``set_temp_config`` themselves (hereafter, legacy APIs) are now
soft-deprecated, meaning their use is discouraged for new code but will not emit
deprecation warnings, in favor of new, similar, but *not* drop-in replacement APIS:
``temporary_cache_dir_path`` and ``temporary_config_dir_path`` respectively. These
alternative functions come with a couple differences in design:

- easily predictable behavior
- safer defaults:

  - temporary files and directories are not leaked by default (``delete=True`` is the default)
  - pre-existing contents are never deleted (because the final dir is always *created*)

- ``delete`` is keyword-only
- ``dir_`` (path) is positional only
- support ``suffix`` and ``prefix``
- ``yield`` value is a ``pathlib.Path`` (not a `str`)
- the ``namespace`` argument is keyword-only and required, whereas legacy API's equivalent argument (``rootname``) was allowed positionally and defaulted to ``'astropy'``, which contributed to confusion surrounding these APIs.

``ASTROPY_CACHE_DIR`` and ``ASTROPY_CONFIG_DIR`` environment variables are now supported,
allowing finer, tool-specific control as compared with ``XDG_CACHE_HOME`` and
``XDG_CONFIG_HOME`` which will only be used if astropy-specific environment variables
are undefined or invalid (e.g., defined as relative paths or pointing to files).


.. _whatsnew-8.0-write-table-indices:

Write table indices to FITS, HDF5 and ECSV formats
==================================================

It is now possible to write `~astropy.table.Table` indices to FITS, HDF5, and ECSV
formats and subsequently read them back in order to round-trip the original indexed
table. This stores the index data as temporary columns in the output and includes other
relevant metadata in the file output, similar to the mechanism for serializing mixin
columns like ``SkyCoord``.

This functionality is enabled by passing ``write_indices=True`` to the table
`~astropy.table.Table.write` method. For example:

>>> import io
>>> from astropy.table import QTable
>>> t = QTable({"a": ["x", "z", "y"], "b": [2.0, 1.0, 1.5], "c": ["a", "b", "c"]})
>>> t.add_index("a")
>>> out = io.StringIO()
>>> t.write(out, format="ecsv", write_indices=True)
>>> t2 = QTable.read(out.getvalue(), format="ecsv")
>>> t2.loc["x"]
<Row index=0>
 a      b     c
str1 float64 str1
---- ------- ----
   x     2.0    a

.. _whatsnew-8.0-vla-logical:

Variable-length array logical columns in FITS
==============================================

Variable-length array columns of FITS logical type (``PL``/``QL``) are now
read and written correctly. Previously, bool input was written as raw 1/0
bytes rather than the ASCII ``T``/``F`` bytes required by the FITS
specification, and on read these columns were exposed as ``int8`` arrays
containing the byte values (84 for ``T``, 70 for ``F``) instead of as bool.
Files written by astropy <= 7.2.0 are still readable: the legacy
``0x00``/``0x01`` encoding is auto-detected on read and decoded as bool,
with an `~astropy.utils.exceptions.AstropyUserWarning` so that users are
aware the file does not strictly conform to the FITS standard.

For example::

    >>> from astropy.io import fits
    >>> col = fits.Column(name="a", format="PL", array=[[True, False, True]])
    >>> hdu = fits.BinTableHDU.from_columns([col])
    >>> hdu.writeto("example.fits", overwrite=True)  # doctest: +SKIP
    >>> fits.getdata("example.fits")["a"][0]  # doctest: +SKIP
    array([ True, False,  True])

.. _whatsnew-8.0-timeseries-lra:

Fast Lomb-Scargle: Low Rank Approximation
=========================================

The fast Lomb-Scargle Periodograms (``fast`` and ``fastchi2`` implementations)
now use Low Rank Approximation (LRA) algorithm by default.
The LRA implementation may be a little slower than the previous fast
implementation (although this is architecture-dependent) but is 8 to 10
orders of magnitude more accurate, on par with their slow (non ``fast``) counterparts.

This change of default allows eliminating previously seen issues with negative powers.

.. _whatsnew-8.0-logical-as-bytes:

Preserving NULL values in FITS logical columns
==============================================

The FITS standard allows logical (``L`` / ``PL`` / ``QL``) column entries
to take three byte values: ``b'T'`` (True), ``b'F'`` (False), or
``b'\x00'`` (undefined / NULL). Astropy has historically converted such
columns to a numpy ``bool`` array on read, which silently maps NULL to
``False``.

A new ``logical_as_bytes`` keyword argument has been added to
:func:`~astropy.io.fits.open` (mirroring the existing
``character_as_bytes`` switch). When set to ``True``, both fixed-length
and variable-length logical columns are returned as ``|S1`` byte arrays
rather than ``bool``, so ``b'\x00'`` is distinguishable from ``b'F'``.
Bytes read this way also round-trip through writing: a column read as
``|S1`` and written back to a FITS file preserves NULL bytes verbatim.

A `~astropy.utils.exceptions.AstropyUserWarning` is now also issued
whenever a logical column being read with ``logical_as_bytes=False``
(the default) contains NULL bytes, alerting users to lost information.

.. _whatsnew-8.0-table-display:

Table display and pandas conversion
===================================

Two improvements make tables with multidimensional columns easier to work with.

A new ``conf.multidim_threshold`` configuration option controls how
multidimensional columns are displayed. Setting it to a small value (for
example, ``3``) shows full content for short arrays such as 3-vectors
(``[1 2 3]``), while larger arrays still fall back to the abbreviated
``first .. last`` representation.

In addition, :meth:`~astropy.table.Table.to_pandas` now accepts
tables with multidimensional columns: previously this would raise a
``ValueError``, but they are now converted to 1D object arrays compatible
with pandas.


.. _whatsnew-8.0-fits-strings:

FITS string column changes
==========================

A few changes affect how string columns are read from FITS binary tables.

When reading FITS files via :meth:`~astropy.table.Table.read`, the
``strip_spaces`` option now defaults to ``True``, so trailing whitespace in
fixed-width string columns is stripped automatically. Pass
``strip_spaces=False`` to recover the previous behavior.

In preparation for the deprecation of ``np.char.chararray`` in NumPy 2.5,
``astropy.io.fits`` will in a future release return ordinary NumPy arrays
rather than ``chararray`` instances for string columns. Code that relies on
``chararray``-specific methods (such as ``.rstrip()`` or ``.decode()``)
should migrate to the equivalent functions in ``np.strings``.

.. _whatsnew-8.0-visualization:

New imshow_simple_norm convenience function
===========================================

A new :func:`~astropy.visualization.imshow_simple_norm` function provides a
compact one-call alternative to building a ``SimpleNorm``/``simple_norm`` and
passing it to ``imshow``, which is convenient for quick image previews:

.. code-block:: python

    from astropy.visualization import imshow_simple_norm

    imshow_simple_norm(image, stretch="asinh", percent=99.5)

.. _whatsnew-8.0-quantity-strings:

Parsing vector strings into Quantity
====================================

It is now possible to construct a :class:`~astropy.units.Quantity` from a
1D vector-like string, with or without units:

.. code-block:: python

    from astropy import units as u

    u.Quantity("[1, 2, 3] m")
    u.Quantity("1, 2, 3", unit="m")

.. _whatsnew-8.0-deprecations:

Deprecation of the built-in test runner
=======================================

The ``astropy.test()`` entry point and the
``~astropy.tests.runner.TestRunner`` /
``~astropy.tests.runner.TestRunnerBase`` classes are now formally
deprecated and will be removed in a future release. Downstream packages that
expose a ``packagename.test()`` function generated via ``TestRunner`` are
affected and should migrate to invoking ``pytest`` directly. The previously
deprecated ``astropy.tests.command`` module has been removed in this release.

Deprecation of astropy.samp
===========================

The ``astropy.samp`` module has been moved downstream into the PyVO package and is available since
the PyVO v1.9 release. Therefore it is now deprecated in astropy itself.

.. _whatsnew-8.0-dependencies:

NumPy 2.0 is now required
=========================

The minimum supported NumPy version is now **2.0**. Code and downstream
packages that still rely on the NumPy 1.x C API or the Python-level
behaviours that changed in NumPy 2.0 will need to be updated.

For users that cannot yet move to NumPy 2.0, the **7.2.x** series of releases
will continue to receive support for six months following the 8.0 release.
That branch remains compatible with NumPy 1.

Contributors to the 8.0 release
===============================

.. release-contributors-start

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

.. hlist::
  :columns: 4

  -  Adam Ginsburg
  -  Aditya Lohuni  *
  -  Albert Y. Shih
  -  Ana Clara Galvão  *
  -  Arthur Sardella  *
  -  Ben Green  *
  -  Bill Wolf  *
  -  Bodhi Silberling  *
  -  Brett Morris
  -  Brigitta Sipőcz
  -  Bruno Sanchez  *
  -  Charalampos Stratakis  *
  -  Clara Brasseur
  -  Clément Robert
  -  Curtis McCully
  -  Cyrus  *
  -  Dhruv Yadav  *
  -  Duncan Macleod
  -  Eero Vaher
  -  Erik Tollerud
  -  Evan Chen  *
  -  Fazeel Usmani  *
  -  Hans Moritz Günther
  -  Ikbar Faiz  *
  -  J. Berg  *
  -  Jett Higgins  *
  -  Joren Hammudoglu  *
  -  Julian Harbeck  *
  -  Kacper Rutkowski  *
  -  Kang Wang
  -  Kartavay Verma  *
  -  Kyle Oman
  -  Larry Bradley
  -  M Bussonnier  *
  -  Macdara Ó Murchú  *
  -  Marten van Kerkwijk
  -  Maximilian Linhoff
  -  Mohsin Mehmood
  -  Nabil Freij
  -  Nadia Dencheva
  -  Naksh Yadav  *
  -  Nathaniel Starkman
  -  Om S Habib  *
  -  P. L. Lim
  -  Pierre Sassoulas  *
  -  Preshanth Jagannathan
  -  R. Virinchi  *
  -  Raphael Erik Hviding
  -  Reem Hamraz  *
  -  RinZ27  *
  -  Simon Conseil
  -  Stelios Voutsinas
  -  Stuart Mumford
  -  Surya K.  *
  -  Syn Pu  *
  -  Thomas Dutkiewicz  *
  -  Thomas Robitaille
  -  Tiago Gomes
  -  Tim Jenness
  -  Tom Aldcroft
  -  Varun Kasyap Pentamaraju  *
  -  Varun Nikam
  -  Vishwas  *
  -  Wang Rui  *
  -  William Jamieson
  -  Yaocheng Chen  *
  -  gilles.landais  *
  -  github-actions
  -  xbreak  *
  -  Περικλής Παντελαίος  *

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

.. release-contributors-end
