Skip to content

Commit b57a70a

Browse files
committed
Name the module motion_models, and alias it where the argument shadows it
The module keeps the underscore, as preferred. What cannot move is the other side of the collision: every `motion_models` binding in the package is either the public keyword argument of MosaicSelfRef, MosaicToRef, fit_motion_models, infer_positions, determine_motion_models and organize_motion_models, or that same parameter reassigned inside its own function. Renaming it would change the documented API that the examples, the notebook and outside scripts call, which is a much larger break than a module name. So the two library files that use both -- align.py and startables.py -- import the module as `mmodels`, with a comment at the import saying why, and test_align.py does the same for the local of that name in several of its tests. Everywhere the name is free (transforms.py, the other tests, the docs, the notebook, the benchmark script) it is imported and referred to plainly as motion_models. Verified beyond the suite that the shadowed call path still works from outside: fit_motion_models(motion_models=['Linear']) on a table fits Linear, and organize_motion_models('linear') resolves, with flystar.motion_models importable under its own name. Full suite passes, docs build clean, and the API reference is back at api/flystar/motion_models/.
1 parent 08f349c commit b57a70a

15 files changed

Lines changed: 124 additions & 115 deletions

docs/alignment.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ These are handed straight to
244244
epochs to support, unless a ``motion_model_input`` column requests
245245
otherwise. Add ``'Linear'`` if you want proper motions at all -- the
246246
default fits none. Accepts a single name, a list of names, the
247-
:class:`~flystar.motionmodels.MotionModel` subclasses themselves, or a
247+
:class:`~flystar.motion_models.MotionModel` subclasses themselves, or a
248248
mixture; see the note below.
249249
* - ``fixed_params_dict``
250250
- ``None``
@@ -268,8 +268,8 @@ These are handed straight to
268268
motion_models='Linear' # a single name
269269
motion_models=['Linear'] # a list of names
270270
motion_models=['Linear', 'Acceleration'] # several
271-
motion_models=[motionmodels.Linear] # the classes themselves
272-
motion_models=[motionmodels.Linear, 'Acceleration'] # mixed
271+
motion_models=[motion_models.Linear] # the classes themselves
272+
motion_models=[motion_models.Linear, 'Acceleration'] # mixed
273273
motion_models='linear' # case does not matter
274274
275275
The valid names are ``'Empty'``, ``'Fixed'``, ``'Linear'``,

docs/benchmark_motion_models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def make_table(n_stars, n_epochs, seed=1):
7474

7575
def time_one(branch, model, n_epochs, n_stars=N_STARS):
7676
"""Time a single fit, and report which model the stars actually got."""
77-
from flystar import motionmodels as MM
77+
from flystar import motion_models as MM
7878

7979
tab = make_table(n_stars, n_epochs)
8080

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
#
8787
# autoapi walks the source statically, so every module on this branch is picked
8888
# up automatically -- including ones that post-date the old hand-maintained
89-
# automodapi list (motionmodels, parallax, startables ...).
89+
# automodapi list (motion_models, parallax, startables ...).
9090

9191
autoapi_dirs = ['../flystar']
9292
autoapi_root = 'api'

docs/examples/motion_model_example.ipynb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,9 @@
100100
"source": [
101101
"import numpy as np\n",
102102
"import matplotlib.pyplot as plt\n",
103-
"from flystar import motionmodels\n",
103+
"from flystar import motion_models\n",
104104
"from flystar.startables import StarTable\n",
105-
"from flystar.motionmodels import Empty, Fixed, Linear, Acceleration, Parallax"
105+
"from flystar.motion_models import Empty, Fixed, Linear, Acceleration, Parallax"
106106
]
107107
},
108108
{
@@ -876,7 +876,7 @@
876876
"name": "stderr",
877877
"output_type": "stream",
878878
"text": [
879-
"/Users/lwei-local/Software/flystar/flystar/motionmodels.py:346: OptimizeWarning: Degree of freedom <= 0 for some star(s). Covariance of the parameters could not be estimated. Setting parameter uncertainties to np.inf.\n",
879+
"/Users/lwei-local/Software/flystar/flystar/motion_models.py:346: OptimizeWarning: Degree of freedom <= 0 for some star(s). Covariance of the parameters could not be estimated. Setting parameter uncertainties to np.inf.\n",
880880
" result = self.run_fit(\n"
881881
]
882882
}
@@ -1167,7 +1167,7 @@
11671167
}
11681168
],
11691169
"source": [
1170-
"all_mm_map = motionmodels.motion_model_map()\n",
1170+
"all_mm_map = motion_models.motion_model_map()\n",
11711171
"tab['n_required'] = np.array([all_mm_map[mm].n_params for mm in tab['motion_model_input']], dtype=int)\n",
11721172
"tab[['n_fit', 'n_required', 'motion_model_input', 'motion_model_used']]"
11731173
]

