Repository: sunpy/sunpy

Issue:
Document that carrington_rotation_time accepts arrays as input
https://docs.sunpy.org/en/stable/api/sunpy.coordinates.sun.carrington_rotation_time.html currently just says it accepts a `float` as input, so I thought I would have to loop through my array and call it one value at a time. I tried it with an array though, and it seemed to work fine, so we should document that arrays are also a valid input.

In addition, if there isn't already a test that uses this function with an array input, we should add one.


Discussion:

Resolving patch:
diff --git a/sunpy/coordinates/sun.py b/sunpy/coordinates/sun.py
--- a/sunpy/coordinates/sun.py
+++ b/sunpy/coordinates/sun.py
@@ -22,6 +22,7 @@
 from astropy.coordinates.representation import CartesianRepresentation, SphericalRepresentation
 # Import erfa via astropy to make sure we are using the same ERFA library as Astropy
 from astropy.coordinates.sky_coordinate import erfa
+from astropy.tests.helper import quantity_allclose
 from astropy.time import Time
 
 from sunpy import log
@@ -91,23 +92,52 @@ def sky_position(t='now', equinox_of_date=True):
     return ra, dec
 
 
-def carrington_rotation_time(crot):
+@u.quantity_input
+def carrington_rotation_time(crot, longitude: u.deg = None):
     """
     Return the time of a given Carrington rotation.
 
+    Fractional Carrington rotation numbers can be provided in two ways:
+    * Fractional numbers to ``crot``
+    * Integer numbers to ``crot`` and a Carrington longitude to ``longitude``
+
+    Inputs can be arrays.  If both ``crot`` and ``longitude`` are provided, the
+    output shape will be the broadcasted combination.
     The round-trip from this method to `carrington_rotation_number` has
     absolute errors of < 0.11 seconds.
 
     Parameters
     ----------
-    crot : float
-        Carrington rotation number. Can be a fractional cycle number.
+    crot : `int`, `float`, `~astropy.units.Quantity`
+        Carrington rotation number(s). Can be a fractional rotation number.
+
+    longitude : `~astropy.units.Quantity`
+        Carrington longitude(s), which must be > 0 degrees and <= 360 degrees.
+        If provided, ``crot`` must be strictly integral.
 
     Returns
     -------
-    astropy.time.Time
-    """
-    estimate = (constants.mean_synodic_period.to_value('day') *
+    `astropy.time.Time`
+
+    Examples
+    --------
+    >>> from sunpy.coordinates.sun import carrington_rotation_time
+    >>> import astropy.units as u
+    >>> carrington_rotation_time(2242)
+    <Time object: scale='utc' format='iso' value=2021-03-17 22:31:37.030>
+    >>> carrington_rotation_time(2000.25)
+    <Time object: scale='utc' format='iso' value=2003-02-27 02:52:57.315>
+    >>> carrington_rotation_time(2000, 270*u.deg)
+    <Time object: scale='utc' format='iso' value=2003-02-27 02:52:57.315>
+    """
+    crot = crot << u.one
+    if longitude is not None:
+        if not quantity_allclose(crot%1, 0):
+            raise ValueError("Carrington rotation number(s) must be integral if `longitude` is provided.")
+        if (longitude <= 0*u.deg).any() or (longitude > 360*u.deg).any():
+            raise ValueError("Carrington longitude(s) must be > 0 degrees and <= 360 degrees.")
+        crot = crot + (1 - longitude/(360*u.deg))
+    estimate = (constants.mean_synodic_period *
                 (crot - 1)) + constants.first_carrington_rotation
 
     # The above estimate is inaccurate (see comments below in carrington_rotation_number),


Test patch:
diff --git a/sunpy/coordinates/tests/test_sun.py b/sunpy/coordinates/tests/test_sun.py
--- a/sunpy/coordinates/tests/test_sun.py
+++ b/sunpy/coordinates/tests/test_sun.py
@@ -1,5 +1,6 @@
 import warnings
 
+import numpy as np
 import pytest
 from numpy.testing import assert_allclose
 
@@ -472,6 +473,39 @@ def test_carrington_rotation_starttime(crot, julian_days):
                                  julian_days * u.day, atol=0.11*u.s)
 
 
+@pytest.mark.parametrize("crot, longitude, crot_fractional",
+                         [(2000, 360, 2000.0),
+                          (2000.0, 270, 2000.25)
+                          ])
+def test_carrington_rotation_time_longitude(crot, longitude, crot_fractional):
+    assert sun.carrington_rotation_time(crot*u.one, longitude*u.deg) == \
+        sun.carrington_rotation_time(crot_fractional*u.one)
+
+
+@pytest.mark.parametrize("crot, longitude, crot_fractional",
+                         [
+                             (np.array([2000, 2000]), np.array(
+                                 [180, 90]), np.array([2000.5, 2000.75])),
+                             (2000, np.array([180, 90]), np.array([2000.5, 2000.75])),
+                             (np.array([2000, 2000]), 180, np.array([2000.5, 2000.5]))
+                         ])
+def test_carrington_rotation_time_longitude_numpy(crot, longitude, crot_fractional):
+    assert all(sun.carrington_rotation_time(crot*u.one, longitude*u.deg) ==
+               sun.carrington_rotation_time(crot_fractional*u.one))
+
+
+@pytest.mark.parametrize("crot, longitude",
+                         [
+                             (2000, 0),
+                             (2000, -10),
+                             (2000, 400),
+                             (2000.5, 180),
+                         ])
+def test_carrington_rotation_time_longitude_err(crot, longitude):
+    with pytest.raises(ValueError):
+        sun.carrington_rotation_time(crot*u.one, longitude*u.deg)
+
+
 def test_carrington_rotation_roundtrip():
     t = Time('2010-1-1')
     crot = sun.carrington_rotation_number(t)