hannorein / rebound

💫 An open-source multi-purpose N-body code.
https://rebound.readthedocs.io/
GNU General Public License v3.0
824 stars 218 forks source link

Rotations #638

Closed dtamayo closed 1 year ago

dtamayo commented 1 year ago

Hi Hanno, @tigerchenlu98, @shadden

Playing around with tides_spin (and debugging my own issues!) I think we do need to add some functionality for doing rotations in order for people to be able to use it easily. Obviously none of this is specific to spins, so I think it makes sense for these general functions to be in REBOUND. Not looking to merge now, but wanted to give you working code so you could try it out.

Probably the best way to get a feel for what might be a good API is to try out different things one might to track/plot in the examples. I love all the great examples Tiger's put together--I thought I'd add an even simpler example to more slowly go through some of the issues someone new would need to think through carefully. I think the issues with what might be the best API might be best resolved by looking through that example and how I'm calculating things. You can find it on the newpr91 branch of REBOUNDx (I've also sent Tiger a pull request to add to the main pull request).

https://github.com/dtamayo/reboundx/blob/newpr91/ipython_examples/SpinsIntro.ipynb

Here are my main questions:

The main question I have is whether we want properties and methods to return vec3ds or lists. For example I've added a hvec and evec attribute to reb_orbit (both as reb_vec3ds) so we can easily get the orbit normal and eccentricity vectors. On the one hand it's nice if these are returned as reb_vec3ds if we want to subsequently want to rotate them using the built-in methods. But we're typically also trying to store outputs from calls to sim.integrate in lists. So we want to say something like h[i] = ps[1].hvec, but this doesn't work if hvec returns a reb_vec3d.

sim.rotate_simulation(normalvec=rebx.calculate_total_angular_momentum())
rebx.rotate_params(normalvec=rebx.calculate_total_angular_momentum())

you would get the wrong behavior, since you'd have rotated the orbits in the first call, and when you called rebx.calculate_total_angular_momentum() the second time, you would get a different answer (with rotated orbits but unrotated spins). Not sure what the best approach is there.

hannorein commented 1 year ago

Thanks! I need to think about it for a bit!

A short (?) question in the meantime. How can you define a rotation with just a normal vector of a plane? Don't you need a unit vector and an angle to describe an arbitrary rotation? So there must be some implicit assumption. It's probably obvious, but what is it?

FWIW, I think the rotate_simulation() function should be generic to allow for arbitrary rotations. So it should get a unit vector and an angle (or three Euler angles, or one quaternion) as an argument.

dtamayo commented 1 year ago

This is not obvious and I have to work it out every time I come back to it, so worth doing (and documenting!) well..

We have an original ref plane, and now want to consider a different rotated ref plane. The natural x axis is then along the line of nodes where the two planes intersect. Of the two possible directions along the line of nodes, I chose the Zorig cross znew direction. This lines up with the definition of the ascending node, when the new reference plane is an orbital plane.

You’re right that in order to specify an arbitrary rotation, we need to specify one additional angle (so x can point along any direction, not just line of nodes). That’s what reb_vec3d.rotate_XYZ_orbital_xyz is doing (and rotate simulation is just calling that with omega =0 for that final rotation). All the variable names follow Murray and Dermott sec 2.8 if it helps.

I tried to think about the rotations a bit. I think for all effects, it makes sense yo write them using vector dot products and cross products like Tiger did for tides (and never call any rotation functions). I think for these functions we should choose whatever will be easier to figure out for the user. I think something with angles or normal vectors is probably easier, but open to alternatives!

On Fri, Dec 30, 2022 at 1:36 PM Hanno Rein @.***> wrote:

Thanks! I need to think about it for a bit!

A short (?) question in the meantime. How can you define a rotation with just a normal vector of a plane? Don't you need a unit vector and an angle to describe an arbitrary rotation? So there must be some implicit assumption. It's probably obvious, but what is it?

FWIW, I think the rotate_simulation() function should be generic to allow for arbitrary rotations. So it should get a unit vector and and angle (or three Euler angles, or one quaternion) as an argument.

— Reply to this email directly, view it on GitHub https://github.com/hannorein/rebound/pull/638#issuecomment-1368097555, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA2ABF273RF5DB4Q4RMZC53WP5IWHANCNFSM6AAAAAATNEV4IA . You are receiving this because you authored the thread.Message ID: @.***>

hannorein commented 1 year ago

I see. Thanks! That helps.

I still have some hesitations. Rotations are so generic, but a lot of the code right now is hard-coded for a specific use case with rather specific definitions. Admittedly, the use case is celestial mechanics. But at least for me, it would be much easier to understand what's going on conceptually if there were a "rotation object". Then one could do something like:

rot_1 = rot_from_normalZ_to_normal(nx, ny, nz) # generate rotation from a normal vector
rot_2 = rot_orbital(Omega, inc, omega) # all kind of convenience functions to generate commonly used rotations
rot_3 = rot_x(angle) # rotation around the x axis
rot_4 = rot_2 * rot_1 # apply one rotation after another
... 
sim.rotate(rot) # rotate simulation
vec.rotate(rot) # rotate a single vector
...
sim.rotate(rot.inverse()) # use inverse 
dtamayo commented 1 year ago

Oh I see what you mean! The current functions are written to chain rotations like that, so that makes perfect sense. I really like just naming one type of specialized rotation carefully and then just having an inverse rather than a separate name.

Are you thinking of doing something fancier using quaternions? I'm not familiar with them...writing inverses using simple rotations along individual axes like I have hardcoded now would I think require the rotation object to collect rotations so that in can later perform their inverses in reverse order. Maybe a linked list in C? Is there an easier way? Maybe I'm too narrow-sighted, but I don't think performance is too important for these for typical people's use-cases (it seems clear to me now that effects should really be doing everything using vectors/tensors). Would these ever get used for visualization? Or do you alway use specialized functions for shaders?

I'm a bit worried about getting this and the parameter naming changes done before going to AAS and having classes start, but I don't want to do a sloppy job on either!

Do you think ps[1].hvec and evec should return a list (to e.g. be consistent with calculate_angular_momentum()), or a reb_vec3d? And should vec.rotate(rot) update vec or return a new one?

dtamayo commented 1 year ago

Maybe we don't have to collect rotations? Could we store a forward and inverse rotation matrix, and any time a rotation is added, update both matrices appropriately?

hannorein commented 1 year ago

I'm happy to implement the rotation object thing. How it's handled in the background is not important and should be opaque to the user. It can be a rotation matrix, quaternions, or Euler angles. They are all equivalent.

For chaining rotations, you do not need to store a chain or the inverse. Think of it as multiplying matrices togethers. You can always calculate the inverse when you need it.

hannorein commented 1 year ago

Regarding hvec, I think a list. We already do that for particle.xyz. The reb_vec3d is mostly needed for c where there are no lists...

hannorein commented 1 year ago

Working on this on the dtamayo-rotations branch

hannorein commented 1 year ago

(Minor: I'm renaming the function reb_simulation_rotate from reb_rotate_simulation. This makes it clear that it acts on a simulation and it's consistent with reb_simulation_iadd, reb_particle_iadd, etc)

hannorein commented 1 year ago

Ok. I think this was definitely worth it.