docs/motion_models.rst

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ Motion models
55
Once every epoch sits in a common frame, each star has a time series of
66
positions, and the question becomes what curve to put through it. A *motion
77
model* is that curve. On this branch these are first-class pluggable classes in
8-
:mod:`flystar.motionmodels`, all deriving from
9-
:class:`~flystar.motionmodels.MotionModel`, and FlyStar chooses between them
8+
:mod:`flystar.motion_models`, all deriving from
9+
:class:`~flystar.motion_models.MotionModel`, and FlyStar chooses between them
1010
**per star** rather than imposing one on the whole table.
1111

1212
This is the part of the API that differs most from ``main``.
@@ -32,27 +32,27 @@ Throughout, :math:`\Delta t \equiv t - t_0`.
3232
- ``n_params``
3333
- Fit parameters
3434
- Position model
35-
* - :class:`~flystar.motionmodels.Empty`
35+
* - :class:`~flystar.motion_models.Empty`
3636
- 0
3737
- --
3838
- :math:`x(t) =` ``fill_value`` (NaN by default), :math:`\sigma_x(t) = \infty`.
3939
For stars with no usable detection at all.
40-
* - :class:`~flystar.motionmodels.Fixed`
40+
* - :class:`~flystar.motion_models.Fixed`
4141
- 1
4242
- :math:`x_0,\ y_0`
4343
- :math:`x(t) = x_0`, the weighted mean position -- no time dependence.
4444
For stars seen once, or held still on purpose.
45-
* - :class:`~flystar.motionmodels.Linear`
45+
* - :class:`~flystar.motion_models.Linear`
4646
- 2
4747
- :math:`x_0, v_x,\ y_0, v_y`
4848
- :math:`x(t) = x_0 + v_x\,\Delta t`. Constant proper motion; the
4949
workhorse.
50-
* - :class:`~flystar.motionmodels.Acceleration`
50+
* - :class:`~flystar.motion_models.Acceleration`
5151
- 3
5252
- :math:`x_0, v_{x0}, a_x,\ y_0, v_{y0}, a_y`
5353
- :math:`x(t) = x_0 + v_{x0}\,\Delta t + \tfrac{1}{2} a_x\,\Delta t^2`.
5454
For stars whose motion visibly curves.
55-
* - :class:`~flystar.motionmodels.Parallax`
55+
* - :class:`~flystar.motion_models.Parallax`
5656
- 3
5757
- :math:`x_0, v_x, \pi,\ y_0, v_y`
5858
- :math:`x(t) = x_0 + v_x\,\Delta t + \pi\,P_x(t)`. Linear motion plus
@@ -68,7 +68,7 @@ coordinates,
6868
y(t) &= y_0 + v_y\,(t - t_0) + \pi\,P_y(t)
6969
7070
:math:`\boldsymbol{P}(t)` is computed by
71-
:meth:`~flystar.motionmodels.Parallax.calc_parallax_vector` from the star's
71+
:meth:`~flystar.motion_models.Parallax.calc_parallax_vector` from the star's
7272
``ra``/``dec``, the position angle ``pa``, and the observatory location
7373
``obsLocation``. Note that ``fit_motion_models`` currently applies a single
7474
``obsLocation`` to every star in the table.
@@ -225,7 +225,7 @@ from an earlier, more complex fit.
225225
Choosing a model versus propagating with one
226226
--------------------------------------------
227227

228-
:func:`~flystar.motionmodels.determine_motion_models` answers a related but
228+
:func:`~flystar.motion_models.determine_motion_models` answers a related but
229229
distinct question, and the ``motion_models`` argument separates them:
230230

231231
* Pass your list to ask *which of the models I requested was this star fit
@@ -238,7 +238,7 @@ distinct question, and the ``motion_models`` argument separates them:
238238
The time-argument contract
239239
==========================
240240

241-
Every model's :meth:`~flystar.motionmodels.MotionModel.model`, and
241+
Every model's :meth:`~flystar.motion_models.MotionModel.model`, and
242242
:meth:`~flystar.startables.StarTable.infer_positions`, take times under one
243243
rule: **shape decides meaning.** Nothing is inferred from ``len(t)`` happening
244244
to equal ``N_stars``.
@@ -262,7 +262,7 @@ to equal ``N_stars``.
262262
For **one time per star**, pass a column vector ``t[:, np.newaxis]`` of shape
263263
``(N_stars, 1)``. A bare 1D array of length ``N_stars`` means a shared grid, not
264264
per-star times. Any other shape raises ``ValueError`` rather than being guessed
265-
at. See :func:`~flystar.motionmodels.broadcast_times`.
265+
at. See :func:`~flystar.motion_models.broadcast_times`.
266266

267267
Propagating the whole table to a single new epoch does not need the
268268
column-vector form -- pass the scalar epoch and let each star's own :math:`t_0`
@@ -285,8 +285,8 @@ Weighting
285285
---------
286286

287287
``weighting`` decides how a per-epoch uncertainty becomes a fit weight, via
288-
:func:`~flystar.motionmodels.sigma_from_error` and then
289-
:func:`~flystar.motionmodels.weight_from_sigma`, which computes
288+
:func:`~flystar.motion_models.sigma_from_error` and then
289+
:func:`~flystar.motion_models.weight_from_sigma`, which computes
290290
:math:`w = 1/\sigma^2`:
291291

292292
.. list-table::
@@ -352,7 +352,7 @@ Unusable uncertainties get weight zero, not a bad weight
352352

353353
A naive :math:`1/\sigma^2` turns a missing or pathological uncertainty into an
354354
infinite or NaN weight, corrupting the whole sum rather than excluding one
355-
point. :func:`~flystar.motionmodels.weight_from_sigma` instead assigns
355+
point. :func:`~flystar.motion_models.weight_from_sigma` instead assigns
356356
**exactly zero** whenever :math:`\sigma` is NaN, infinite, exactly zero, or so
357357
small that squaring it underflows -- and to any epoch marked invalid. Such an
358358
epoch drops cleanly out of both the fit and the :math:`\chi^2`.
@@ -396,7 +396,7 @@ per-star fit cannot see.
396396
Fitting: one star or a whole table
397397
==================================
398398

399-
:meth:`~flystar.motionmodels.MotionModel.fit` handles both, dispatching on
399+
:meth:`~flystar.motion_models.MotionModel.fit` handles both, dispatching on
400400
dimensionality:
401401

402402
* **1D** arrays of shape ``(n_epochs,)`` -- a single star, already filtered to
@@ -760,10 +760,10 @@ Adding new models
760760
=================
761761

