Repository: astropy/astropy

Issue:
Can't compose units from equivalent units that have prefixes
For example, the following works:

``` python
>>> u.m.compose(units=[u.pc])
[Unit("3.24078e-17 pc")]
```

but this does not work:

``` python
>>> u.m.compose(units=[u.kpc])
ERROR: UnitsError: Cannot represent unit m in terms of the given units [astropy.units.core]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/internal/1/root/src/astropy/astropy/astropy/units/core.py", line 1226, in compose
    max_depth=max_depth, depth=0, cached_results={}))
  File "/internal/1/root/src/astropy/astropy/astropy/units/core.py", line 1099, in _compose
    raise result
astropy.units.core.UnitsError: Cannot represent unit m in terms of the given units
```

Or even more trivially:

``` python
>>> u.m.compose(units=[u.km])
ERROR: UnitsError: Cannot represent unit m in terms of the given units [astropy.units.core]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/internal/1/root/src/astropy/astropy/astropy/units/core.py", line 1226, in compose
    max_depth=max_depth, depth=0, cached_results={}))
  File "/internal/1/root/src/astropy/astropy/astropy/units/core.py", line 1099, in _compose
    raise result
astropy.units.core.UnitsError: Cannot represent unit m in terms of the given units
```

Of course, in the above examples the use of `compose` appears to be overkill, but this comes up in more general cases as well.


Discussion:
astropy-bot[bot]: Hi there @fastakash :wave: - thanks for the pull request! I'm just a friendly :robot: that checks for issues related to the changelog and making sure that this pull request is milestoned and labeled correctly. This is mainly intended for the maintainers, so if you are not a maintainer you can ignore this, and a maintainer will let you know if any action is required on your part :smiley:.

I noticed the following issues with this pull request:

* The milestone has not been set (this can only be set by a maintainer)
* Changelog entry not present (or pull request number missing) and neither the **Affects-dev** nor the **no-changelog-entry-needed** label are set

Would it be possible to fix these? Thanks!

*If there are any issues with this message, please report them [here](https://github.com/astropy/astropy-bot/issues).*
fastakash: Hi @bsipocz   can you please add the label work-in-progress?  I initially thought my work was fine but now see that a failing unit-test is because of the change I made.  I will investigate further.

Sorry for the PR being premature
fastakash: The label "work in progress" may now be removed.  Hoping for a review/comment next!
bsipocz: @fastakash - I've changed the title of your PR to be more informative about its content (as probably none of us knows the issues by their number).
fastakash: @mhvk Thanks for patiently explaining everything to me!  I hope I addressed all your feedback.  I will be more careful with reviewing all the existing unittests in the future.  I know this was a small task that you could have probably finished yourself in minutes!  I hope to pick up more and bigger tasks, especially in the summer.
fastakash: Hi @mhvk   Thanks again for your help.  Do you think my updates are ok?  If you are busy, please ignore this message!  I will start looking at other issues.. thanks
mhvk: @fastakash - apart from the very minor comment about the docstring, this all looks good. If you change that one, add `[ci skip]` to your commit message, since it is not necessary to run the tests again.

Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 6a7d944032..80561595a0 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -130,6 +130,10 @@ astropy.time
 astropy.units
 ^^^^^^^^^^^^^
 
+- In ``UnitBase.compose()``, if a sequence (list|tuple) is passed in to
+  ``units``, the default for ``include_prefix_units`` is set to
+  `True`, so that no units get ignored. [#6957]
+
 astropy.utils
 ^^^^^^^^^^^^^
 
diff --git a/astropy/units/core.py b/astropy/units/core.py
index 5791366027..097bc77522 100644
--- a/astropy/units/core.py
+++ b/astropy/units/core.py
@@ -1115,7 +1115,7 @@ class UnitBase(metaclass=InheritDocstrings):
         return [self]
 
     def compose(self, equivalencies=[], units=None, max_depth=2,
-                include_prefix_units=False):
+                include_prefix_units=None):
         """
         Return the simplest possible composite unit(s) that represent
         the given unit.  Since there may be multiple equally simple
@@ -1141,7 +1141,8 @@ class UnitBase(metaclass=InheritDocstrings):
 
         include_prefix_units : bool, optional
             When `True`, include prefixed units in the result.
-            Default is `False`.
+            Default is `True` if a sequence is passed in to ``units``,
+            `False` otherwise.
 
         Returns
         -------
@@ -1151,6 +1152,11 @@ class UnitBase(metaclass=InheritDocstrings):
             automatically determine which of the candidates are
             better.
         """
+        # if units parameter is specified and is a sequence (list|tuple),
+        # include_prefix_units is turned on by default.  Ex: units=[u.kpc]
+        if include_prefix_units is None:
+            include_prefix_units = isinstance(units, (list, tuple))
+
         # Pre-normalize the equivalencies list
         equivalencies = self._normalize_equivalencies(equivalencies)
 


Test patch:
diff --git a/astropy/units/tests/test_units.py b/astropy/units/tests/test_units.py
index 2343d90e61..1f9b2e6a04 100644
--- a/astropy/units/tests/test_units.py
+++ b/astropy/units/tests/test_units.py
@@ -399,6 +399,25 @@ def test_compose_issue_579():
     assert result[0]._powers == [4, 1, -2]
 
 
+def test_compose_prefix_unit():
+    x =  u.m.compose(units=(u.m,))
+    assert x[0].bases[0] is u.m
+    assert x[0].scale == 1.0
+    x = u.m.compose(units=[u.km], include_prefix_units=True)
+    assert x[0].bases[0] is u.km
+    assert x[0].scale == 0.001
+    x = u.m.compose(units=[u.km])
+    assert x[0].bases[0] is u.km
+    assert x[0].scale == 0.001
+
+    x = (u.km/u.s).compose(units=(u.pc, u.Myr))
+    assert x[0].bases == [u.pc, u.Myr]
+    assert_allclose(x[0].scale, 1.0227121650537077)
+
+    with raises(u.UnitsError):
+        (u.km/u.s).compose(units=(u.pc, u.Myr), include_prefix_units=False)
+
+
 def test_self_compose():
     unit = u.kg * u.s