From 1030bcba71f9403d0a9a944c26de4df8536551a6 Mon Sep 17 00:00:00 2001 From: Joey Chatelain Date: Fri, 5 Nov 2021 23:54:34 -0700 Subject: [PATCH 1/6] fix for parabolic comets --- neoexchange/astrometrics/ephem_subs.py | 7 ++++++- neoexchange/core/plots.py | 12 ++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/neoexchange/astrometrics/ephem_subs.py b/neoexchange/astrometrics/ephem_subs.py index 9f857c68c..6129d9079 100644 --- a/neoexchange/astrometrics/ephem_subs.py +++ b/neoexchange/astrometrics/ephem_subs.py @@ -2247,12 +2247,17 @@ def convert_to_ecliptic(equatorial_coords, coord_date): def orbital_pos_from_true_anomaly(nu, body_elements): - """Give Cartesian coordinates for a given true anomaly in radians for s given set of orbital elements""" + """Give Cartesian coordinates for a given true anomaly in radians for a given set of orbital elements""" a = body_elements['meandist'] e = body_elements['eccentricity'] i = radians(body_elements['orbinc']) om = radians(body_elements['longascnode']) w = radians(body_elements['argofperih']) + if not a: # Comet + if e < 1: # Periodic + a = body_elements['perihdist'] / (1 - e) + else: # Non-periodic, not going to deal with this right now. + return [0, 0, 0] # Calculate radial distance and heliocentric, ecliptic Cartesian Coordinates based on orbital elements r = a * (1 - e ** 2) / (1 + e * cos(nu)) diff --git a/neoexchange/core/plots.py b/neoexchange/core/plots.py index 7e9e2bffc..fea0550b7 100644 --- a/neoexchange/core/plots.py +++ b/neoexchange/core/plots.py @@ -730,8 +730,12 @@ def lc_plot(lc_list, meta_list, lc_model_dict={}, period=1, pscan_list=[], shape plot_period.y_range = DataRange1d(names=['period']) plot_period.x_range = DataRange1d(names=['period'], bounds=(0, None)) plot_orbit = figure(plot_width=900, plot_height=900, x_axis_location=None, y_axis_location=None) - plot_orbit.y_range = Range1d(min(-1.1, -1.1 * body.meandist), max(1.1, 1.1 * body.meandist)) - plot_orbit.x_range = Range1d(min(-1.1, -1.1 * body.meandist), max(1.1, 1.1 * body.meandist)) + if body.meandist: + orbit_range = body.meandist + else: + orbit_range = 1 + plot_orbit.y_range = Range1d(min(-1.1, -1.1 * orbit_range), max(1.1, 1.1 * orbit_range)) + plot_orbit.x_range = Range1d(min(-1.1, -1.1 * orbit_range), max(1.1, 1.1 * orbit_range)) plot_orbit.grid.grid_line_color = None plot_shape = figure(plot_width=600, plot_height=600, x_axis_location=None, y_axis_location=None) plot_shape.grid.grid_line_color = None @@ -1193,7 +1197,7 @@ def get_orbit_position(meta_list, lc_list, body): def get_full_orbit(body): body_elements = model_to_dict(body) - full_orbit = [orbital_pos_from_true_anomaly(nu, body_elements) for nu in np.arange(0, 7, .01)] + full_orbit = [orbital_pos_from_true_anomaly(nu, body_elements) for nu in np.arange(-3.5, 3.5, .01)] full_orbit_x = [pos[0] for pos in full_orbit] full_orbit_y = [pos[1] for pos in full_orbit] full_orbit_z = [pos[2] for pos in full_orbit] @@ -1201,7 +1205,7 @@ def get_full_orbit(body): planetary_elements = get_planetary_elements() for planet in planetary_elements: - p_orb = [orbital_pos_from_true_anomaly(nu, planetary_elements[planet]) for nu in np.arange(0, 7, .01)] + p_orb = [orbital_pos_from_true_anomaly(nu, planetary_elements[planet]) for nu in np.arange(-3.5, 3.5, .01)] position_data[f'{planet}_x'] = [pos[0] for pos in p_orb] position_data[f'{planet}_y'] = [pos[1] for pos in p_orb] position_data[f'{planet}_z'] = [pos[2] for pos in p_orb] From c3b7861925b0610d9573b1510f066ee228d3dad0 Mon Sep 17 00:00:00 2001 From: Joey Chatelain Date: Mon, 8 Nov 2021 10:17:04 -0800 Subject: [PATCH 2/6] add tests for comet LC --- neoexchange/neox/tests/test_plot_lc.py | 45 ++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/neoexchange/neox/tests/test_plot_lc.py b/neoexchange/neox/tests/test_plot_lc.py index 506150b7b..d8ab2993d 100644 --- a/neoexchange/neox/tests/test_plot_lc.py +++ b/neoexchange/neox/tests/test_plot_lc.py @@ -72,6 +72,33 @@ def setUp(self): self.body2.save_physical_parameters({'parameter_type': 'P', 'value': 5.27}) + params = {'name': 'C/2017 K2', + 'abs_mag': 7.0, + 'slope': 2.0, + 'epochofel': '2021-07-05 00:00:00', + 'epochofperih': '2022-12-19 05:50:00', + 'argofperih': 236.19001, + 'longascnode': 88.244, + 'orbinc': 87.55297, + 'eccentricity': 1.0005709, + 'perihdist': 1.7974686, + 'source_type': 'C', + 'elements_type': 'MPC_COMET', + 'active': True, + 'origin': 'O', + 'ingest': '2015-05-11 17:20:00', + 'score': 90, + 'discovery_date': '2015-05-10 12:00:00', + 'update_time': '2015-05-18 05:00:00', + 'num_obs': 17, + 'arc_length': 3.123456789, + 'not_seen': 0.423456789, + 'updated': True, + 'meandist': None, + 'meananom': None, + } + self.comet, created = Body.objects.get_or_create(pk=3, **params) + self.username = 'bart' self.password = 'simpson' self.email = 'bart@simpson.org' @@ -84,6 +111,7 @@ def setUp(self): update_proposal_permissions(self.bart, [{'code': self.neo_proposal.code}]) save_dataproduct(self.body2, lcname, DataProduct.ALCDEF_TXT) + save_dataproduct(self.comet, lcname, DataProduct.ALCDEF_TXT) @patch('neox.auth_backend.lco_authenticate', mock_lco_authenticate) def login(self): @@ -135,3 +163,20 @@ def test_can_view_lightcurve(self): # test opening up a ALCDEF file associated alcdef_text = self.browser.find_element_by_xpath("/html/body/pre").text self.assertIn('OBJECTNUMBER=433', alcdef_text) return + + @patch('neox.auth_backend.lco_authenticate', mock_lco_authenticate) + def test_can_view_comet_lightcurve(self): # test opening up a ALCDEF file associated with a body + self.login() + lc_url = reverse('lc_plot', args=[self.comet.id]) + self.browser.get(self.live_server_url + lc_url) + + self.assertIn('Lightcurve for object: C/2017 K2', self.browser.title) + + canvas = self.browser.find_element_by_xpath( + "/html/body[@class='page']/div[@id='page-wrapper']/div[@id='page']/div[@id='main']/div[@name='lc_plot']/div[@class='bk']/div[@class='bk'][2]/div[@class='bk'][1]/div[@class='bk']/div[@class='bk bk-canvas-events']") + phase_tab = self.browser.find_element_by_xpath( + "/html/body[@class='page']/div[@id='page-wrapper']/div[@id='page']/div[@id='main']/div[@name='lc_plot']/div[@class='bk']/div[@class='bk bk-tabs-header bk-above']/div[@class='bk bk-headers-wrapper']/div[@class='bk bk-headers']/div[@class='bk bk-tab']") + phase_tab.click() + period_box = self.browser.find_element_by_xpath( + "/html/body[@class='page']/div[@id='page-wrapper']/div[@id='page']/div[@id='main']/div[@name='lc_plot']/div[@class='bk']/div[@class='bk'][2]/div[@class='bk'][2]/div[@class='bk'][2]/div[@class='bk']/div[@class='bk'][1]/div[@class='bk'][1]/div[@class='bk']/div[@class='bk bk-input-group']/div[@class='bk bk-spin-wrapper']/input[@class='bk bk-input']") + return From 3891b5acd5213b516bd0761534ce90c5f49ecffc Mon Sep 17 00:00:00 2001 From: Joey Chatelain Date: Mon, 8 Nov 2021 16:46:08 -0800 Subject: [PATCH 3/6] accomodate non-periodic orbits of all kinds --- neoexchange/astrometrics/ephem_subs.py | 16 ++++++++++------ neoexchange/core/plots.py | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/neoexchange/astrometrics/ephem_subs.py b/neoexchange/astrometrics/ephem_subs.py index 6129d9079..75a6ab7c9 100644 --- a/neoexchange/astrometrics/ephem_subs.py +++ b/neoexchange/astrometrics/ephem_subs.py @@ -2248,19 +2248,23 @@ def convert_to_ecliptic(equatorial_coords, coord_date): def orbital_pos_from_true_anomaly(nu, body_elements): """Give Cartesian coordinates for a given true anomaly in radians for a given set of orbital elements""" - a = body_elements['meandist'] + a = body_elements.get('meandist', 0) e = body_elements['eccentricity'] i = radians(body_elements['orbinc']) om = radians(body_elements['longascnode']) w = radians(body_elements['argofperih']) + q = body_elements.get('perihdist', 0) + + # Calculate radial distance if not a: # Comet - if e < 1: # Periodic - a = body_elements['perihdist'] / (1 - e) - else: # Non-periodic, not going to deal with this right now. - return [0, 0, 0] + e = 1 + if e >= 1: # shorten extent of nu to prevent wayward infinities and beyond. + nu *= acos(-1/e) / pi - 0.0001 + r = q * (1 + e) / (1 + e * cos(nu)) + else: + r = a * (1 - e ** 2) / (1 + e * cos(nu)) # Calculate radial distance and heliocentric, ecliptic Cartesian Coordinates based on orbital elements - r = a * (1 - e ** 2) / (1 + e * cos(nu)) x_h_ecl = r * (cos(om) * cos(w + nu) - sin(om) * sin(w + nu) * cos(i)) y_h_ecl = r * (sin(om) * cos(w + nu) + cos(om) * sin(w + nu) * cos(i)) z_h_ecl = r * (sin(w + nu) * sin(i)) diff --git a/neoexchange/core/plots.py b/neoexchange/core/plots.py index fea0550b7..ce4367824 100644 --- a/neoexchange/core/plots.py +++ b/neoexchange/core/plots.py @@ -1197,7 +1197,7 @@ def get_orbit_position(meta_list, lc_list, body): def get_full_orbit(body): body_elements = model_to_dict(body) - full_orbit = [orbital_pos_from_true_anomaly(nu, body_elements) for nu in np.arange(-3.5, 3.5, .01)] + full_orbit = [orbital_pos_from_true_anomaly(nu, body_elements) for nu in np.arange(-pi, pi, .01)] full_orbit_x = [pos[0] for pos in full_orbit] full_orbit_y = [pos[1] for pos in full_orbit] full_orbit_z = [pos[2] for pos in full_orbit] @@ -1205,7 +1205,7 @@ def get_full_orbit(body): planetary_elements = get_planetary_elements() for planet in planetary_elements: - p_orb = [orbital_pos_from_true_anomaly(nu, planetary_elements[planet]) for nu in np.arange(-3.5, 3.5, .01)] + p_orb = [orbital_pos_from_true_anomaly(nu, planetary_elements[planet]) for nu in np.arange(-pi, pi, .01)] position_data[f'{planet}_x'] = [pos[0] for pos in p_orb] position_data[f'{planet}_y'] = [pos[1] for pos in p_orb] position_data[f'{planet}_z'] = [pos[2] for pos in p_orb] From 2e935d711b5732cea549e5f75baa374ac0743b17 Mon Sep 17 00:00:00 2001 From: Joey Chatelain Date: Tue, 9 Nov 2021 10:58:21 -0800 Subject: [PATCH 4/6] adjust plot range for comets --- neoexchange/core/plots.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/neoexchange/core/plots.py b/neoexchange/core/plots.py index ce4367824..56204c2a9 100644 --- a/neoexchange/core/plots.py +++ b/neoexchange/core/plots.py @@ -732,6 +732,8 @@ def lc_plot(lc_list, meta_list, lc_model_dict={}, period=1, pscan_list=[], shape plot_orbit = figure(plot_width=900, plot_height=900, x_axis_location=None, y_axis_location=None) if body.meandist: orbit_range = body.meandist + elif body.perihdist: + orbit_range = body.perihdist else: orbit_range = 1 plot_orbit.y_range = Range1d(min(-1.1, -1.1 * orbit_range), max(1.1, 1.1 * orbit_range)) From ba4a8635c585ab62c038342bb574f1a543f69fad Mon Sep 17 00:00:00 2001 From: Joey Chatelain Date: Tue, 9 Nov 2021 12:28:36 -0800 Subject: [PATCH 5/6] fix tests --- neoexchange/neox/tests/test_plot_lc.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/neoexchange/neox/tests/test_plot_lc.py b/neoexchange/neox/tests/test_plot_lc.py index d8ab2993d..1ff5968dd 100644 --- a/neoexchange/neox/tests/test_plot_lc.py +++ b/neoexchange/neox/tests/test_plot_lc.py @@ -41,9 +41,9 @@ def setUp(self): super(LighCurvePlotTest, self).setUp() settings.DATA_ROOT = self.test_dir settings.MEDIA_ROOT = self.test_dir - lcname = '433_738215_ALCDEF.txt' + self.lcname = '433_738215_ALCDEF.txt' lcpath = os.path.abspath(os.path.join('photometrics', 'tests')) - save_to_default(os.path.join(lcpath, lcname), lcpath) + save_to_default(os.path.join(lcpath, self.lcname), lcpath) params = { 'name' : '433', 'abs_mag' : 21.0, @@ -110,9 +110,6 @@ def setUp(self): update_proposal_permissions(self.bart, [{'code': self.neo_proposal.code}]) - save_dataproduct(self.body2, lcname, DataProduct.ALCDEF_TXT) - save_dataproduct(self.comet, lcname, DataProduct.ALCDEF_TXT) - @patch('neox.auth_backend.lco_authenticate', mock_lco_authenticate) def login(self): self.browser.get('%s%s' % (self.live_server_url, '/accounts/login/')) @@ -136,6 +133,7 @@ def test_no_lightcurve(self): @patch('neox.auth_backend.lco_authenticate', mock_lco_authenticate) def test_can_view_lightcurve(self): # test opening up a ALCDEF file associated with a body + save_dataproduct(self.body2, self.lcname, DataProduct.ALCDEF_TXT) self.login() lc_url = reverse('lc_plot', args=[self.body2.id]) self.browser.get(self.live_server_url + lc_url) @@ -166,6 +164,7 @@ def test_can_view_lightcurve(self): # test opening up a ALCDEF file associated @patch('neox.auth_backend.lco_authenticate', mock_lco_authenticate) def test_can_view_comet_lightcurve(self): # test opening up a ALCDEF file associated with a body + save_dataproduct(self.comet, self.lcname, DataProduct.ALCDEF_TXT) self.login() lc_url = reverse('lc_plot', args=[self.comet.id]) self.browser.get(self.live_server_url + lc_url) From b1dc20b203206d4fa62a08937654b0f709281656 Mon Sep 17 00:00:00 2001 From: Joey Chatelain Date: Tue, 9 Nov 2021 12:37:56 -0800 Subject: [PATCH 6/6] update version number/readme --- README.md | 3 +++ neoexchange/neox/settings.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 538fa3a28..5d2895139 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,9 @@ Portal for scheduling observations of NEOs (and other Solar System objects) usin ## History +### 3.10.1 +* Fix orbit plots for cometary elements. + ### 3.10.0 * Add support for DAMIT and advanced LC plotting features. diff --git a/neoexchange/neox/settings.py b/neoexchange/neox/settings.py index 37efb99fe..3942d0c31 100644 --- a/neoexchange/neox/settings.py +++ b/neoexchange/neox/settings.py @@ -7,7 +7,7 @@ import rollbar -VERSION = '3.10.0' +VERSION = '3.10.1' CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))