Closed mkelley closed 1 month ago
Hello @mkelley! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found:
sbpy/activity/tests/test_dust.py
:Line 27:80: E501 line too long (88 > 79 characters) Line 38:80: E501 line too long (87 > 79 characters) Line 75:80: E501 line too long (88 > 79 characters) Line 126:80: E501 line too long (84 > 79 characters) Line 189:80: E501 line too long (82 > 79 characters) Line 484:80: E501 line too long (86 > 79 characters) Line 496:80: E501 line too long (85 > 79 characters)
sbpy/data/__init__.py
:Line 76:80: E501 line too long (80 > 79 characters) Line 234:80: E501 line too long (84 > 79 characters) Line 246:80: E501 line too long (87 > 79 characters) Line 264:80: E501 line too long (87 > 79 characters) Line 334:80: E501 line too long (81 > 79 characters) Line 340:80: E501 line too long (81 > 79 characters) Line 346:80: E501 line too long (83 > 79 characters) Line 894:80: E501 line too long (81 > 79 characters) Line 960:80: E501 line too long (89 > 79 characters)
sbpy/data/names.py
:Line 432:80: E501 line too long (80 > 79 characters) Line 438:80: E501 line too long (80 > 79 characters) Line 442:80: E501 line too long (80 > 79 characters)
sbpy/dynamics/models.py
:Line 53:80: E501 line too long (80 > 79 characters) Line 92:80: E501 line too long (81 > 79 characters) Line 190:80: E501 line too long (80 > 79 characters) Line 225:80: E501 line too long (80 > 79 characters) Line 286:80: E501 line too long (92 > 79 characters) Line 311:80: E501 line too long (80 > 79 characters) Line 344:80: E501 line too long (80 > 79 characters)
sbpy/dynamics/state.py
:Line 32:80: E501 line too long (84 > 79 characters) Line 55:80: E501 line too long (82 > 79 characters) Line 58:80: E501 line too long (88 > 79 characters) Line 79:80: E501 line too long (80 > 79 characters) Line 128:80: E501 line too long (83 > 79 characters) Line 137:80: E501 line too long (80 > 79 characters) Line 140:80: E501 line too long (84 > 79 characters) Line 142:80: E501 line too long (81 > 79 characters) Line 298:80: E501 line too long (88 > 79 characters) Line 415:80: E501 line too long (83 > 79 characters) Line 418:80: E501 line too long (86 > 79 characters)
sbpy/dynamics/syndynes.py
:Line 43:80: E501 line too long (86 > 79 characters) Line 57:80: E501 line too long (84 > 79 characters) Line 135:80: E501 line too long (87 > 79 characters) Line 196:80: E501 line too long (81 > 79 characters) Line 199:80: E501 line too long (81 > 79 characters) Line 220:80: E501 line too long (81 > 79 characters) Line 268:80: E501 line too long (81 > 79 characters) Line 297:24: E721 do not compare types, use 'isinstance()' Line 437:80: E501 line too long (83 > 79 characters) Line 455:80: E501 line too long (80 > 79 characters) Line 517:80: E501 line too long (82 > 79 characters) Line 606:80: E501 line too long (83 > 79 characters)
sbpy/dynamics/tests/test_models.py
:Line 57:80: E501 line too long (88 > 79 characters) Line 72:80: E501 line too long (88 > 79 characters) Line 163:80: E501 line too long (86 > 79 characters) Line 193:80: E501 line too long (86 > 79 characters)
sbpy/dynamics/tests/test_state.py
:Line 41:80: E501 line too long (83 > 79 characters) Line 54:80: E501 line too long (82 > 79 characters) Line 114:80: E501 line too long (95 > 79 characters) Line 158:80: E501 line too long (86 > 79 characters) Line 197:80: E501 line too long (83 > 79 characters) Line 220:80: E501 line too long (84 > 79 characters) Line 239:80: E501 line too long (80 > 79 characters) Line 279:80: E501 line too long (82 > 79 characters) Line 285:80: E501 line too long (94 > 79 characters) Line 286:80: E501 line too long (88 > 79 characters) Line 287:80: E501 line too long (110 > 79 characters) Line 290:80: E501 line too long (88 > 79 characters) Line 312:80: E501 line too long (81 > 79 characters) Line 340:80: E501 line too long (88 > 79 characters) Line 372:80: E501 line too long (93 > 79 characters) Line 382:80: E501 line too long (118 > 79 characters) Line 391:80: E501 line too long (95 > 79 characters) Line 396:80: E501 line too long (97 > 79 characters) Line 435:80: E501 line too long (80 > 79 characters) Line 438:80: E501 line too long (80 > 79 characters) Line 446:80: E501 line too long (81 > 79 characters) Line 453:80: E501 line too long (80 > 79 characters) Line 490:80: E501 line too long (82 > 79 characters) Line 491:80: E501 line too long (91 > 79 characters) Line 509:80: E501 line too long (80 > 79 characters) Line 559:80: E501 line too long (86 > 79 characters)
sbpy/dynamics/tests/test_syndynes.py
:Line 166:80: E501 line too long (83 > 79 characters) Line 190:80: E501 line too long (83 > 79 characters) Line 203:80: E501 line too long (83 > 79 characters) Line 337:80: E501 line too long (87 > 79 characters) Line 373:80: E501 line too long (85 > 79 characters) Line 378:80: E501 line too long (84 > 79 characters) Line 392:80: E501 line too long (83 > 79 characters) Line 396:80: E501 line too long (89 > 79 characters) Line 406:80: E501 line too long (85 > 79 characters) Line 438:80: E501 line too long (85 > 79 characters) Line 440:80: E501 line too long (84 > 79 characters) Line 451:80: E501 line too long (86 > 79 characters) Line 463:80: E501 line too long (81 > 79 characters) Line 478:80: E501 line too long (83 > 79 characters) Line 481:80: E501 line too long (86 > 79 characters) Line 494:80: E501 line too long (80 > 79 characters) Line 495:80: E501 line too long (86 > 79 characters)
sbpy/utils/decorators.py
:Line 69:80: E501 line too long (84 > 79 characters) Line 124:80: E501 line too long (84 > 79 characters)
Attention: Patch coverage is 99.81343%
with 2 lines
in your changes missing coverage. Please review.
Project coverage is 82.99%. Comparing base (
eaff110
) to head (057efbc
). Report is 1 commits behind head on main.
Files | Patch % | Lines |
---|---|---|
sbpy/dynamics/state.py | 98.75% | 2 Missing :warning: |
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
@hhsieh00 FYI, readthedocs builds documentation on pull requests (see the "checks" below). For your convenience, the time and dynamics documentation are at:
https://sbpy--394.org.readthedocs.build/en/394/sbpy/time.html https://sbpy--394.org.readthedocs.build/en/394/sbpy/dynamics.html
Unfortunately, removing the blackslashes causes Sphinx to interpret those lines as parts of an enumerated list, so "(2001) Einstein" becomes "2001. Einstein". However, we can fix that by following the closing parenthesis with a non-breaking space.
https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#enumerated-lists
👍🏻 An example has been added.
Adds the capability to generate syndynes and synchrones. This PR is essentially four enhancements:
State
objects to hold particle position, velocity, and time.astropy.time
system.DynamicalModel
abstract base class to define an API for solving the equations of motion, and implement a few simple models.SynGenerator
and supporting classes which can generate syndynes/synchrones.Design choices are given below.
State
objectsDynamical state is 3D position, velocity, and time. The
sbpy.dynamics.State
object encapsulates these quantities using theastropy.coordinates
sub-module. State vectors are internally stored as a coordinate frame object with position and velocity data. Example coordinate frame objects areHeliocentricEclipticIAU76
,FK5
, andICRS
. Some coordinate frames require observation time for transformation to other coordinate frames, e.g., from a heliocentric to a barycentric frame. Time is added to the frame as needed. In addition,sbpy
adds anArbitraryFrame
, which is used by default and cannot be converted to other frames.The state's time attribute is typically an
astropy.time.Time
object. However, when theArbitraryFrame
is used the state's time attribute may be anastropy.units.Quantity
.Create a
State
object for a particle at x=2 au, moving with v_y=30 km/s, on 2023 Dec 08:States can also be arrays of states:
To specify a coordinate frame, use the
frame
keyword argument:Alternative implementations
Initially,
State
stored its data asr
,v
, andt
arrays. This involved extra code for parameter checking and coordinate transformations. Switching to usingBaseCoordinateFrame
objects simplified both of these aspects. However,t
still must be saved internally, as some coordinate frames do not use time.State
could instead useastropy.coordinates.SkyCoord
.SkyCoord
can store 3D vectors, but is also designed to accommodate 2D representations and operations on the celestial sphere. Dynamical state must only be 3D.BaseCoordinateFrame
data could also be 2D, but is more general and lacks many ofSkyCoord
's 2D-based convenience methods. I also found confusingSkyCoord
's tendency to duplicate position and velocity vectors in its coordinate frame objects:Sub-classing
SkyCoord
orBaseCoordinateFrame
would complicate theState
API, especially ifSkyCoord
was sub-classed, whereas the goal is to keep the API simple.State
could have been based onEphem
.Ephem
lacks coordinate frame information. Functionally, this would be similar to savingr
,v
, andt
as their own arrays.Ephemeris time
NAIF SPICE uses TDB seconds from the J2000 epoch for time coordinates. This is not explicitly supported in
astropy.time
, so a newSpiceEphemerisTime
has been created. By importing thesbpy.time
sub-module, it becomes available for use as the format "et":Dynamical integrations (below) will convert
Time
objects to ephemeris time.Dynamical models
A lightweight approach to integrating the equations of motion has been implemented in
sbpy.dynamics
. The newDynamicalModel
abstract base class defines the API and implements solving the equations withscipy.integrate.solve_ivp
. The implementation is straightforward if we bumpsbpy
's minimum supportedscipy
version from 1.3 to 1.4, which allows for arguments to be passed fromsolve_ivp
to the integrated functions.solve_ivp
keyword arguments, such as solution tolerances, or the choice of integrator, are set duringDyanmicalModel
initialization. The default arguments were designed for high precision: a relative tolerance of 2.3e-14, use of a function for the Jacobian matrix (as opposed to numerical estimates), and use of the LSODA integrator. All of these may be overridden by the user at the time of initialization.Inheriting classes provide methods that calculate the derivatives of position and velocity, and the Jacobian matrix. Time is given as a float in units of seconds, and position and velocity as a 6-element array (
[x, y, z, v_x, v_y, v_z]
) in units of kilometers and kilometers per second.The solution is generated using the
solve
method, whose arguments are the initial dynamical state and the time at which the solution is desired. Additional arguments fordx_dt
anddf_drv
are possible, and necessary in order to pass thebeta
parameter for integrations considering radiation pressure.Three dynamical model implementations are defined,
FreeExpansion
,SolarGravity
, andSolarGravityAndRadiationPressure
. The following example integrates a pure-gravitational orbit around the Sun:Syndynes and synchrones
The capability to produce syndynes and synchrones has been added to
sbpy.dynamics
with theSynGenerator
class. AState
object is used for the dust source, and dust particle orbits are parameterized using beta, the ratio of the force from solar radiation to the force from solar gravity.The source object state and dust particle states are integrated using the
DynamicalModel
framework. TheSolarGravityAndRadiationPressure
model is used by default, but others may be provided at the time theSynGenerator
object is initialized. This framework should allow for more complex problems, such as planetary perturbations, or particle fragmentation, provided a dynamical model can be developed to support them.The integrated particle states are saved in the
particles
attribute. However, it is more convenient get the syndynes and synchrones packaged asSyndyne
andSynchrone
objects, which are specializedState
objects:If an observer is provided to the
SynGenerator
object at initialization, then the syndyne/synchrone object will have acoords
attribute, which is aSkyCoord
object containing the observed coordinates of the syndyne/synchrone test particles.Syndynes and synchrones may be converted to
Ephem
objects. The sbpyDataClass
fields were updated to include the beta parameter (beta_rad) and relative time (t_relative).Alternative implementations
Initially,
SynGenerator
had a two step process:The initial states were generated at initialization, and final states by the
solve()
method. Two steps allows for the initial particle states to be modified by the user before being integrated, e.g., to give them a non-zero ejection velocity. This is still possible in the implemented design, but would be instead addressed by sub-classing and overridinginitialize_states
:Other changes
There are some improvements to the activity.dust documentation. Some package infrastructure files have been edited with small improvements. The pytest configuration now respects the options in setup.cfg.
Addresses #19.