-
Notifications
You must be signed in to change notification settings - Fork 33
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
Keplerian orbits #279
base: master
Are you sure you want to change the base?
Keplerian orbits #279
Conversation
src/osp/scientific/kepler.cpp
Outdated
/** | ||
* @brief Wrap an angle to the range [0, 2π), useful for keplerian orbits | ||
*/ | ||
inline constexpr double wrap_angle(double angle) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when practical use const
for function parameters or local variables that aren't modified.
please also make sure the continuous integration passes before you finish your changes for this PR. |
src/testapp/sessions/orbits.cpp
Outdated
Session &uniCore, | ||
Session &uniScnFrame) | ||
{ | ||
feenableexcept(FE_INVALID | FE_OVERFLOW); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function appears to only exist on linux?
I'm not sure enabling floating point exception trapping in a platform specific manner is the right approach.
if you're doing math operations that are prone to encountering floating point exceptions, i'd rather see standardized / standard-library functionality used only, unless there's a substantial performance improvement to be had from calling out to platform specific functions.
seems like this header has the standardized stuff in it? https://en.cppreference.com/w/cpp/numeric/fenv
src/osp/scientific/kepler.cpp
Outdated
{ | ||
if (is_elliptic()) | ||
{ | ||
#if 1 // Use standard kepler solver |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whatever is going to be here in the final version, if constexpr
can likely do the job here
src/osp/scientific/kepler.cpp
Outdated
return E; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you remove the extra newlines?
@@ -133,63 +143,63 @@ static double mean_motion(double const mu, double const a) | |||
|
|||
static double get_orbiting_radius(double const ecc, double const semiMajorAxis, double const trueAnomaly) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all of these math functions look like they are noexcept
right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i am a noob at c++ but in (76-77) of kerpler.cpp double df_E = 1.0 - eccentricity * cos(E); // if eccentricity happens to be 1, a div by 0 , since c++ do-while loops execute at least once, idk if that causes issues in c++ / this specific implementation (ie it may check if eccentricity is 1 and use some other code) but every language i actually have skills in throws a exception
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my understanding is that floating point exceptions are not the same as C++ exceptions. I think if it does a divide by zero, the program will simply crash
regardless, if crashing isn't the right answer, then what should the function do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More accurately, on posix platforms it'll receive a signal that interrupts program flow and calls a signal handler (which we have no handler registered, so it'll default to crashing)
on windows it'll likely result in a structured exception handler exception being thrown (which is not the same thing as a C++ exception, and is handled completely differently, just like signal handling is done totally differently)
Regardless, throwing a c++ exception isnt the right answer, we need to have a solid determination of what we want the program to do if div by zero happens in this function.
Crashing is one choice, or we can return some kind of error code, but we want to document what the behaivor is going to be and then stick to it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A floating point math error isn't necessarily an unrecoverable error for the end application, so I don't know if crashing is a good default choice choice for handling them.
I'm not well versed in c++ error handling though, so I don't know what the right way to handle floating point errors would be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My recommendation is to add noexcept
unless someone provides a signal-handler -> exception translator, which very well might happen.
Until then, noexcept
can result in better code-gen in many situations, helps analysis tools, and it can always be removed later.
*/ | ||
static double solve_kepler_hyperbolic(double const meanAnomaly, double const eccentricity, int steps) | ||
{ | ||
double const tolerance = 1.0E-10; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can make tolerance
into a constexpr
variable here can't you?
static constexpr tolerance = 1.0E-10;
do | ||
{ | ||
double f_E = eccentricity * sinh(E) - E - meanAnomaly; | ||
double df_E = eccentricity * cosh(E) - 1.0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
f_E
and df_E
can both be const.
I know this might seem very silly to point out, but I've watched so many people make really brain dead mistakes because they've modified a variable that they intended to never be modified.
I think it's a serious mis-design of C++ that variables aren't immutable by default, and require a keyword to make mutable. Instead of the situation we have now.
delta = f_E / df_E; | ||
E = E - delta; | ||
--steps; | ||
} while (std::abs(delta) > tolerance && steps > 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor: I recommend wrapping the individual portions of your conditional expression in parens.
Watched a lot of people mistake the order of evaluation here too... Had a few bugs in prod because of it at work.
double const beta = ecc / (1 + std::sqrt(1 - ecc * ecc)); | ||
return E + 2.0 * atan2(beta * sin(E), 1 - beta * cos(E)); | ||
|
||
// return 2.0 * atan2(std::sqrt(1 + ecc) * sin(E / 2.0), std::sqrt(1 - ecc) * cos(E / 2.0)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor: Best to remove unused/commented-out code
/** | ||
* @brief Convert true anomaly (ν) to hyperbolic eccentric anomaly (E) | ||
*/ | ||
static double true_anomaly_to_hyperbolic_eccentric_anomaly(double const &ecc, double const &v) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any value in taking ecc
and v
as references instead of by value?
At least on x86_64 linux, with clang, sizeof(double)
is 8bytes, which is the same as a pointer.
So on most platforms, when passing by value, the double should be passed via register (assuming few enough parameters). On platforms where there isnt enough space to pass by register, it'll implicitly pass by reference / pointer regardless.
|
||
u = Vector3d(cos(a) * cos(b) - sin(a) * sin(b) * cos(c), | ||
cos(a) * sin(b) + sin(a) * cos(b) * cos(c), | ||
sin(a) * sin(c)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it fair to assume that C++ standard library doesn't provide any special math functions that do much / any of this for us? i'm guessing that cos and sin are all we get?
delta = f_E / df_E; | ||
E = E - delta; | ||
|
||
E = wrap_angle(E); // Sometimes this sometimes fails to converge without this line |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sometimes this sometimes -> the algorithm sometimes
// newton-rhapson | ||
do | ||
{ | ||
double f_E = eccentricity * sinh(E) - E - meanAnomaly; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor: not a show-stopper, but i personally prefer to see functions from the standard library explicitly called out as coming from the std::
namespace.
Vector3d const v, | ||
Vector3d const w, | ||
Vector3d &position, | ||
Vector3d &velocity) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it doesn't appear that position and velocity are used as input parameters, but are just output parameters.
Consider returning std::pair<Vector3d, Vector3d>
instead, and then unpacking the tuple at the place where the function is called, either using std::tie
or just structured bindings, depending on how the calling code wants to.
This is less about codegen and more about just how using the function looks, but there could be advantages for named return value optimization.
} | ||
} | ||
|
||
void KeplerOrbit::get_uvw_vectors(Vector3d &u, Vector3d &v, Vector3d &w) const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as my comment for get_state_vectors
using namespace osp; | ||
|
||
// M_PI isn't standard, so define it here. | ||
static constexpr double PI = Magnum::Math::Constants<double>::pi(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can use pi_v from https://en.cppreference.com/w/cpp/numeric/constants, if you prefer.
} | ||
|
||
std::optional<double> KeplerOrbit::get_next_time_radius_equals(const double r, const double currentTime) const { | ||
std::optional<double> apoapsis = get_apoapsis(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i like std::optional a lot from a programmer ease of use perspective, but just wanted to double check that you don't consider it a barrier to good codegen compared to using explicit checks for NaN ?
|
||
KeplerOrbitParams m_params; | ||
|
||
KeplerOrbit(KeplerOrbitParams const params) : m_params(params) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
KeplerOrbitParams
is large enough that it should probably always be passed by const& to the function.
auto const [vx, vy, vz] = sat_views(rMainSpaceCommon.m_satVelocities, rMainSpaceCommon.m_data, planetCount); | ||
auto const [qx, qy, qz, qw] = sat_views(rMainSpaceCommon.m_satRotations, rMainSpaceCommon.m_data, planetCount); | ||
|
||
std::mt19937 gen(seed); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you might get slightly better random number generation performance with the _64 version of the mt19937 because it works off of a bigger block size of random data
Added a Keplerian orbit type in scientific/kepler.h, to later build patched conics on top of