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

Set preferred normal direction for conversion from points to FourierPlanarCurve #1463

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

ryanwu4
Copy link

@ryanwu4 ryanwu4 commented Dec 12, 2024

Previously, the from_values() method in FourierPlanarCurve would convert the coords provided to an r(s) representation with s sorted to be monotonically increasing (counterclockwise). However, this means that if the original coordinates were parameterized "backwards" (clockwise), the new FourierPlanarCurve would return coordinates in a reversed order, which is problematic if you are trying to run something like a Biot Savart integral where the coordinate order is the implicit current direction in the loop. This PR fixes the issue by flipping the normal vector of the FourierPlanarCurve when the provided coordinates are in the clockwise direction.

@ryanwu4 ryanwu4 changed the title Set preferred coordinate direction for conversion from points to FourierPlanarCurve Set preferred normal direction for conversion from points to FourierPlanarCurve Dec 12, 2024
Copy link

codecov bot commented Dec 12, 2024

Codecov Report

Attention: Patch coverage is 61.53846% with 5 lines in your changes missing coverage. Please review.

Project coverage is 95.58%. Comparing base (44bac68) to head (8fd19b2).
Report is 3067 commits behind head on master.

Files with missing lines Patch % Lines
desc/geometry/curve.py 61.53% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1463      +/-   ##
==========================================
+ Coverage   95.31%   95.58%   +0.26%     
==========================================
  Files          87       98      +11     
  Lines       22262    25165    +2903     
==========================================
+ Hits        21220    24053    +2833     
- Misses       1042     1112      +70     
Files with missing lines Coverage Δ
desc/geometry/curve.py 95.66% <61.53%> (-0.34%) ⬇️

... and 77 files with indirect coverage changes

@ddudt ddudt self-requested a review December 12, 2024 21:16
Comment on lines 838 to 839
rotmat = rotation_matrix(axis, angle)
coords_rotated = coords_centered @ rotmat # rotate to X-Y plane
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need the additional rotation and not just flipping the normal vector direction? I think we only need to flip the normal vector, since that would effectively reverse the parameterization in real space

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, I think we could flip the sign of the Fourier coefficients r_n where $m &lt; 0$, since $\cos(-\theta)=\cos(\theta)$ and $\sin(-\theta)=-\sin(\theta)$

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The weird thing is that the curve points test passes with just flipping the sign of the normal, but the fields are different between spline and normal-flipped-FourierPlanar unless I redo the rotation matrix with the flipped normal. I'll look into this a bit more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I looked at the coil shapes with and without re-rotating, and it appears that if you only flip the sign of the normal without rotating as well, you mirror the curve as well -- this is because you swap the chirality of the curve. If you drew the curve on a piece of paper, flipping the normal would be looking at the sheet of paper from the back, rather than flipping the piece of paper itself.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you try flipping the sign of the r_n coefficients? (Only the $\sin$ terms I think.) I think that would be a cleaner solution than flipping the normal + rotation, if it works.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason this doesn't work, no matter what it seems to need both an axis reversal and a rotation (pancake flip) to get the curves to line up again -- I clarified in the code what the rotation is now doing, but I couldn't find the best way to do it without another rotation.

desc/geometry/curve.py Show resolved Hide resolved
desc/geometry/curve.py Outdated Show resolved Hide resolved
tests/test_curves.py Outdated Show resolved Hide resolved
@ryanwu4
Copy link
Author

ryanwu4 commented Dec 13, 2024

In the process of working on this , I noticed the following behavior:
If the normal vector of a FourierPlanarCurve is defined as [0,0,-1], the curve direction will be incorrect (clockwise about the normal instead of counterclockwise). This is due to the axis term in the corresponding x compute function being undefined for axes parallel to the Z axis, so the rotation matrix defaults to the unit diagonal matrix. I added this as an assertion check, though conceivably this can probably be fixed.

The problematic code in the compute function:

    Zaxis = jnp.array([0.0, 0.0, 1.0])  # 2D curve in X-Y plane has normal = +Z axis
    axis = cross(Zaxis, normal)
    angle = jnp.arccos(dot(Zaxis, safenormalize(normal)))
    A = rotation_matrix(axis=axis, angle=angle)

Minimal Example:

coil = FourierPlanarCoil(
            current=1e6,
            center=[0, 0, 0],
            normal=[0, 0, 1],
            r_n=2,
            modes=[0],
        )

coil2 = FourierPlanarCoil(
            current=1e6,
            center=[0, 0, 0],
            normal=[0, 0, -1],
            r_n=2,
            modes=[0],
        )

field1 = coil.compute_magnetic_field([0, 0, 0], source_grid=LinearGrid(N=100), basis = "xyz")
field2 = coil2.compute_magnetic_field([0, 0, 0], source_grid=LinearGrid(N=100), basis = "xyz")

np.testing.assert_allclose(field1, field2, rtol=1e-3, atol=1e-3) # these fields shouldn't be equal but they are

The expected behavior of the assertion should be to fail, as field1 and field2 should have equal and opposite magnitudes. Instead, they are actually the same number, and the assertion succeeds. Compare this to if you perturb the normal vectors just a tiny bit (eg. [0, 0.1, +-1]), and the field correctly has opposite signs.

@dpanici
Copy link
Collaborator

dpanici commented Dec 18, 2024

In the process of working on this , I noticed the following behavior: If the normal vector of a FourierPlanarCurve is defined as [0,0,-1], the curve direction will be incorrect (clockwise about the normal instead of counterclockwise). This is due to the axis term in the corresponding x compute function being undefined for axes parallel to the Z axis, so the rotation matrix defaults to the unit diagonal matrix. I added this as an assertion check, though conceivably this can probably be fixed.

The problematic code in the compute function:

    Zaxis = jnp.array([0.0, 0.0, 1.0])  # 2D curve in X-Y plane has normal = +Z axis
    axis = cross(Zaxis, normal)
    angle = jnp.arccos(dot(Zaxis, safenormalize(normal)))
    A = rotation_matrix(axis=axis, angle=angle)

Minimal Example:

coil = FourierPlanarCoil(
            current=1e6,
            center=[0, 0, 0],
            normal=[0, 0, 1],
            r_n=2,
            modes=[0],
        )

coil2 = FourierPlanarCoil(
            current=1e6,
            center=[0, 0, 0],
            normal=[0, 0, -1],
            r_n=2,
            modes=[0],
        )

field1 = coil.compute_magnetic_field([0, 0, 0], source_grid=LinearGrid(N=100), basis = "xyz")
field2 = coil2.compute_magnetic_field([0, 0, 0], source_grid=LinearGrid(N=100), basis = "xyz")

np.testing.assert_allclose(field1, field2, rtol=1e-3, atol=1e-3) # these fields shouldn't be equal but they are

The expected behavior of the assertion should be to fail, as field1 and field2 should have equal and opposite magnitudes. Instead, they are actually the same number, and the assertion succeeds. Compare this to if you perturb the normal vectors just a tiny bit (eg. [0, 0.1, +-1]), and the field correctly has opposite signs.

related to #1456

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.

3 participants