Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
5a9429f
Passing in arg
Farrmol Jul 22, 2024
d1f285c
test commit
sblunt Jul 22, 2024
a0b603f
add minimal unit test for reflected-light functionality
sblunt Jul 22, 2024
04e8638
add if name == main to test_brightness
sblunt Jul 22, 2024
2d9129b
Called times2trueanom_and_eccanom
Farrmol Jul 23, 2024
017c8ce
modify read_input to read in brightness vals
sblunt Jul 24, 2024
03ea984
update todos in test_brightness
sblunt Jul 24, 2024
64045dd
test system for brightness calc: gj 504 -> beta pic
sblunt Jul 24, 2024
48008b6
Merge branch 'add-brigtness-calc-to-comp-all-orbits' of https://githu…
Farrmol Jul 26, 2024
9b9055e
Added Brightness_calculation
Farrmol Jul 29, 2024
81f8388
fixed bug returning brightness
sblunt Jul 30, 2024
ec07520
Plot of betaPicB data
Farrmol Aug 5, 2024
f308ed4
test
Farrmol Aug 5, 2024
4c3f6cf
add brightness to return stms of comp_all_orbits & todo for farrah
sblunt Aug 5, 2024
37bbb19
add test_compute_posteriors
sblunt Aug 5, 2024
9dcd884
Added brightness as a model prediction
Farrmol Aug 5, 2024
222dec7
add pretty visual to test for farrah
sblunt Aug 19, 2024
1863981
add todo for farrah in read input test
sblunt Sep 17, 2024
2ca8419
cleaned up test brightness
Farrmol Sep 20, 2024
17574f8
Merge branch 'add-brigtness-calc-to-comp-all-orbits' of https://githu…
Farrmol Sep 20, 2024
30248b6
testing add change
sblunt Sep 20, 2024
d9ef026
Merge branch 'add-brigtness-calc-to-comp-all-orbits' of https://githu…
sblunt Sep 20, 2024
aa16ef8
Added assert brightness test
Farrmol Sep 27, 2024
b37f60d
Merge branch 'add-brigtness-calc-to-comp-all-orbits' of https://githu…
sblunt Oct 8, 2024
cd0c00a
mcmc running
sblunt Oct 8, 2024
1a069bb
Assert nan values
Farrmol Oct 9, 2024
7300015
changed these to test 'test_brightness.py'
Farrmol Oct 10, 2024
914810d
Merge branch 'add-brigtness-calc-to-comp-all-orbits' of https://githu…
Farrmol Oct 10, 2024
683094a
Test orbital data to run mcmc
Farrmol Oct 11, 2024
1a6f24e
Moved test orbital data to example data file
Farrmol Oct 11, 2024
3234195
Added ID to test table
Farrmol Oct 11, 2024
ac4ac17
another change that has columns in right order...lol
Farrmol Oct 11, 2024
3147d1b
object_id changed to object to fit exception!
Farrmol Oct 11, 2024
f40b8db
added + read in a simulated data file to compute posteriors test
Farrmol Oct 11, 2024
062b1f3
100th attempt at fixing the csv file: adding ra and dec error
Farrmol Oct 11, 2024
ddd6820
changed errors to 0.01
Farrmol Oct 11, 2024
4d14d01
mcmc test
Farrmol Oct 18, 2024
f7c2208
trying to run orbitize plot just with simulated ra and dec data
Farrmol Oct 31, 2024
8890f96
Trying to find what's wrong in simulated ra and dec data file
Farrmol Oct 31, 2024
e70e82a
changed sep pa end year
Farrmol Feb 4, 2025
ffcaace
brightness calc incorp in mcmc
sblunt Feb 14, 2025
e009c18
new test mcmc file with added brightness values
Farrmol Feb 19, 2025
8e9a8e2
separate test for brightness mcmc
Farrmol Feb 24, 2025
4966b3d
corner plot
Farrmol Mar 4, 2025
ab6b40c
new simulated ra and dec data
Farrmol Mar 14, 2025
a539090
updated parallax (0.03) to match simulated ra/dec data file
Farrmol Mar 17, 2025
0f358a6
mcmc under tests
Farrmol Mar 24, 2025
239aff1
testing brightness posteriors
Farrmol Jun 6, 2025
7048e56
HDF5 for shorted pos data set
Farrmol Jun 6, 2025
1f85c2e
Merge branch 'main' into add-brigtness-calc-to-comp-all-orbits
sblunt May 18, 2026
e742b07
moved to CIERA-Research
Farrmol Jun 18, 2026
5cefd94
moved to CIERA-Research
Farrmol Jun 18, 2026
ec46224
Moved to CIERA-Research
Farrmol Jun 24, 2026
1eac665
Moved to CIERA-Research or deleted
Farrmol Jun 24, 2026
f43a5d6
Revert commit I made that added additional formatting changes.
sblunt Jul 1, 2026
a3298b6
change sep_pa_end_year back to 2025
sblunt Jul 1, 2026
c6f1ccb
remove todo
sblunt Jul 1, 2026
0f4a4f6
formatting
sblunt Jul 1, 2026
6b7e534
fix test failures for abs_astrom
sblunt Jul 1, 2026
26d7551
clean up test_brightness
sblunt Jul 1, 2026
7b86edb
add placeholder reflected light example csv
sblunt Jul 1, 2026
489baef
fix bug for 1d brightness arrays
sblunt Jul 1, 2026
83ad686
fix comp_all_orbits bugs with new brightness implementation
sblunt Jul 1, 2026
f98c820
fix bug with brightness and xyz basis
sblunt Jul 1, 2026
53e697e
fix rebound test
sblunt Jul 1, 2026
3859a4a
add times2trueanom docstring
sblunt Jul 20, 2026
98c29da
update comp_all_orbits docstring
sblunt Jul 20, 2026
4c938ad
update test_compute_posterior docstring
sblunt Jul 20, 2026
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
6 changes: 6 additions & 0 deletions orbitize/example_data/reflected_light_example.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
epoch,object,sep,sep_err,pa,pa_err,brightness,brightness_err
54781,1,210.0,27.0,211.49,1.9,0.9,0.1
52953,1,413.0,22.0,34,4,0.6,0.2
55129,1,299.0,14.0,211,3,,
55194,1,306.0,9.0,212.1,1.7,,
55296,1,346.0,7.0,209.9,1.2,,
76 changes: 60 additions & 16 deletions orbitize/kepler.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,62 @@ def tau_to_manom(date, sma, mtot, tau, tau_ref_epoch):

return mean_anom

def times2trueanom_and_eccanom(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing docstring

sma,
epochs,
mtot,
ecc,
tau,
tau_ref_epoch=58849,
tolerance=1e-9,
max_iter=100,
use_c=True,
use_gpu=False,
):
"""
Convert times to true anomaly and eccentric anomaly by solving Kepler's Equation.

Args:
sma (np.array): semi-major axis of orbit [au]
epochs (np.array): MJD times for which we want the positions of the planet
mtot (np.array): total mass of the two-body orbit (M_* + M_planet) [Solar masses]
ecc (np.array): eccentricity of the orbit [0,1]
tau (np.array): epoch of periastron passage in fraction of orbital period past MJD=0 [0,1]
tau_ref_epoch (float, optional): reference date that tau is defined with respect to (default: 58849)
tolerance (float, optional): absolute tolerance of iterative computation. Defaults to 1e-9.
max_iter (int, optional): maximum number of iterations before switching. Defaults to 100.
use_c (bool, optional): Use the C solver if configured. Defaults to True
use_gpu (bool, optional): Use the GPU solver if configured. Defaults to False

Returns:
2-tuple:

np.array: true anomalies (shape n_epochs)

np.array: eccentric anomalies (shape n_epochs)
"""

n_orbs = np.size(sma) # num sets of input orbital parameters
n_dates = np.size(epochs) # number of dates to compute offsets and vz


# Necessary for _calc_ecc_anom, for now
if np.isscalar(epochs): # just in case epochs is given as a scalar
epochs = np.array([epochs])
ecc_arr = np.tile(ecc, (n_dates, 1))

# # compute mean anomaly (size: n_orbs x n_dates)
manom = tau_to_manom(epochs[:, None], sma, mtot, tau, tau_ref_epoch)
# compute eccentric anomalies (size: n_orbs x n_dates)
eanom = _calc_ecc_anom(manom, ecc_arr, tolerance=tolerance, max_iter=max_iter, use_c=use_c, use_gpu=use_gpu)

# compute the true anomalies (size: n_orbs x n_dates)
# Note: matrix multiplication makes the shapes work out here and below
tanom = 2.*np.arctan(np.sqrt((1.0 + ecc)/(1.0 - ecc))*np.tan(0.5*eanom))

return tanom, eanom



def calc_orbit(
epochs, sma, ecc, inc, aop, pan, tau, plx, mtot, mass_for_Kamp=None, tau_ref_epoch=58849, tolerance=1e-9,
Expand All @@ -70,7 +126,7 @@ def calc_orbit(
For example, if you want to return the stellar RV, this is the planet mass.
If you want to return the planetary RV, this is the stellar mass. [Solar masses].
For planet mass ~ 0, mass_for_Kamp ~ M_tot, and function returns planetary RV (default).
tau_ref_epoch (float, optional): reference date that tau is defined with respect to (i.e., tau=0)
tau_ref_epoch (float, optional): reference date that tau is defined with respect to (default: 58849)
tolerance (float, optional): absolute tolerance of iterative computation. Defaults to 1e-9.
max_iter (int, optional): maximum number of iterations before switching. Defaults to 100.
use_c (bool, optional): Use the C solver if configured. Defaults to True
Expand All @@ -89,26 +145,14 @@ def calc_orbit(

Written: Jason Wang, Henry Ngo, 2018
"""
n_orbs = np.size(sma) # num sets of input orbital parameters
n_dates = np.size(epochs) # number of dates to compute offsets and vz

# return planetary RV if `mass_for_Kamp` is not defined
# return planetary RV if `mass_for_Kamp` is not defined
if mass_for_Kamp is None:
mass_for_Kamp = mtot
ecc

# Necessary for _calc_ecc_anom, for now
if np.isscalar(epochs): # just in case epochs is given as a scalar
epochs = np.array([epochs])
ecc_arr = np.tile(ecc, (n_dates, 1))
tanom, eanom = times2trueanom_and_eccanom(sma, epochs, mtot, ecc, tau, tau_ref_epoch=tau_ref_epoch, tolerance=tolerance, max_iter=max_iter, use_c=use_c, use_gpu=use_gpu)

# # compute mean anomaly (size: n_orbs x n_dates)
manom = tau_to_manom(epochs[:, None], sma, mtot, tau, tau_ref_epoch)
# compute eccentric anomalies (size: n_orbs x n_dates)
eanom = _calc_ecc_anom(manom, ecc_arr, tolerance=tolerance, max_iter=max_iter, use_c=use_c, use_gpu=use_gpu)

# compute the true anomalies (size: n_orbs x n_dates)
# Note: matrix multiplication makes the shapes work out here and below
tanom = 2.*np.arctan(np.sqrt((1.0 + ecc)/(1.0 - ecc))*np.tan(0.5*eanom))
# compute 3-D orbital radius of second body (size: n_orbs x n_dates)
radius = sma * (1.0 - ecc * np.cos(eanom))

Expand Down
28 changes: 25 additions & 3 deletions orbitize/read_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ def read_file(filename):
have_seppacorr = np.zeros(
num_measurements, dtype=bool
) # zeros are False
if "brightness" in input_table.columns:
have_brightness = ~input_table["brightness"].mask
else:
have_brightness = np.zeros(num_measurements, dtype=bool)
if "rv" in input_table.columns:
have_rv = ~input_table["rv"].mask
else:
Expand Down Expand Up @@ -231,11 +235,14 @@ def read_file(filename):
else:
have_rv = np.zeros(num_measurements, dtype=bool) # zeros are False

# Rob: not sure if we need this but adding just in case
if "instrument" in input_table.columns:
have_inst = np.ones(num_measurements, dtype=bool)
else:
have_inst = np.zeros(num_measurements, dtype=bool)
if "brightness" in input_table.columns:
have_brightness = np.ones(num_measurements, dtype=bool)
else:
have_brightness = np.zeros(num_measurements, dtype=bool)

# orbitize! backwards compatability since we added new columns, some old data formats may not have them
# fill in with default values
Expand Down Expand Up @@ -280,9 +287,9 @@ def read_file(filename):
if not isinstance(row["object"], (int, np.int32, np.int64)):
raise Exception("Invalid object ID. Object IDs must be integers.")

# determine input quantity type (RA/DEC, SEP/PA, or RV)
# determine input quantity type (RA/DEC, SEP/PA, RV, or BRIGHTNESS)
if orbitize_style:
if row["quant_type"] == "rv": # special format for rv rows
if row["quant_type"] == "rv" or row["quant_type"] == 'brightness': # special format for rv rows
output_table.add_row(
[
MJD,
Expand Down Expand Up @@ -332,6 +339,7 @@ def read_file(filename):
)

else: # When not in orbitize style

if have_ra[index] and have_dec[index]:
# check if there's a covariance term
if have_radeccorr[index]:
Expand Down Expand Up @@ -428,6 +436,20 @@ def read_file(filename):
"defrv",
]
)
if have_brightness[index]:
output_table.add_row(
[
MJD,
row["object"],
row["brightness"],
row["brightness_err"],
None,
None,
None,
"brightness",
"defbr",
]
)

return output_table

Expand Down
6 changes: 3 additions & 3 deletions orbitize/sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,14 @@ def _logl(self, params):

if self.system.hipparcos_IAD is not None:
# compute Ra/Dec predictions at the Hipparcos IAD epochs
raoff_model, deoff_model, _ = self.system.compute_all_orbits(
raoff_model, deoff_model, _, _ = self.system.compute_all_orbits(
params, epochs=self.system.hipparcos_IAD.epochs_mjd
)

(
raoff_model_hip_epoch,
deoff_model_hip_epoch,
_,
_, _
) = self.system.compute_all_orbits(
params, epochs=Time([1991.25], format="decimalyear").mjd
)
Expand All @@ -134,7 +134,7 @@ def _logl(self, params):
).mjd

# compute Ra/Dec predictions at the Gaia epoch
raoff_model, deoff_model, _ = self.system.compute_all_orbits(
raoff_model, deoff_model, _, _ = self.system.compute_all_orbits(
params, epochs=gaiahip_epochs
)

Expand Down
44 changes: 40 additions & 4 deletions orbitize/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,15 @@ def __init__(
# List of index arrays corresponding to each rv for each body
self.rv = []

# index arrays corresponding to brightness for each body
self.brightness = []

self.fit_astrometry = True
radec_indices = np.where(self.data_table["quant_type"] == "radec")
seppa_indices = np.where(self.data_table["quant_type"] == "seppa")

brightness_indices = np.where(self.data_table["quant_type"] == "brightness")

if len(radec_indices[0]) == 0 and len(seppa_indices[0]) == 0:
self.fit_astrometry = False
rv_indices = np.where(self.data_table["quant_type"] == "rv")
Expand Down Expand Up @@ -140,6 +145,7 @@ def __init__(
np.intersect1d(self.body_indices[body_num], seppa_indices)
)
self.rv.append(np.intersect1d(self.body_indices[body_num], rv_indices))
self.brightness.append(np.intersect1d(self.body_indices[body_num], brightness_indices))

# we should track the influence of the planet(s) on each other/the star if:
# we are not fitting massless planets and
Expand Down Expand Up @@ -302,6 +308,7 @@ def __init__(

self.param_idx = self.basis.param_idx


def save(self, hf):
"""
Saves the current object to an hdf5 file
Expand Down Expand Up @@ -365,6 +372,11 @@ def compute_all_orbits(self, params_arr, epochs=None, comp_rebound=False):
vz (np.array of float): N_epochs x N_bodies x N_orbits array of
radial velocities at each epoch.

brightness (np.array of float): N_epochs x N_bodies x N_orbits of
photometric brightness predictions, assuming a Lambertian disk
reflection law, at each epoch. Normalized so that brightness=1
at maximum.

"""

if epochs is None:
Expand All @@ -386,6 +398,7 @@ def compute_all_orbits(self, params_arr, epochs=None, comp_rebound=False):
dec_perturb = np.zeros((n_epochs, self.num_secondary_bodies + 1, n_orbits))

vz = np.zeros((n_epochs, self.num_secondary_bodies + 1, n_orbits))
brightness_out = np.zeros((n_epochs, self.num_secondary_bodies + 1, n_orbits))

# mass/mtot used to compute each Keplerian orbit will be needed later to compute perturbations
if self.track_planet_perturbs:
Expand Down Expand Up @@ -489,16 +502,33 @@ def compute_all_orbits(self, params_arr, epochs=None, comp_rebound=False):
tau_ref_epoch=self.tau_ref_epoch,
)

tanom, eanom = kepler.times2trueanom_and_eccanom(sma, epochs, mtot, ecc, tau, tau_ref_epoch=self.tau_ref_epoch)


R = (sma*(1-ecc**2))/(1+ecc*np.cos(tanom))

z = (R)*(-np.cos(argp)*np.sin(inc)*np.sin(tanom)-np.cos(tanom)*np.sin(inc)*np.sin(argp))

B = np.arctan2(-R, z)+ np.pi

alpha = (1/np.pi)*(np.sin(B)+(np.pi-B)*np.cos(B))

albedo = 0.5 # NOTE: we're only fitting relative changes in brightness, so the actual value of albedo doesn't matter

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we're fitting relative changes, how will that work? Are we going to analytically marginalize over some linear scale factor term in the future in lnlike?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great question. Noting for posterity here our offline conversation that it indeed makes sense to fit a linear scale factor. I just raised issue #416 in response to this comment.

brightness = albedo*alpha/R**2
Comment on lines +505 to +517

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe not for this PR, but we should make this part of code customizable: we can replace it with a user-provided function or class, so that we can handle custom and non-Lambertian models. We could refactor this into a separate function here or make an issue to do it.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed! I raised issue #417 to keep track. Let's do it in a future PR.



# raoff, decoff, vz are scalers if the length of epochs is 1
if len(epochs) == 1:
raoff = np.array([raoff])
decoff = np.array([decoff])
vz_i = np.array([vz_i])
brightness = np.array([brightness])

# add Keplerian ra/deoff for this body to storage arrays
ra_kepler[:, body_num, :] = np.reshape(raoff, (n_epochs, n_orbits))
dec_kepler[:, body_num, :] = np.reshape(decoff, (n_epochs, n_orbits))
vz[:, body_num, :] = np.reshape(vz_i, (n_epochs, n_orbits))
brightness_out[:, body_num, :] = np.reshape(brightness, (n_epochs, n_orbits))

# vz_i is the ith companion radial velocity
if self.fit_secondary_mass:
Expand Down Expand Up @@ -572,11 +602,12 @@ def compute_all_orbits(self, params_arr, epochs=None, comp_rebound=False):
raoff[:, :, bad_orbits] = np.inf
deoff[:, :, bad_orbits] = np.inf
vz[:, :, bad_orbits] = np.inf
return raoff, deoff, vz
brightness_out[:, :, bad_orbits] = np.inf
return raoff, deoff, vz, brightness_out
else:
return raoff, deoff, vz
return raoff, deoff, vz, brightness_out
else:
return raoff, deoff, vz
return raoff, deoff, vz, brightness_out

def compute_model(self, params_arr, use_rebound=False):
"""
Expand Down Expand Up @@ -611,7 +642,7 @@ def compute_model(self, params_arr, use_rebound=False):
standard_params_arr, comp_rebound=True
)
else:
raoff, decoff, vz = self.compute_all_orbits(standard_params_arr)
raoff, decoff, vz, brightness = self.compute_all_orbits(standard_params_arr)

if len(standard_params_arr.shape) == 1:
n_orbits = 1
Expand Down Expand Up @@ -663,6 +694,11 @@ def compute_model(self, params_arr, use_rebound=False):
model[self.rv[body_num], 0] = vz[self.rv[body_num], body_num, :]
model[self.rv[body_num], 1] = np.nan

# Brightness
if len(self.brightness[body_num]) > 0:
model[self.brightness[body_num], 0] = brightness[self.brightness[body_num], body_num, :]
model[self.brightness[body_num], 1] = np.nan

# if we have abs astrometry measurements in the input file (i.e. not
# from Hipparcos or Gaia), add the parallactic & proper motion here by
# calling AbsAstrom compute_model
Expand Down
4 changes: 2 additions & 2 deletions tests/test_abs_astrometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def test_1planet():
sys.track_planet_perturbs = True

params = np.array([sma, ecc, inc, aop, pan, tau, plx, mass_b, m0])
ra, dec, _ = sys.compute_all_orbits(params)
ra, dec, _, _ = sys.compute_all_orbits(params)

# the planet and stellar orbit should just be scaled versions of one another
planet_ra = ra[:, 1, :]
Expand Down Expand Up @@ -143,7 +143,7 @@ def test_arbitrary_abs_astrom():
]
)

plxonly_fullorbit_ra, plxonly_fullorbit_dec, _ = mySystem.compute_all_orbits(
plxonly_fullorbit_ra, plxonly_fullorbit_dec, _, _ = mySystem.compute_all_orbits(
plx_only_params, epochs=np.linspace(epochs[0], epochs[0] + 365.25 / 2, int(1e6))
)

Expand Down
Loading
Loading