Here is an example of how to use the API in C (haven't done anything in python yet): https://github.com/hannorein/rebound/blob/dtamayo-rotations/examples/quaternions/problem.c

The way one can now construct transformations looks really nice. For example, have a look at this code: https://github.com/hannorein/rebound/blob/dtamayo-rotations/src/rotations.c#L122

How do you want to proceed? I'm happy to continue and convert all the remaining functions that you have already written into the new framework. But you've probably thought about this way more than me. I'm also happy to do it together over zoom if you want.

I can also definitely have a go at the python side...

dtamayo commented 1 year ago

This is amazing, thanks!! If you'd be up for implementing the python side of the rotations, I'm very happy to update everything to the new syntax.

Are you thinking of also using quaternions to scale vectors? If not, what about using 'rotate' instead of 'quat'? I would immediately be able to guess what e.g. 'reb_rotate(v, rot)' does, but maybe 'reb_quat_act' is less obvious?

One thing I wasn't sure about was whether these rotations should return new vectors or update them in place. I guess if in Python vec3d has a .rotate method, the latter makes more sense? There aren't any reb_vec3d's in the simulation struct that would affect things if we tried to calculate a rotated version of them and didn't realize we were affecting any subsequent integration?

hannorein commented 1 year ago

Alright, I'll have a look at the python side.

I don't like rotate instead of quat because rotate is a verb. We could use rotation. But I somehow still prefer quat as it more accurately describes the object.

We could rename act to rotate. Not sure about this one. I don't see a use case for scaling right now, but it also seems unnecessary to exclude.

I think the functions should return a new vector. Compare with the particle add and multiply fucntion. There can be a special "in place" function, like iadd.

hannorein commented 1 year ago

Also, let me know if you need an extra functions. For example you might need one that creates a rotation from one vector to another.

dtamayo commented 1 year ago

You're definitely right it should be a noun. I'm happy with quat! I think I like rotate instead of act, because at least on the Python side, I think it will always be object.rotate, but happy with whatever you prefer.

And that does seem like a useful rotation to add, though I don't think we need it specifically for the transformations we put in.

Also amazed how simple it is to calculate the inverse using quaternions!

hannorein commented 1 year ago

You used @classmethod on the python side. That seems like the right approach. Do you know what the earliest python version is that supports it?

dtamayo commented 1 year ago

As far as I can tell around Python 2.6 ~ 2007?

https://peps.python.org/pep-0318/#:~:text=Two%20decorators%20(%20classmethod()%20and,in%20Python%20since%20version%202.2. https://peps.python.org/pep-3129/

I tried to change the reb_vec3d init to accept either the default x,y,z , a list, or a reb_vec3d. I thought that way any Python function that needs to call a C function that takes reb_vec3d can be written to take a vec argument that the user can pass as a list or a reb_vec3d. The function then passes reb_vec3d(vec) to C. Maybe you see something more elegant!

And please feel free to just implement the basic rotation python side, and I can fill in and document any leftover C functions in quat.c and update the ones I put in.

hannorein commented 1 year ago

Ok. Great. Thanks!

hannorein commented 1 year ago

I'll rename reb_vec3d and reb_quat to Vec3d and Quat in python. That way it's consistent with reb_simulation -> Simulation and reb_particle -> Particle

hannorein commented 1 year ago

The basic python implementation should be working now. A few things:

You were right about the in-place issue. It's not so straightforward. Although a bit more cumbersome, I've now named the methods irotate if the rotation is done in place. For example: reb_simulation_irotate(sim, quat) in C and sim.irotate(quat) in python. That's consistent with add and iadd functions we already have. I'm happy to change it again if we want to. In the end, it seems unlikely that we ever want to create a copy of a simulation when we rotate it. And sim.integrate() sim.move_to_com() also don't return a copy.

The Quat constructor can create a rotation by either using the cartesian coordinates (ix, iy, iz, r), or by using an angle and axis. If no argument is passed, it returns the identity. I think for other rotations, we should just use a @classmethod. Otherwise the constructor becomes illegible. And the class methods can be nicely documented.

In summary, one can now do things such as

q1 = rebound.Quat()
q2 = rebound.Quat(angle=1,axis=[0,0,1])
q3 = rebound.Quat.with_orbital(omega=1,inc=2,Omega=2)
q4 = q3 * q2 * q1  # q3 applied after q2, after q1
sim.irotate(q4)
q5 = q4.inverse()
sim.irotate(q5)
hannorein commented 1 year ago

I've implemented a function to go from quaternions to omega, inc, Omega. https://github.com/hannorein/rebound/blob/dtamayo-rotations/src/rotations.c#L156

This mapping has degeneracies. Or more precisely, Euler angles have degeneracies. So one doesn't always get the same angles back that one used when constructing the quaternion. There's no way to avoid this. But it should be ok as the transformations themselves are not degenerate. You must have the same problems in reb_tools_calc_plane_Omega_inc. However, I'm not sure what conventions went into it that routine, so the values for inc, Omega might differ in some cases.

Edit: Actually, never mind, it's different when you have a normal vector....

dtamayo commented 1 year ago

I could use some feedback on naming, that I think comes from the following tension:

For most basic rotations, we want to take a vector and simply rotate it (keeping the coordinate axes fixed). But I think for the typical celestial mechanics type applications, we have a fixed physical vector, and we want its components expressed in rotated reference axes (so we need to do the inverse rotation on the vector). How do we make what's being done in the names obvious?

Given that (I think!) in most applications we want the inverse rotations, I would personally find it a bit annoying and counterintuitive to only have the forward rotations and have to think through the fact that I need to take the forward rotations.inverse() every time. So if we only want one set for the complicated compound rotations, I would vote for just having the inverse reference frame rotations, and let people take the inverse for forward rotations, or build up their compound rotations themselves. We could also just have both (even though from_angles and reference_frame_rotation_from_angles are just inverses of one another). What about these names? I think I like 'Quad.from_angles' better than 'Quad.with_orbital' since it is a generic Euler angle rotation (though I do like giving the angles the names Omega, inc and omega since I would guess most people using REBOUND would be more likely to be able to picture what those mean than generic Euler angle names).

Forward rotations Quad.from_angles(Omega, inc, omega)

Inverse rotations Quad.reference_frame_rotation_from_angles(Omega, inc, omega=0) Quad.reference_frame_rotation_from_vectors(newz, newx=None)

Also, what about renaming rotations.c to quaternions.c ?

hannorein commented 1 year ago

No preference between rotations.c and quaternions.c.

In general, I would only ever implement one direction of a rotation. Then in the examples, calculate the inverse if needed. This is much easier to understand than having to look up the documentation/implementation of two similar functions.

Again, in general, I think we should not implement many rotations. It's really easy now to construct new ones with literally one (long) line of code:

q = rebound.Quat(angle=Omega,axis=[0,0,1]) * rebound.Quat(angle=inc,axis=[1,0,0]) * rebound.Quat(angle=omega,axis=[0,0,1])

So this can easily be part of the example/user's code.

I don't think angles is good. The reason being: there are 12 different combinations of Euler angles (XYX, XZX, XYZ, ...). The one we use for Omega, inc, omega is just one specific one. So I think it should include the word orbit or celestial.

I'm ok with either with or from for the constructor names. The reason I chose with initially, is because I didn't want to imply that the rotation is "from the angles omega, Omega, inc to some other angles". I think you have that issue in the from_vectors name as well. But from is more consistent with sim.from_archive(), etc. I don't know.

hannorein commented 1 year ago

One more thought: Let's make the vector method generic something like: ...vectors(from=[0,0,1], to=[0,1,0]). I think that's better than hard-coding it for the z direction. I can implement that (I think there's a trick to make that numerically stable for all angles)...

dtamayo commented 1 year ago

I think that's a really useful building block, though I think we could find a better name? What you have in mind is finding the rotation such that if you put in [0,0,1] you'd get back [0,1,0] right? Maybe something as simple as Quad.rotation(from=, to=)?

But I actually feel pretty strongly that we should also have something like axes_rotation, which in python could either take angles or vectors. I think just having your generic individual rotations makes sense for simple cases, but it's tricky for compound rotations.

You are perhaps the strongest spatial thinker I know, and have spent a lot of time programming rotations. As a new user, I would wonder for a second rotation whether the from vector should be in my original coordinate system, or after the first rotation, so I would have to look that up in the documentation. And then I'd have to draw out and think carefully about which are the rotations I need to do and in what order.

Since I think the convention in celestial mechanics is to specify the coordinates of the reference directions (e.g. the RA and Dec of ecliptic pole and vernal equinox, or z = orbit normal), I think Quat.axes_rotation(newz, newx=None) is all most users would want to do, and it would be nice if they didn't have to think about how those rotations need to take place. If someone wants to do something more complicated or customized, they have all the intuitive Quat building blocks to chain their specific rotations with.

In fact, if I had to choose, I would keep Quat.axes_rotation(newz, newx) as a constructor, and get rid of all the other classmethods using euler angles.

As for quaternion_to_angles, it might make sense to use the quaternion to rotate an orbit in the reference plane, and use particle_to_orbit to get the angles. Clearly the degeneracies still exist, but we've already put the work into dealing with edge cases and quadrant placements (I wasn't sure what you meant by the comment that the quadrants might be wrong)

hannorein commented 1 year ago

I'm not sure if we're on the same page. Can you clarify why you think there is a need for axis_rotation(newz) if you could just use rotation(from=[0,0,1], to=newz)? The later seems much more intuitive (an general) to me.

dtamayo commented 1 year ago

I agree that for a single rotation it would not make sense to do anything more complicated. But say I have orbital elements in equatorial coordinates and I set up my sim, and now want to rotate to a new x and z axis, for which I know the vectors.

So I might do from=[0,0,1] to=newz. But then for the x rotation, I might wonder whether I should use my xvec in the original coordinate system, or whether I would need from to be the xvec in my intermediate reference system. I would also wonder whether the order in which I do the rotations matter. I could certainly look at the documentation and think about it and figure it out, but I'd probably need to draw things for myself and test the outputs to check whether things are behaving correctly.

It just seems like such a routine transformation, that I would prefer to have a documented

Quad.axes_rotation(newz, newx)

that doesn't require me to understand rotations and whether or not they commute in order to do what I'm trying to do. It also requires me writing less code in the output block.

hannorein commented 1 year ago

Oh, you are thinking of two separate rotation. Sorry, I didn't get that initially. I still don't quite see the use case for it, but maybe it's not worth discussing any further. We're really just talking about whether it's worth implementing a convenience method or not.

I've implemented the "from to" function (name can be changed). I think you can now code up the "axis_rotation" function in a few lines like this (I'm still a bit confused about the definition of newx, so this probably needs to be adjusted):

def axis_rotation(newz, newx=None):
    q1 = rebound.Quat.with_from_to(fromv=[0,0,1], tov=newz)
    if newx is None:
        return q1
    return rebound.Quat.with_from_to(fromv=[1,0,0], tov=newx) * q1
hannorein commented 1 year ago

Ok. I think I now see what you mean. Sorry, I'm slow. In your example newz points towards the ecliptic pole and newx towards the vernal equinox. But for that to be meaningful, the two vectors need to be orthogonal. Will there be an exception if they are not (if so to what accuracy) or will it just result in undefined behaviour?

hannorein commented 1 year ago

With that new insight, the implementation for the axis_rotation function would be

q1 = rebound.Quat.with_from_to(fromv=pole, tov=[0,0,1]) # pole is now on the z axis
q2 = rebound.Quat.with_from_to(fromv=equinox.irotate(q1), tov=[1,0,0]) # equinox is now on the x axis
q = q2*q1

where pole and equinox point towards the pole and equinox in the original frame. But it will lead to non meaningful behaviour if the vectors are not orthogonal.

dtamayo commented 1 year ago

The problem is you're too quick! I'm just advocating for the rest of us :)

I think that makes perfect sense...my vote would be for subtracting (newz dot newx)zhat from newx to make them perpendicular. I think there are many cases where people have RA Dec for the pole and node, which presumably will only be perpendicular to the number of decimal places for which the angles are listed. I think it's OK to put the burden on the user of choosing perpendicular vectors?

hannorein commented 1 year ago

Sounds good! Glad we're on the same page now! Just put it in the documentation that the vectors should be perpendicular. (I bet the documentation will be longer than the implementation 😉)

dtamayo commented 1 year ago

Thank you so much for all this help--I really like how this has come together!

I really like your from, to notation, since it makes really clear what the rotation is going to end up doing without needing to specify whether we are rotating the vector or rotating the axes. I think we should definitely do the same for the other two constructors. These could be

from_reference_to_orbital from_reference_to_rotated_axes

though I think from_reference is probably OK to leave implied, so I'm writing them as

to_orbital to_rotated_axes

With these conventions, I think it might make code really readable to rename Quad as Rotation

e.g.

vec.rotate(Rotation.from_to(fromv=, tov)) sim.rotate(Rotation.to_rotated_axes(newz=, newx=))

If you still prefer Quad I'm OK with keeping it though.

I also like just removing 'with' from the alternate constructors...I think I'd prefer e.g. init_to_rotated_axes, but init is left implied in python...

I much prefer sim.rotate to sim.irotate. Like you said, I don't see a real use case for a copy, and sim.move_to_com() does things in place. I like the distinction for objects that can be part of a Simulation, e.g. Particle and Vec3d. There you really could have cases where you do and don't want things to be done in place, and it makes sense to have both. What do you think?

I was also curious why you rewrote the __getitem__ and __setitem__ for Vec3d

dtamayo commented 1 year ago

Also happy to zoom if it's faster to talk

hannorein commented 1 year ago

TLDR: yes to everything.

to_orbital and to_rotated_axes make sense. Adding from_reference doesn't really add anything unless you say what reference is...

Yes to renaming Quad to Rotation (but then we should keep rotations.c).

Yes to removing with.

Yes to renaming irotate to rotate. It's actually quite confusing because i could also mean inverse. I think for Vec3d we want q * vec to return a copy. But vec.rotate(q) to replace vec in place (and not returning anything to avoid confusion). Same for particle. Do you agree?

I didn't know there were implementations for __getitem__ and __setitem__ for vec3d. Did you add them? I can't find them...

dtamayo commented 1 year ago

Great! Truly bizarre...I didn't write getitem or setitem, I'll remove them.

I think that makes sense for in place vs copies. It's the only sensible way to make them all consistent, and it's nice that __mul__ gives you a way to get a copy if you wan it.

Thank you!!

hannorein commented 1 year ago

Well, I wrote getitem and setitem, but I didn't rewrite them. (Don't remove them)

dtamayo commented 1 year ago

OK I think this is pretty good, please take a look!

I think you'll like the changes. I moved spherical_to_xyz and vice versa to tools.py. But once I did that, I realized that the user really doesn't need to know (and probably shouldn't want to know!) about Vec3d's at all, so I cut out all its functionality. Now we just have Rotation, and instead of having it act on a vec3d, we just do e.g. rot * [1,2,3], which I think is way nicer. Thanks for pushing me to simplify things!

I've also added a Rotations.ipynb and a test_rotations.py. Feel free to add/edit!

One thing I wasn't sure about for the documentation was whether Rotation.from_to(fromv, tov) is rotating counterclockwise around the fromv cross tov axis. I've written it assuming that is the case, but let me know if that's wrong!

hannorein commented 1 year ago

Amazing! Thank you. I'll go over it in one more time then merge it.. I'll check if the from_to documentation is correct...

hannorein commented 1 year ago

You've changed the rotations in _init_to_orbital (right side of 2.121, but it should be left side). I don't think it's consistent anymore. I think we want this function to give two identical particles in this test:

double Omega = 0.12;
double inc = 0.223;
double omega = 0.345;
struct reb_rotation r_orbit = reb_rotation_init_to_orbital(Omega, inc, omega);
struct reb_simulation* r = reb_create_simulation();
reb_add_fmt(r, "m", 1.);
reb_add_fmt(r, "a e", 1., 0.001); // orbit in the xy plane, periastron on x axis
reb_add_fmt(r, "a e Omega inc omega ", 1., 0.001, Omega, inc, omega); // 3d orbit
reb_particle_irotate(&r->particles[1], r_orbit);

I'll change it back (and update the notebook). Just letting you know in case I miss something.

dtamayo commented 1 year ago

Oops, you're totally right. Just like Rotation.to_new_axes maps [1,0,0] to newx, [0,1,0] to newy, and [0,0,1] to newz, Rotation.to_orbital should map [1,0,0] to pericenter and [0,0,1] to orbit normal. This is what you had before, and I got confused and flipped it around. This also means that the quaternion_to_orbital function in rotations.c should have the first line I added removed (where I added an inverse to make things match up). Thanks for catching that!

dtamayo commented 1 year ago

Wait...I think maybe I had it right.

Orbital is with x along peri, and z along orbit normal. So from_orbital_to_reference should take [1,0,0] along peri, and map it to the direction toward pericenter in the reference system, etc. Therefore from_reference_to_orbital should take the direction toward pericenter in the reference system and map it to [1,0,0], which is I think the change I made from how you had it before.

Am I thinking about that wrong?

dtamayo commented 1 year ago

I think what’s confusing about all of this is that the useful transformations we want for most use cases are inverse transformations (where the physical vector is fixed and the axes rotate). I like your from , to convention so that you can always check that it’s doing the right thing.

I’d also be happy flipping it and calling it from_orbital_to_reference if you think that’s easier to parse (since then we’re always starting with simple axes vectors like [1,0,0] and mapping it to something complicated.

If so, maybe we should rename them from_reference_to_new_axes and from_orbital_to_reference_axes?

On Tue, Jan 3, 2023 at 8:44 AM Hanno Rein @.***> wrote:

You've changed the rotations in _init_to_orbital (right side of 2.121, but it should be left side). I don't think it's consistent anymore. I think we want this function to give two identical particles in this test:

double Omega = 0.12;double inc = 0.223;double omega = 0.345;struct reb_rotation r_orbit = reb_rotation_init_to_orbital(Omega, inc, omega);struct reb_simulation* r = reb_create_simulation();reb_add_fmt(r, "m", 1.);reb_add_fmt(r, "a e", 1., 0.001); // orbit in the xy plane, periastron on x axisreb_add_fmt(r, "a e Omega inc omega ", 1., 0.001, Omega, inc, omega); // 3d orbitreb_particle_irotate(&r->particles[1], r_orbit);

I'll change it back. Just letting you know in case I miss something.

— Reply to this email directly, view it on GitHub https://github.com/hannorein/rebound/pull/638#issuecomment-1369988936, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA2ABFZX4WCLTAWRPP2TUGTWQRJN5ANCNFSM6AAAAAATNEV4IA . You are receiving this because you authored the thread.Message ID: @.***>

hannorein commented 1 year ago

It's just a naming thing again. I've just updated the examples and I think it should be all consistent now (except the reb_rotation_to_orbital function).

I think q*v should move the vector v=[1,0,0] to the orbital plane.

dtamayo commented 1 year ago

I agree. In my mind, I think from_reference_to_new_axes and from_orbital_to_reference_axes would then be the most consistent names. Otherwise I think to_orbital and to_new_axes are doing opposite things

hannorein commented 1 year ago

Yes. You were thinking of q*sim moving to a frame where the orbit normal is [0,0,1]. Not wrong, just different.

hannorein commented 1 year ago

Sorry. I take the yes back ;-). I think the naming is consistent now.

dtamayo commented 1 year ago

Yeah...clearly rotating a vector in the same axes or keeping a vector fixed and rotating the axes are inverses of one another...just for the spatially inept like me, I like choosing the names in a way that reflects what people are usually trying to do. At least with the complicated orbital and new constructors, I think we're always trying to keep the same physical vectors / simulation, and rotate the coordinate axes

hannorein commented 1 year ago

Let me think for a minute... I think we cannot change from: vector to:vector because that is the default in many other applications.

dtamayo commented 1 year ago

I think of from_to as consistent with from_reference_to_new_axes and from_orbital_to_reference_axes

It's taking the from vector in the original coordinate system, and rotating to new axes where it lies along tov

hannorein commented 1 year ago

I'm fine with to_new_axis. I'm still struggling with the orbital function. I understand what you say, I just think it's still really confusing. Can we use a compromise and use the current implementation on my branch with the name reb_rotation_init_orbit()? Without to or from. Then one just needs to look up the documentation to figure out which direction it goes.