Skip to content
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

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

dylan-mcleod
Copy link

Added a Keplerian orbit type in scientific/kepler.h, to later build patched conics on top of

@dylan-mcleod dylan-mcleod marked this pull request as draft May 11, 2024 00:20
src/osp/scientific/kepler.cpp Outdated Show resolved Hide resolved
src/osp/scientific/kepler.cpp Outdated Show resolved Hide resolved
/**
* @brief Wrap an angle to the range [0, 2π), useful for keplerian orbits
*/
inline constexpr double wrap_angle(double angle)
Copy link
Member

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.

@jonesmz
Copy link
Member

jonesmz commented May 11, 2024

please also make sure the continuous integration passes before you finish your changes for this PR.

Session &uniCore,
Session &uniScnFrame)
{
feenableexcept(FE_INVALID | FE_OVERFLOW);
Copy link
Member

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

{
if (is_elliptic())
{
#if 1 // Use standard kepler solver
Copy link
Contributor

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

return E;
}


Copy link
Member

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)
Copy link
Member

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?

Copy link

@tigercoding56 tigercoding56 May 11, 2024

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

Copy link
Member

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?

Copy link
Member

@jonesmz jonesmz May 11, 2024

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.

Copy link
Author

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.

Copy link
Member

@jonesmz jonesmz Jun 23, 2024

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;
Copy link
Member

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;
Copy link
Member

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);
Copy link
Member

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));
Copy link
Member

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)
Copy link
Member

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.

@jonesmz jonesmz requested a review from EhWhoAmI June 23, 2024 12:41

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));
Copy link
Member

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
Copy link
Member

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;
Copy link
Member

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)
Copy link
Member

@jonesmz jonesmz Jun 23, 2024

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
Copy link
Member

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();
Copy link
Member

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();
Copy link
Member

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) {}
Copy link
Member

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);
Copy link
Member

@jonesmz jonesmz Jun 23, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants