Skip to content

Cannot write votable with units equivalent to Jy/beam #17497

Description

@ErikOsinga

Description

Before astropy v7.0.0, I was able to write a table to 'votable' format which has units Jy/beam.

e.g. in astropy v6.1.4:

In [12]: a['col_flux_peak'].unit
Out[12]: Unit("mJy / beam")

In [13]: a.write('~/test.xml',format='votable')

However, as of astropy v7.0.0, this raises an error (full trace at bottom)

ValueError: Unit 'beam' not supported by the CDS standard.

Why has this behaviour changed? And can it be updated such that Jy/beam units are allowed again?

Thanks in advance,
Erik

In [11]: a['col_flux_peak'].unit
Out[11]: Unit("mJy / beam")

In [12]: a.write('~/test.xml',format='votable')
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[12], line 1
----> 1 a.write('~/test.xml',format='votable')

File /usr/local/lib/python3.12/site-packages/astropy/table/connect.py:130, in TableWrite.__call__(self, serialize_method, *args, **kwargs)
    128 instance = self._instance
    129 with serialize_method_as(instance, serialize_method):
--> 130     self.registry.write(instance, *args, **kwargs)

File /usr/local/lib/python3.12/site-packages/astropy/io/registry/core.py:386, in UnifiedOutputRegistry.write(self, data, format, *args, **kwargs)
    381     format = self._get_valid_format(
    382         "write", data.__class__, path, fileobj, args, kwargs
    383     )
    385 writer = self.get_writer(format, data.__class__)
--> 386 return writer(data, *args, **kwargs)

File /usr/local/lib/python3.12/site-packages/astropy/io/votable/connect.py:176, in write_table_votable(input, output, table_id, overwrite, tabledata_format)
    173 table_file = from_table(input, table_id=table_id)
    175 # Write out file
--> 176 table_file.to_xml(output, tabledata_format=tabledata_format)

File /usr/local/lib/python3.12/site-packages/astropy/io/votable/tree.py:4384, in VOTableFile.to_xml(self, fd, compressed, tabledata_format, _debug_python_based_parser, _astropy_version)
   4382 for element_set in element_sets:
   4383     for element in element_set:
-> 4384         element.to_xml(w, **kwargs)

File /usr/local/lib/python3.12/site-packages/astropy/io/votable/tree.py:3991, in Resource.to_xml(self, w, **kwargs)
   3988         elm.to_xml(w, **kwargs)
   3990 for elm in self.tables:
-> 3991     elm.to_xml(w, **kwargs)
   3993 for elm in self.resources:
   3994     if elm.type != "meta":

File /usr/local/lib/python3.12/site-packages/astropy/io/votable/tree.py:3282, in TableElement.to_xml(self, w, **kwargs)
   3280     for element_set in (self.fields, self.params, self.groups, self.links):
   3281         for element in element_set:
-> 3282             element.to_xml(w, **kwargs)
   3283 elif kwargs["version_1_2_or_later"]:
   3284     index = list(self._votable.iter_tables()).index(self)

File /usr/local/lib/python3.12/site-packages/astropy/io/votable/tree.py:1678, in Field.to_xml(self, w, **kwargs)
   1676 attrib = w.object_attrs(self, self._attr_list)
   1677 if "unit" in attrib:
-> 1678     attrib["unit"] = self.unit.to_string("cds")
   1679 with w.tag(self._element_name, attrib=attrib):
   1680     if self.description is not None:

File /usr/local/lib/python3.12/site-packages/astropy/units/core.py:762, in UnitBase.to_string(self, format, **kwargs)
    720 r"""Output the unit in the given format as a string.
    721 
    722 Parameters
   (...)
    759 s
    760 """
    761 f = unit_format.get_format(format)
--> 762 return f.to_string(self, **kwargs)

File /usr/local/lib/python3.12/site-packages/astropy/units/format/cds.py:303, in CDS.to_string(cls, unit, fraction)
    298 @classmethod
    299 def to_string(
    300     cls, unit: UnitBase, fraction: bool | Literal["inline"] = False
    301 ) -> str:
    302     # Remove units that aren't known to the format
--> 303     unit = cls._decompose_to_known_units(unit)
    305     if not unit.bases:
    306         if unit.scale == 1:

File /usr/local/lib/python3.12/site-packages/astropy/units/format/generic.py:640, in Generic._decompose_to_known_units(cls, unit)
    633 """
    634 Partially decomposes a unit so it is only composed of units that
    635 are "known" to a given format.
    636 """
    637 if isinstance(unit, core.CompositeUnit):
    638     return core.CompositeUnit(
    639         unit.scale,
--> 640         [cls._decompose_to_known_units(base) for base in unit.bases],
    641         unit.powers,
    642         _error_check=False,
    643     )
    644 if isinstance(unit, core.NamedUnit):
    645     try:

File /usr/local/lib/python3.12/site-packages/astropy/units/format/generic.py:646, in Generic._decompose_to_known_units(cls, unit)
    644 if isinstance(unit, core.NamedUnit):
    645     try:
--> 646         cls._get_unit_name(unit)
    647     except ValueError:
    648         if isinstance(unit, core.Unit):

File /usr/local/lib/python3.12/site-packages/astropy/units/format/generic.py:585, in Generic._get_unit_name(cls, unit)
    582 @classmethod
    583 def _get_unit_name(cls, unit: NamedUnit) -> str:
    584     name = unit._get_format_name(cls.name)
--> 585     cls._validate_unit(name)
    586     return name

File /usr/local/lib/python3.12/site-packages/astropy/units/format/generic.py:592, in Generic._validate_unit(cls, unit, detailed_exception)
    590 if unit not in cls._units:
    591     if detailed_exception:
--> 592         raise ValueError(
    593             f"Unit '{unit}' not supported by the {cls.__name__} standard. "
    594             + cls._did_you_mean_units(unit)
    595         )
    596     raise ValueError()
    597 if unit in cls._deprecated_units:

ValueError: Unit 'beam' not supported by the CDS standard. 

Expected behavior

Expect writing table to 'votable' format with units Jy/beam to work.

How to Reproduce

Take any table with units Jy/beam in any of the columns and try to write it to votable format.

from astropy.table import Table
import astropy.units as u
b = Table()
b['flux'] = [5]
b['flux'].unit = u.Jy/u.beam
b.write('~/test.xml',format='votable')

### Versions

```python
import astropy
try:
    astropy.system_info()
except AttributeError:
    import platform; print(platform.platform())
    import sys; print("Python", sys.version)
    import astropy; print("astropy", astropy.__version__)
    import numpy; print("Numpy", numpy.__version__)
    import erfa; print("pyerfa", erfa.__version__)
    try:
        import scipy
        print("Scipy", scipy.__version__)
    except ImportError:
        print("Scipy not installed")
    try:
        import matplotlib
        print("Matplotlib", matplotlib.__version__)
    except ImportError:
        print("Matplotlib not installed")
platform
--------
platform.platform() = 'Linux-4.18.0-553.27.1.el8_10.x86_64-x86_64-with-glibc2.36'
platform.version() = '#1 SMP Tue Nov 5 04:50:16 EST 2024'
platform.python_version() = '3.12.2'

packages
--------
astropy              7.0.0
numpy                1.26.4
scipy                1.14.1
matplotlib           3.9.3
pandas               2.2.3
pyerfa               2.0.1.5

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions