.. _whatsnew-4.0:
**************************
What's New in Astropy 4.0?
**************************
Overview
========
Astropy 4.0 is a major release that ... since
the 3.2.x series of releases.
In particular, this release includes:
* :ref:`whatsnew-4.0-planck2018`
* :ref:`whatsnew-4.0-constants-units`
* :ref:`whatsnew-4.0-galactocentric`
* :ref:`whatsnew-4.0-time-ymdhms`
* :ref:`whatsnew-4.0-time-plotting`
* :ref:`whatsnew-4.0-time-high-precision`
* :ref:`whatsnew-4.0-time-leap-seconds`
* :ref:`whatsnew-4.0-quantity`
* :ref:`whatsnew-4.0-wcsaxes-1d`
* :ref:`whatsnew-4.0-wcsaxes-default`
* :ref:`whatsnew-4.0-wcs-fit`
* :ref:`whatsnew-4.0-wcs-time`
* :ref:`whatsnew-4.0-timeseries-fold`
* :ref:`whatsnew-4.0-table-new`
* :ref:`whatsnew-4.0-table-performance`
* :ref:`whatsnew-4.0-modeling-new-models`
* :ref:`whatsnew-4.0-cache`
* :ref:`whatsnew-4.0-modeling-api`
* :ref:`whatsnew-4.0-table-api`
* :ref:`whatsnew-4.0-uncertainty-api`
In addition to these major changes, Astropy v4.0 includes a large number of
smaller improvements and bug fixes, which are described in the
:ref:`changelog`. By the numbers:
* 948 issues have been closed since v3.2
* 493 pull requests have been merged since v3.2
* 74 distinct people have contributed code, 26 of which are first time contributors to Astropy
.. _whatsnew-4.0-planck2018:
Pre-Publication Planck 2018 Cosmological Parameters
===================================================
A pre-publication version of the Planck 2018 cosmological parameters
has been included based on the second version of the submitted paper. This
will be replaced with a final version when the paper is accepted.
.. doctest-requires:: scipy
>>> from astropy.cosmology import Planck18_arXiv_v2
>>> Planck18_arXiv_v2.age(0) # doctest: +FLOAT_CMP
.. _whatsnew-4.0-constants-units:
Improved Consistency of Physical Constants and Units
====================================================
Physical constants and the units using them are based on measurements that improve
over time and therefore are not "constant" numerically. Generally, you want to
use the latest values, but sometimes it is necessary to reproduce earlier
results by going back in time. For that purpose, we have now introduced new
``astropy.physical_constants`` and ``astropy.astronomical_constants`` science
state objects, which can be used to enable previous versions of the constants,
and to make sure that units and physical constants are self-consistent. For more
details, see :ref:`astropy-constants-prior`.
.. _whatsnew-4.0-galactocentric:
Updates to Galactocentric Frame
===============================
Most coordinate frames implemented in :ref:`astropy.coordinates
` have standard parameters that are set by IAU consensus
(e.g., the ``ICRS`` frame). Unlike these, the
`~astropy.coordinates.Galactocentric` coordinate frame does not have an absolute
definition: its parameters (the solar motion and position relative to the
Galactic center) are measurements that continue to be refined as newer stellar
surveys are executed and analyzed. When it was added, the default parameter
values used by the `~astropy.coordinates.Galactocentric` frame (i.e., the
parameter values assumed when defining a frame without explicitly setting
values, like ``galcen = Galactocentric()``) were set to commonly used values at
the time, but these are now somewhat out of date. With v4.0, we have added
functionality for globally controlling the default parameter values used by this
frame by setting the `~astropy.coordinates.galactocentric_frame_defaults` object
with the name of a parameter set. The parameter set names can currently be one
of ``"pre-v4.0"`` (to get the original, pre-version-4.0 values of the parameters),
``"v4.0"`` (to get a more modern set of values adopted in v4.0), and ``"latest"``
(which is currently an alias for ``"v4.0"`` and will always alias the most recent
set of parameters).
If your code depends sensitively on the choice of
`~astropy.coordinates.Galactocentric` frame parameters, make sure to explicitly
set the parameter set in your code, for example, after importing
:ref:`astropy.coordinates `::
>>> import astropy.coordinates as coord
>>> coord.galactocentric_frame_defaults.set('v4.0') # doctest: +IGNORE_OUTPUT
The `~astropy.coordinates.Galactocentric` frame now also maintains a list of
references to scientific papers for the default values of the frame attributes.
For example, after adopting the v4.0 parameter set and defining a frame, we can
retrieve the references (as a dictionary of links to ADS) for the parameters
using the ``.frame_attribute_references`` attribute::
>>> import astropy.coordinates as coord
>>> coord.galactocentric_frame_defaults.set('v4.0') # doctest: +IGNORE_OUTPUT
>>> galcen = coord.Galactocentric()
>>> galcen # doctest: +FLOAT_CMP
, galcen_distance=8.122 kpc, galcen_v_sun=(12.9, 245.6, 7.78) km / s, z_sun=20.8 pc, roll=0.0 deg)>
>>> galcen.frame_attribute_references
{'galcen_coord': 'http://adsabs.harvard.edu/abs/2004ApJ...616..872R',
'galcen_distance': 'https://ui.adsabs.harvard.edu/abs/2018A%26A...615L..15G',
'galcen_v_sun': ['https://ui.adsabs.harvard.edu/abs/2018RNAAS...2..210D',
'https://ui.adsabs.harvard.edu/abs/2018A%26A...615L..15G',
'https://ui.adsabs.harvard.edu/abs/2004ApJ...616..872R'],
'z_sun': 'https://ui.adsabs.harvard.edu/abs/2019MNRAS.482.1417B'}
Note, however, if a frame parameter is set by the user, it is removed from the
reference list::
>>> import astropy.units as u
>>> galcen = coord.Galactocentric(z_sun=10*u.pc)
>>> galcen.frame_attribute_references
{'galcen_coord': 'http://adsabs.harvard.edu/abs/2004ApJ...616..872R',
'galcen_distance': 'https://ui.adsabs.harvard.edu/abs/2018A%26A...615L..15G',
'galcen_v_sun': ['https://ui.adsabs.harvard.edu/abs/2018RNAAS...2..210D',
'https://ui.adsabs.harvard.edu/abs/2018A%26A...615L..15G',
'https://ui.adsabs.harvard.edu/abs/2004ApJ...616..872R']}
More information can be found in the documentation for the frame class:
`~astropy.coordinates.Galactocentric`.
.. _whatsnew-4.0-time-ymdhms:
New ``ymdhms`` Time Format
==========================
A new `~astropy.time.Time` format was added to allow convenient input and output
of times via year, month, day, hour, minute, and second values. For example::
>>> from astropy.time import Time
>>> t = Time({'year': 2015, 'month': 2, 'day': 3,
... 'hour': 12, 'minute': 13, 'second': 14.567})
>>> t.iso
'2015-02-03 12:13:14.567'
>>> t.ymdhms.year
2015
.. _whatsnew-4.0-time-plotting:
New Context Manager for Plotting Time Values
============================================
Matplotlib natively provides a mechanism for plotting dates and times on one
or both of the axes, as described in
`Date tick labels `_.
To make use of this, you can use the ``plot_date`` attribute of :class:`~astropy.time.Time` to get
values in the time system used by Matplotlib.
However, in many cases, you will probably want to have more control over the
precise scale and format to use for the tick labels, in which case you can make
use of the `~astropy.visualization.time_support` function which can be called
either directly or as a context manager, and after which :class:`~astropy.time.Time` objects can be
passed to matplotlib plotting functions. The axes are then automatically labeled
with times formatted using the :class:`~astropy.time.Time` class:
.. plot::
:include-source:
:context: reset
import matplotlib.pyplot as plt
from astropy.time import Time
from astropy.visualization import time_support
time_support(format='isot', scale='tai') # doctest: +IGNORE_OUTPUT
plt.figure(figsize=(5,3)) # doctest: +IGNORE_OUTPUT
plt.plot(Time([52000, 53000, 54000], format='mjd'), [1.2, 3.3, 2.3]) # doctest: +IGNORE_OUTPUT
For more information, see :ref:`plotting-times`.
.. _whatsnew-4.0-time-high-precision:
Support for Parsing High-Precision Values with Time
===================================================
For numerical formats, :class:`~astropy.time.Time` can now be instantiated
from strings, quadruple precision ``numpy`` floats (if available on a given
platform), and :class:`~decimal.Decimal` instances. For instance, in the example below
you can see how in a string we can give full precision, while entering the same
number as a float gives precision loss::
>>> from astropy.time import Time
>>> t = Time('2450000.123456789012345', format='jd')
>>> t - Time(2450000, 0.123456789012345, format='jd')
>>> t - Time(2450000.123456789012345, format='jd') # doctest: +FLOAT_CMP
You can also output values as string, etc.::
>>> t.to_value('jd', subfmt='str')
'2450000.123456789012345'
.. _whatsnew-4.0-time-leap-seconds:
Improved Handling of Leap Second Updates
========================================
``astropy`` now automatically checks for and applies new leap seconds the first
time a :class:`~astropy.time.Time` is instantiated. This is done with the new
:class:`~astropy.utils.iers.LeapSeconds` class, which can, in the hopefully
unlikely case it is needed, also be used directly.
.. _whatsnew-4.0-quantity:
Major Improvements in Compatibility of Quantity Objects with NumPy Functions
============================================================================
While :class:`~astropy.units.Quantity` objects have worked well in arithmetic
operations via ``numpy``'s "universal functions" (ufuncs), for other ``numpy``
functions it has been a bit hit and miss. For instance, units would be lost
when trying to concatenate quantities, make histograms, or in functions
such as `numpy.where`.
For ``numpy`` version 1.17 and later, however, it is possible to override the
behavior of ``numpy`` functions, and this is used in ``astropy`` 4.0 to make
essentially all functions work as expected with quantities. If a ``numpy``
function does not work as expected, it is now a bug that we can fix!
Two concise examples:
.. doctest-requires:: numpy>=1.17
>>> import numpy as np
>>> from astropy import units as u
>>> np.where([True, False, False], [1., 2., 3.]*u.m, 1.*u.cm)
>>> np.hstack(([1., 2., 3.]*u.m, 1.*u.cm))
.. note:: for NumPy 1.16, one can get the same behaviour by setting
environment variable ``NUMPY_EXPERIMENTAL_ARRAY_FUNCTION=1``.
For details, see
`NEP 18 `_
.. _whatsnew-4.0-wcsaxes-1d:
Plotting 1D Profile Plots with WCSAxes
=======================================
The :ref:`astropy.visualization.wcsaxes ` module now supports plotting
data with one-dimensional WCS (including 1D profiles extracted from higher
dimensional objects). The following example shows a plot of a 1D profile
extracted from a 3D spectral cube — because all world coordinates vary along
that slice, all three coordinates are still shown in the final plot:
.. plot::
:context: reset
:align: center
:include-source:
import matplotlib.pyplot as plt
import astropy.units as u
from astropy.wcs import WCS
from astropy.io import fits
from astropy.utils.data import get_pkg_data_filename
filename = get_pkg_data_filename('l1448/l1448_13co.fits')
hdu = fits.open(filename)[0]
ax = plt.subplot(projection=WCS(hdu.header), slices=(50, 'x', 'y'))
ax.imshow(hdu.data[:, :, 50])
.. _whatsnew-4.0-wcsaxes-default:
Default Labelling with WCSAxes
==============================
As seen in the example in :ref:`whatsnew-4.0-wcsaxes-1d`, axis
labels are now shown by default, using either the names of the
coordinates axes, if available, or the physical types of the
axes. To disable this, you can use::
ax.coords[0].set_auto_axislabel(False)
ax.coords[1].set_auto_axislabel(False)
ax.coords[2].set_auto_axislabel(False)
Or you can also set the axis label to what you want instead::
ax.coords[0].set_axislabel("Right Ascension")
ax.coords[1].set_axislabel("Declination")
ax.coords[2].set_axislabel("Velocity (m/s)")
.. _whatsnew-4.0-wcs-fit:
New Function to Fit WCS to Pairs of Pixel/World Coordinates
===========================================================
A new function :func:`astropy.wcs.utils.fit_wcs_from_points` has been added
to fit a (FITS) WCS to a set of points for which both pixel and world coordinates
are available. For instance, if we have an image in which four stars have known
pixel and celestial coordinates::
>>> import numpy as np
>>> from astropy.coordinates import SkyCoord
>>> stars_pixel = [np.array([153, 64, 593, 663]),
... np.array([581, 199, 190, 445])]
>>> stars_world = SkyCoord([266.729, 266.872, 266.031, 265.921],
... [-28.627, -29.156, -29.170, -28.815],
... unit='deg', frame='fk5')
we can find the best-fitting WCS with::
.. doctest-requires:: scipy
>>> from astropy.wcs.utils import fit_wcs_from_points
>>> fit_wcs_from_points(stars_pixel, stars_world) # doctest: +FLOAT_CMP
WCS Keywords
Number of WCS axes: 2
CTYPE : 'RA---TAN' 'DEC--TAN'
CRVAL : 266.3952562116589 -28.89933479456074
CRPIX : 364.8753661483385 385.9573650918672
CD1_1 CD1_2 : -0.001388743579175224 4.580696254922365e-08
CD2_1 CD2_2 : -8.098819668907673e-07 0.0013876745578212755
NAXIS : 599 391
.. _whatsnew-4.0-wcs-time:
Support for WCS Transformations between Pixel and Time Values
=============================================================
The :meth:`WCS.world_to_pixel ` and
:meth:`WCS.pixel_to_world ` methods can now
take and return :class:`~astropy.time.Time` objects for WCS transformations
that involve time::
>>> from astropy.io import fits
>>> from astropy.wcs import WCS
>>> header = fits.Header()
>>> header['CTYPE1'] = 'TIME'
>>> header['CDELT1'] = 86400.
>>> header['MJDREF'] = 58788.
>>> wcs = WCS(header)
>>> wcs.pixel_to_world([2, 3, 4])