762762
New motion models are welcome, and adding one does not mean touching the
763-
fitting machinery. A model is a single class in :mod:`flystar.motionmodels`
764-
that subclasses :class:`~flystar.motionmodels.MotionModel`, declares what it
763+
fitting machinery. A model is a single class in :mod:`flystar.motion_models`
764+
that subclasses :class:`~flystar.motion_models.MotionModel`, declares what it
765765
fits and what it needs held fixed, and implements two methods. There is no
766-
registry to edit: :func:`~flystar.motionmodels.motion_model_map` discovers
766+
registry to edit: :func:`~flystar.motion_models.motion_model_map` discovers
767767
models through ``MotionModel.__subclasses__()``, so the class is selectable by
768768
name -- ``motion_models=['Wobble']`` -- as soon as the module defining it is
769769
imported.
@@ -825,7 +825,7 @@ What to implement
825825
- ``(t, fit_params, fit_param_errs=None, fixed_params_dict=None)``,
826826
returning ``(x, y)`` -- or ``(x, y, xe, ye)`` when errors are passed in.
827827
This is what propagation calls, so it must honour the time-argument
828-
contract above; use :func:`~flystar.motionmodels.broadcast_times`
828+
contract above; use :func:`~flystar.motion_models.broadcast_times`
829829
rather than reimplementing it.
830830
* - ``run_fit``
831831
- ``(t, x, y, xe, ye, valid, fixed_params_dict=None, weighting='var',

docs/overview.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ fraction of a year. Nothing else is expected of you.
8080

8181
Where a uniform timescale is genuinely required, FlyStar converts internally.
8282
The parallax model needs the Earth's barycentric position, whose ephemeris is
83-
indexed in TDB, so :class:`~flystar.motionmodels.Parallax` does the
83+
indexed in TDB, so :class:`~flystar.motion_models.Parallax` does the
8484
``utc -> tdb`` conversion itself before evaluating
8585
:func:`~flystar.parallax.parallax_in_direction`.
8686

@@ -136,7 +136,7 @@ Where the pieces live
136136
* - :mod:`flystar.transforms`
137137
- Coordinate transformation models -- shifts, four-parameter,
138138
polynomial, Legendre, and spline/clipped variants.
139-
* - :mod:`flystar.motionmodels`
139+
* - :mod:`flystar.motion_models`
140140
- Per-star motion models and the machinery that chooses between them.
141141
* - :mod:`flystar.align`
142142
- The iterative match/transform/average loop that drives everything else.

flystar/align.py

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
import numpy as np
99
import matplotlib.pyplot as plt
1010
from tqdm import tqdm
11-
from flystar import match, transforms, plots, motionmodels
11+
from flystar import match, transforms, plots
12+
# Aliased: 'motion_models' is the public keyword argument of the aligners
13+
# and fit_motion_models, and the local it becomes, so importing the module
14+
# under its own name would shadow it inside the functions that need it.
15+
from flystar import motion_models as mmodels
1216
from flystar.starlists import StarList
1317
from flystar.startables import StarTable
1418
from astropy.table import Table, Column, vstack
@@ -376,7 +380,7 @@ def __init__(
376380
self.match_sigma_mag = match_sigma_mag
377381

378382
# Organize motion models into a list of MotionModel classes, sorted by increasing number of parameters.
379-
self.motion_models = motionmodels.organize_motion_models(motion_models)
383+
self.motion_models = mmodels.organize_motion_models(motion_models)
380384

381385
# if motion_model_for_new_star is None:
382386
# self.motion_model_for_new_star = self.motion_models[-1]
@@ -1147,7 +1151,7 @@ def setup_ref_table_from_starlist(self, star_list):
11471151
"""
11481152
col_arrays = {}
11491153

1150-
motion_model_col_names = motionmodels.all_motion_model_param_names(with_errors=True, with_fixed=True) + ['m0','m0_err','use_in_trans', 'motion_model_input', 'motion_model_used']
1154+
motion_model_col_names = mmodels.all_motion_model_param_names(with_errors=True, with_fixed=True) + ['m0','m0_err','use_in_trans', 'motion_model_input', 'motion_model_used']
11511155
for col_name in star_list.colnames:
11521156
if col_name == 'name':
11531157
# The "name" column will be 1D; but we will also add a "name_in_list" column.
@@ -1513,7 +1517,7 @@ def update_ref_table_aggregates(self, keep_orig=None, n_boot=0, seed=None, proce
15131517
motion_model_class_names += self.ref_table['motion_model_used'][keep_orig].tolist()
15141518
vals_orig['motion_model_used'] = self.ref_table['motion_model_used'][keep_orig]
15151519
vals_orig['n_params'] = self.ref_table['n_params'][keep_orig]
1516-
motion_model_col_names = motionmodels.motion_model_param_names(motion_model_class_names, with_errors=True, with_fixed=True)
1520+
motion_model_col_names = mmodels.motion_model_param_names(motion_model_class_names, with_errors=True, with_fixed=True)
15171521
for mm in motion_model_col_names:
15181522
if mm in self.ref_table.keys():
15191523
vals_orig[mm] = self.ref_table[mm][keep_orig]
@@ -1939,15 +1943,15 @@ def calc_bootstrap_errors(self, n_boot=100, seed=None, boot_epochs_min=-1, calc_
19391943
if 'Fixed' not in motion_model_list:
19401944
motion_model_list.append('Fixed')
19411945

1942-
motion_col_list = motionmodels.motion_model_param_names(motion_model_list, with_errors=False, with_fixed=False)
1946+
motion_col_list = mmodels.motion_model_param_names(motion_model_list, with_errors=False, with_fixed=False)
19431947
if calc_vel_in_bootstrap:
19441948
motion_boot_sum = {}
19451949
motion2_boot_sum = {}
19461950
for col in motion_col_list:
19471951
motion_boot_sum[col] = np.zeros((len(ref_table['x'])))
19481952
motion2_boot_sum[col] = np.zeros((len(ref_table['x'])))
19491953

1950-
all_mm_map = motionmodels.motion_model_map()
1954+
all_mm_map = mmodels.motion_model_map()
19511955
motion_model_list = [all_mm_map[mm_name] for mm_name in motion_model_list]
19521956
motion_boot_min_epochs = np.max([mm.n_params for mm in motion_model_list])
19531957

@@ -2616,7 +2620,7 @@ def = None. If not None, then this should contain an array or list of transform
26162620

26172621
# If motion_model_used in columns but params columns are missing, raise a warning and remove motion_model_used column to avoid confusion.
26182622
# if 'motion_model_used' in self.ref_list.colnames:
2619-
# motion_model_params = motionmodels.motion_model_param_names(np.unique(self.ref_list['motion_model_used']), with_errors=False, with_fixed=True)
2623+
# motion_model_params = mmodels.motion_model_param_names(np.unique(self.ref_list['motion_model_used']), with_errors=False, with_fixed=True)
26202624
# missing_params = [param for param in motion_model_params if (param not in self.ref_list.colnames) and (f'{param}_err' not in self.ref_list.colnames) and (param not in self.fixed_params_dict.keys())]
26212625
# if len(missing_params) > 0:
26222626
# warnings.warn("Warning: 'motion_model_used' column found in ref_list, but the following motion model parameter columns are missing: " + ", ".join(missing_params) + ". Removing 'motion_model_used' column to avoid confusion.")
@@ -2995,8 +2999,8 @@ def infer_positions(t, startable, motion_models=None, fixed_params_dict=None, re
29952999
if motion_models is None:
29963000
# Setting the default to None to avoid mutable default argument issue
29973001
# See https://stackoverflow.com/questions/15189245/assigning-class-variable-as-default-value-to-class-method-argument
2998-
motion_models = [motionmodels.Empty, motionmodels.Fixed]
2999-
all_mm_map = motionmodels.motion_model_map()
3002+
motion_models = [mmodels.Empty, mmodels.Fixed]
3003+
all_mm_map = mmodels.motion_model_map()
30003004
if all(isinstance(mm, str) for mm in motion_models):
30013005
mm_names = motion_models
30023006
motion_models = [all_mm_map[mm] for mm in motion_models]
@@ -3005,9 +3009,9 @@ def infer_positions(t, startable, motion_models=None, fixed_params_dict=None, re
30053009

30063010
# Always add Empty and Fixed in motion models
30073011
if 'Fixed' not in mm_names:
3008-
motion_models.insert(0, motionmodels.Fixed)
3012+
motion_models.insert(0, mmodels.Fixed)
30093013
if 'Empty' not in mm_names:
3010-
motion_models.insert(0, motionmodels.Empty)
3014+
motion_models.insert(0, mmodels.Empty)
30113015

30123016
# Otherwise, infer positions using the most complex motion model with the existing columns, until it reaches Fixed or Empty
30133017
# Sort motion models inversely by mm.n_params
@@ -3039,13 +3043,13 @@ def determine_motion_models(startable, motion_models=None, fixed_params_dict=Non
30393043
"""Determine, per star, which motion model to use.
30403044
30413045
Thin wrapper kept for backward compatibility -- the implementation lives in
3042-
motionmodels.determine_motion_models, so that startables (which align
3046+
mmodels.determine_motion_models, so that startables (which align
30433047
imports) can use it too. See there for the precedence rules.
30443048
30453049
The `processes`/`chunksize` arguments are unused; the implementation is
30463050
vectorized. They are accepted so existing calls keep working.
30473051
"""
3048-
return motionmodels.determine_motion_models(
3052+
return mmodels.determine_motion_models(
30493053
startable, motion_models=motion_models, fixed_params_dict=fixed_params_dict
30503054
)
30513055

@@ -3082,7 +3086,7 @@ def setup_ref_table_from_starlist(star_list, motion_models):
30823086
array in the original reference star list.
30833087
"""
30843088
col_arrays = {}
3085-
motion_model_col_names = motionmodels.motion_model_param_names(motion_models, with_errors=True)
3089+
motion_model_col_names = mmodels.motion_model_param_names(motion_models, with_errors=True)
30863090
for col_name in star_list.colnames:
30873091
if col_name == 'name':
30883092
# The "name" column will be 1D; but we will also add a "name_in_list" column.
@@ -3276,7 +3280,7 @@ def add_rows_for_new_stars(ref_table, star_list, idx_list, motion_model_name='Fi
32763280
idx_lis_new = np.array(list(set(idx_lis_orig) - set(idx_list)))
32773281
N_newstars = len(idx_lis_new)
32783282

3279-
mm_map = motionmodels.motion_model_map()
3283+
mm_map = mmodels.motion_model_map()
32803284
mm = mm_map[motion_model_name]
32813285

32823286
# Add optional fixed params default values into fixed params dict, prioritizing values in fixed_params_dict
@@ -3948,7 +3952,7 @@ def transform_from_object(starlist, transform):
39483952
# For more complicated motion_models,
39493953
# we can't easily transform them, set the values to nans and refit later.
39503954
if mot:
3951-
motion_model_params = motionmodels.motion_model_param_names()
3955+
motion_model_params = mmodels.motion_model_param_names()
39523956
for param in motion_model_params:
39533957
if param in keys:
39543958
starlist_f[param] = np.nan

0 commit comments

Comments
 (0)