Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prototype fix for CID uniqueness #1

Merged
merged 3 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions lcviz/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from lightkurve import LightCurve

from glue.core.component_id import ComponentID
from glue.core.link_helpers import LinkSame
from jdaviz.core.helpers import ConfigHelper
from lcviz.events import ViewerRenamedMessage
Expand Down Expand Up @@ -123,6 +124,8 @@ class LCviz(ConfigHelper):
'plot': 'lcviz-time-viewer',
'reference': 'flux-vs-time'}]}]}]}

_component_ids = {}

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._default_time_viewer_reference_name = _default_time_viewer_reference_name
Expand Down Expand Up @@ -188,3 +191,17 @@ def get_data(self, data_label=None, cls=LightCurve, subset=None):
Data is returned as type cls with subsets applied.
"""
return super()._get_data(data_label=data_label, mask_subset=subset, cls=cls)

def _phase_comp_lbl(self, component):
return f'phase:{component}'

def _set_data_component(self, data, component_label, values):
if component_label not in self._component_ids:
self._component_ids[component_label] = ComponentID(component_label)

if self._component_ids[component_label] in data.components:
data.update_components({self._component_ids[component_label]: values})
else:
data.add_component(values, self._component_ids[component_label])

data.add_component(values, self._component_ids[component_label])
24 changes: 23 additions & 1 deletion lcviz/plugins/binning/binning.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from astropy.time import Time
from traitlets import Bool, observe
from glue.config import data_translator

from jdaviz.core.custom_traitlets import IntHandleEmpty
from jdaviz.core.events import (ViewerAddedMessage, ViewerRemovedMessage)
Expand Down Expand Up @@ -184,10 +185,31 @@ def bin(self, add_data=True):
scale=input_lc.time_original.scale)
lc.add_column(time_col, name="time_original", index=len(lc._required_columns))

lc.meta.update({'_LCVIZ_BINNED': True})

# convert to glue Data manually, so we may edit the `phase` component:
handler, _ = data_translator.get_handler_for(lc)
data = handler.to_data(lc)
phase_comp_lbl = self.app._jdaviz_helper._phase_comp_lbl(self.ephemeris_selected)

# here we use the `value` attribute of `lc.time`, which has units of *phase*:
self.app._jdaviz_helper._set_data_component(data, phase_comp_lbl, lc.time.value)

else:
data = None

if add_data:
# add data to the collection/viewer
# NOTE: lc will have _LCVIZ_EPHEMERIS set if phase-folded
self.add_results.add_results_from_plugin(lc)
self._set_results_viewer()
self.add_results.add_results_from_plugin(data or lc)

if self.ephemeris_selected != 'No ephemeris':
# prevent phase axis from becoming a time axis:
viewer_id = self.ephemeris_plugin._obj.phase_viewer_id
pv = self.app.get_viewer(viewer_id)
phase_comp_lbl = self.app._jdaviz_helper._phase_comp_lbl(self.ephemeris_selected)
pv.state.x_att = self.app._jdaviz_helper._component_ids[phase_comp_lbl]

return lc

Expand Down
91 changes: 44 additions & 47 deletions lcviz/plugins/ephemeris/ephemeris.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ class Ephemeris(PluginTemplateMixin, DatasetSelectMixin):
template_file = __file__, "ephemeris.vue"

# EPHEMERIS
phase_cids = {}
component_mode = Unicode().tag(sync=True)
component_edit_value = Unicode().tag(sync=True)
component_items = List().tag(sync=True)
Expand Down Expand Up @@ -123,7 +122,11 @@ def user_api(self):
return PluginUserApi(self, expose=expose)

def _phase_comp_lbl(self, component):
return f'phase:{component}'
if self.app._jdaviz_helper is None:
# duplicate logic from helper in case this is ever called before the helper
# is fully intialized
return f'phase:{component}'
return self.app._jdaviz_helper._phase_comp_lbl(component)

@property
def phase_comp_lbl(self):
Expand Down Expand Up @@ -172,19 +175,19 @@ def _callable(times):

return _callable

def times_to_phases(self, times, component=None):
if component is None:
component = self.component.selected
def times_to_phases(self, times, ephem_component=None):
if ephem_component is None:
ephem_component = self.component.selected

return self._times_to_phases_callable(component)(times)
return self._times_to_phases_callable(ephem_component)(times)

def phases_to_times(self, phases, component=None):
if component is None:
component = self.component.selected
def phases_to_times(self, phases, ephem_component=None):
if ephem_component is None:
ephem_component = self.component.selected

# this is not used internally, so we don't need the traitlet
# and callable optimizations
ephem = self.ephemerides.get(component, {})
ephem = self.ephemerides.get(ephem_component, {})
t0 = ephem.get('t0', _default_t0)
period = ephem.get('period', _default_period)
dpdt = ephem.get('dpdt', _default_dpdt)
Expand All @@ -194,18 +197,22 @@ def phases_to_times(self, phases, component=None):
else:
return t0 + (phases)*period

def _update_all_phase_arrays(self, *args, component=None):
if component is None:
for component in self.component.choices:
self._update_all_phase_arrays(component=component)
def _update_all_phase_arrays(self, *args, ephem_component=None):
# `ephem_component` is the name given to the
# *ephemeris* component in the orbiting system, e.g. "default",
# rather than the glue Data Component ID:

if ephem_component is None:
for ephem_component in self.component.choices:
self._update_all_phase_arrays(ephem_component=ephem_component)
return

dc = self.app.data_collection

phase_comp_lbl = self._phase_comp_lbl(component)
phase_comp_lbl = self._phase_comp_lbl(ephem_component)

# we'll create the callable function for this component once so it can be re-used
_times_to_phases = self._times_to_phases_callable(component)
_times_to_phases = self._times_to_phases_callable(ephem_component)

new_links = []
for i, data in enumerate(dc):
Expand All @@ -215,21 +222,11 @@ def _update_all_phase_arrays(self, *args, component=None):

times = data.get_component('World 0').data
phases = _times_to_phases(times)
if component not in self.phase_cids:
self.phase_cids[component] = ComponentID(phase_comp_lbl)

if self.phase_cids[component] in data.components:
data.update_components({self.phase_cids[component]: phases})
else:
data.add_component(phases, self.phase_cids[component])

# this loop catches phase components generated automatically by
# when add_results is triggered in other plugins:
for comp in data.components:
if phase_comp_lbl == comp.label:
data.remove_component(phase_comp_lbl)
self.app._jdaviz_helper._set_data_component(
data, phase_comp_lbl, phases
)

data.add_component(phases, self.phase_cids[component])
if i != 0:
ref_data = dc[0]
new_link = LinkSame(
Expand All @@ -247,7 +244,7 @@ def _update_all_phase_arrays(self, *args, component=None):

# update any plugin markers
# TODO: eventually might need to loop over multiple matching viewers
phase_viewer_id = self._phase_viewer_id(component)
phase_viewer_id = self._phase_viewer_id(ephem_component)
if phase_viewer_id in self.app.get_viewer_ids():
phase_viewer = self.app.get_viewer(phase_viewer_id)
for mark in phase_viewer.custom_marks:
Expand Down Expand Up @@ -282,7 +279,7 @@ def create_phase_viewer(self):
self.app.set_data_visibility(phase_viewer_id, data.label, visible == 'visible')

pv = self.app.get_viewer(phase_viewer_id)
pv.state.x_att = self.phase_cids[self.component_selected]
pv.state.x_att = self.app._jdaviz_helper._component_ids[self.phase_comp_lbl]
return pv

def vue_create_phase_viewer(self, *args):
Expand Down Expand Up @@ -370,7 +367,7 @@ def _change_component(self, *args):
# otherwise, this is a new component and there is no need.
self._ephem_traitlet_changed()

def update_ephemeris(self, component=None, t0=None, period=None, dpdt=None):
def update_ephemeris(self, ephem_component=None, t0=None, period=None, dpdt=None):
"""
Update the ephemeris for a given component.

Expand All @@ -389,22 +386,22 @@ def update_ephemeris(self, component=None, t0=None, period=None, dpdt=None):
-------
dictionary of ephemeris corresponding to ``component``
"""
if component is None:
component = self.component_selected
if ephem_component is None:
ephem_component = self.component_selected

if component not in self.component.choices:
if ephem_component not in self.component.choices:
raise ValueError(f"component must be one of {self.component.choices}")

existing_ephem = self._ephemerides.get(component, {})
existing_ephem = self._ephemerides.get(ephem_component, {})
for name, value in {'t0': t0, 'period': period, 'dpdt': dpdt}.items():
if value is not None:
existing_ephem[name] = value
if component == self.component_selected:
if ephem_component == self.component_selected:
setattr(self, name, value)

self._ephemerides[component] = existing_ephem
self._update_all_phase_arrays(component=component)
self.hub.broadcast(EphemerisChangedMessage(ephemeris_label=component,
self._ephemerides[ephem_component] = existing_ephem
self._update_all_phase_arrays(ephem_component=ephem_component)
self.hub.broadcast(EphemerisChangedMessage(ephemeris_label=ephem_component,
sender=self))
return existing_ephem

Expand All @@ -429,7 +426,7 @@ def round_to_1(x):
self.update_ephemeris(**{event.get('name'): event.get('new')})
# will call _update_all_phase_arrays
else:
self._update_all_phase_arrays(component=self.component_selected)
self._update_all_phase_arrays(ephem_component=self.component_selected)

# update step-sizes
self.period_step = round_to_1(self.period/5000)
Expand Down Expand Up @@ -475,18 +472,18 @@ def _update_periodogram(self, *args):
def vue_adopt_period_at_max_power(self, *args):
self.period = self.period_at_max_power

def get_data(self, dataset, component=None):
def get_data(self, dataset, ephem_component=None):
# TODO: support subset_to_apply and then include a wrapper at the helper-level?
# (would need to catch when cls does not result in a lightkurve object or write
# behaviors for other cases as well)
if component is None:
component = self.component.selected
if ephem_component is None:
ephem_component = self.component.selected

lc = self.app._jdaviz_helper.get_data(dataset)
data = next((x for x in self.app.data_collection if x.label == dataset))

comps = {str(comp): comp for comp in data.components}
xcomp = f'phase:{component}'
xcomp = f'phase:{ephem_component}'
phases = data.get_component(comps.get(xcomp)).data

# the following code is adopted directly from lightkurve
Expand All @@ -502,8 +499,8 @@ def get_data(self, dataset, component=None):
phlc.add_column(lc.time.copy(), name="time_original", index=len(lc._required_columns))

# Add extra column and meta data specific to FoldedLightCurve
ephemeris = self.ephemerides.get(component)
phlc.meta["_LCVIZ_EPHEMERIS"] = {'ephemeris': component, **ephemeris}
ephemeris = self.ephemerides.get(ephem_component)
phlc.meta["_LCVIZ_EPHEMERIS"] = {'ephemeris': ephem_component, **ephemeris}
phlc.meta["PERIOD"] = ephemeris.get('period')
phlc.meta["EPOCH_TIME"] = ephemeris.get('t0')
phlc.sort("time")
Expand Down
2 changes: 1 addition & 1 deletion lcviz/viewers.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,4 +223,4 @@ def times_to_phases(self, times):
if ephem is None:
raise ValueError("must have ephemeris plugin loaded to convert")

return ephem.times_to_phases(times, component=self.ephemeris_component)
return ephem.times_to_phases(times, ephem_component=self.ephemeris_component)