Skip to content

Implement SpectralCoord class #422

Closed

Description

@astrofrog

As discussed with @eteq and @keflavich

In the APE 14 API, we currently use SkyCoord as the high-level object for celestial coordinates, but we only use Quantity for spectral coordinates, without any attributes that describe the frame of reference the spectral quantity is in, meaning that we can't easily convert between different spectral representations. The solution is to develop a SpectralQuantity that subclasses Quantity and adds metadata about the spectral frame as well as conversion functionality. This is best developed in specutils to start with for faster development, and we can incorporate it in to astropy core later.

SpectralQuantity should have the following attributes:

  • frame=: this would be a BaseFrame object
  • rest=: a quantity giving the rest wavelength or frequency (assuming the same frame etc. as the parent quantity)
  • type: radio_velocity, optical_velocity, frequency, energy, wavenumber, air_wavelength, etc.

In addition, we should make sure SpectralQuantity(SpectralQuantity(...), …) should work (needed for APE 14) and convert the spectral coordinate, as should e.g. SpectralQuantity.to(..., frame=’...’, type=’...’).

Any of the attributes can be passed into the constructor as None, in which case it basically just behaves like a normal Quantity. But if they are not None, to does smart things where possible and raises exceptions if anything would be inconsistent(i.e., to(u.um, type=’velocity’)).

We also decided that information about bin edges doesn’t belong inside this class - instead either there could be a SpectralAxis class that subclasses SpectralQuantity, or that contains e.g. a SpectralQuantity for the bin edges.

One item that still needs to be discussed is where to keep information about the direction of the observations - needed when transforming frames. One option is to include it as an attribute of the SpectalQuantity, and another option is to pass it as an argument during the transformation.

This should be developed in specutils in a file that should be well isolated from the rest of the code and could easily be moved to the core package once we are happy with it.

Activity

  1. changed the title [-]Implement SpectralQuantity[/-] [+]Implement SpectralQuantity class[/+] on Dec 5, 2018
  2. nmearl commented on Dec 5, 2018

    @nmearl
    Contributor

    This is... interesting. So we want to move information about the axis to the axis's Quantity representation? Wouldn't this defeat the purpose of a mixin? Does this also presuppose that the Quantity has information about the WCS to know which axis information is being used to generate the SpectralQuantity when used in parsing e.g. fits files? Is the type functionally different from just a more fleshed out physical_type that already exists on Quantity objects?

  3. astrofrog commented on Dec 5, 2018

    @astrofrog
    MemberAuthor

    @nmearl - the primary idea of this class is that it is what will be returned by wcs.pixel_to_world with the APE14 API. It doesn't replace the current mix-in class, but it means that this is the type of object that would end up being returned from e.g. .spectral_axis. We should talk more in person tomorrow!

  4. nden commented on Dec 5, 2018

    @nden
    Contributor

    Is there a reason why this (or part of it ) is not landing in GWCS? It seems a logical place.

  5. stscicrawford commented on Dec 5, 2018

    @stscicrawford
    Contributor

    As noted in the APE, this information should be part of the WCS:

    The wcs property will provide a conversion between pixel position and the spectral axis. It will be defined in detail in a pending WCS APE, but in short it must have two methods: world_to_pixel and pixel_to_world. These functions map from “pixel” coordinates (i.e., indices) to “world” coordinates, where world can be anything - even pixels. See the examples above. This attribute will also contain metadata like the specific spectral frame (e.g., optical versus radio velocity definitions). Note that the details of this wcs attribute may change depending on the outcome of the WCS APE process, and hence this APE does not rely heavily upon it. However, additional functionality making use of more advanced WCS functionality is a possibility for future development of the Spectrum1D class. The wcs property should provide seamless and efficient access to the spectral_axis`.

    https://github.com/astropy/astropy-APEs/blob/master/APE13.rst#wcs-spectrum1d-and-data-cubes

  6. astrofrog commented on Dec 5, 2018

    @astrofrog
    MemberAuthor

    We should discuss this in person :)

  7. astrofrog commented on Feb 8, 2019

    @astrofrog
    MemberAuthor

    To close the loop on this - we did speak about it in person at the coordination meeting with @crawfordsm, @nden, and @nmearl after this thread and there was general agreement that this should exist. One minor tweak was that @nmearl argued it should be called SpectralCoord, which I agree with.

  8. changed the title [-]Implement SpectralQuantity class[/-] [+]Implement SpectralCoord class[/+] on Feb 8, 2019
  9. nden commented on Feb 8, 2019

    @nden
    Contributor

    I am also in favor of SpectralCoord.
    We also talked about putting the initial version in gwcs to avoid circular dependencies.

  10. added this to the v0.6.0 milestone on Feb 28, 2019
  11. nmearl commented on Jul 12, 2019

    @nmearl
    Contributor

    Do we expect that conversions of the SpectralCoord object would live on the SpectralCoord or on the wcs? E.g. air_to_vac conversions create a new wavelength array, which I assume would mean that the wcs would need to update its lookup table to do pixel to world conversions.

  12. nden commented on Jul 12, 2019

    @nden
    Contributor

    I'd expect something similar to SkyCoord, i.e. SpectralCoord has methods for converting the value from one "frame" to another, except in this case there might not be frames in the same sense as the coords package. The WCS will output SpectralCoord objects which will know how to further transform.

  13. eteq commented on Jul 26, 2019

    @eteq
    Member

    @nmearl and I have been discussing out-of-band how the SkyCoord machinery translates over to this idea, and I'm copying some of the relevant discussion here so its publicly visible:

    I’m not sure it makes sense to use the coordinates machinery directly in this context. It was designed (intentially) for 3D coordinates. I guess the question is: what of the machinery there is actually useful for SpectralCoord? Is it mainly the transform graph machinery, or the frame attribute machinery? Put another way: it may be more trouble than it’s worth to tie it directly into the coordinates frame/representation machinery, but one could imagine an independent thing that only uses the frame attribute machinery and none of the rest of it .

    From @nmearl: If we used the SkyCoord as an analog, we might have a WavelengthRepresentation that handles transforms to e.g. VelocityRepresentation/EnergyRepresentation/etc, and this would be attached to a frame object, but it’s not clear that if this frame object derives from the astropy BaseCoordinateFrame and given that its defaultly setup to deal in 3d space, it’d be able to understand how to transform a representation in 1d to a different coordinate frame.

    From me again: Another concern about SpectralCoord being like SkyCoord: if we want SpectralCoord to be the thing that we get when we do spec.spectral_axis, that means it has to be different from the coordinates machinery because it has to be like a Quantity. The Whereas coordinate frame classes contain things like a Quantity - you have to do coord.ra instead of just coord, where the 1d nature of SpectralCoord makes that unnecessary.

    So the experiment to try would be to see if a light-weight Quantity subclass can be created that uses the FrameAttribute classes and can be shoved into the FrameTransformGraph machinery. If so then it’s a great re-use of the machinery, but I’m not sure if that’s trvial, reasonale but not trivial, or very hard.

  14. eteq commented on Aug 9, 2019

    @eteq
    Member

    There has been some substantial developments in the idea SpectralCoord following the python in astronomy conference. Suffice it to say it's a pretty complicated beast the more folks involved in that discussion thought about it. But some important realizations to summarize right now:

    • We think SpectralCoord might be fully described by a Quantity that also has an observer (which is an astropy.coordinate frame object, or could be None if unknown), a target (similar to observer), and rest (which is importantly not necessarily the same as "rv"/"redshift")
    • redshift/rv information is encoded in the velocity offset between the target and observer. We should have accessors/creators
    • probably SpectralCoord should be immutable like spatial coordinates, and to, etc yields new SpectralCoord's

    To try to progress, @nmearl, @astrofrog and I are trying to spec out some use cases to try to explore implementations. But that will probably be in astropy core since we now realize it needs to be in wcs as well. Will need to figure out exactly how we want to end up using that here.

  15. added and removed on Aug 15, 2019
  16. modified the milestones: v0.7, v0.8 on Jan 4, 2020
  17. eteq commented on Feb 25, 2020

    @eteq
    Member

    In a marathon session, @astrofog, @nmearl, and I worked out the standing issues we think still exist here. Some of these build from discussions in #524 rather than this issue itself, but I figured this is still the better place to put it:

    • From an earlier in-person discussion. Switch from the existing to_rest/to_obsever/etc to in_velocity_frame (only adjusts velocity) and with_observer (adjusts the observer position as well)

    • Need to combine discussion threads in Implement SpectralCoord class #422, Implement spectral coordinate object #524, and Default observer and target for SpectralCoord #555

    • Do we still agree observer/target or is rest necessary? observer/target yes. But for rest we realized we've mixed together "velocity" meaning the rest/vel conv/etc, and "redshift/rv" which are a very different use of velocity. We shouldn't do that. Below is a plan to separate them more cleanly.

    • Eventually need to apply the barycentric correction way of doing RV (i.e., not a direct sum - see https://docs.astropy.org/en/stable/api/astropy.coordinates.SkyCoord.html#astropy.coordinates.SkyCoord.radial_velocity_correction for more). Not targeting that right now unless it comes "for free" with the coordinates machinery, but an eventual goal.

    • We will separate out the "apply the RV transformation" method from the "create new spectralcoord" operation

    • "with_observer" may actually have no real use. Is "in_velocity_frame" what we all care about? Decide to make with_observer private (_change_observer_to, but make an issue about making it public if use case appears.

    • Do we have any case where we want to shift "target"? No. In fact the only valid comparison for arbitrary observer/target combos is shifting to the velocity frame of the target. This should be a string shortcut for in_velocity_frame of 'target' and/or 'rest' or possibly 'target-rest'? 'target' for sure, follow-on issue for 'rest'/etc

      • Highlighted example to include: asteroid, with orbiting observer, shift both to rest and then compare
    • We should allow target=None and observer=None at class creation time. Then they can be set to value, but not re-setting once they've been set to something other than None.

    • We know we want a "arbitrary rv shift" method. It should have the signature with_los_shift(target=0, observer=0) . Inputs can be either dimensionless, interpreted as redshift, velocity interpreted as rv, and all others raise exceptions. The specific choice of this signature is to allow for simple offsets of just with_los_shift(20*u.km/u.s) for those who don't want to thing that hard, but more advanced control of the target/observer specifics for those who do care.

    • rest and velocity_convention in Implement spectral coordinate object #524 should be renamed doppler_rest and doppler_convention

    • doppler_rest/doppler_convention should be like target/observer where None means settable, but if already set, cannot be re-set. except by using to and explicitly setting the rest/velocity_convention.

    • In the end this leaves 4 attributes that are actual state of the SpectralCoord in addition to the normal Quantity data: target, observer (both SkyCoords), doppler_rest (velocity Quantity or None), doppler_convention (string or None)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions