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

Single-stage optimization with spec: CoilSet, ReducedCoilSet and CoilNormalField (PR 2 of 2) #438

Merged
merged 22 commits into from
Oct 21, 2024

Conversation

smiet
Copy link
Contributor

@smiet smiet commented Jul 22, 2024

This PR adds new features, and enables the optimizations presented at the Simons' retreat and meeting, and available in preprint (https://arxiv.org/abs/2407.02097).

The main additions are:

class CoilSet

A helper class to handle a set of coil objects as a single object:

  • convenience class
  • provides helper methods for all the coil-related optimization targets
  • serves as the base class for ReducedCoilSet
  • allows the ReducedCoilSet and CoilNormalField to be implemented with minimal code duplication.

class ReducedCoilSet

Identical in use to CoilSet, but it's degrees-of-freedom are linear combinations of the coil degrees-of-freedom.

  • Calculates its degrees-of-freedom by performing a singular-value-decomposition on an arbitrary user-provided mapping.
  • Ability to re-calculate the reduced basis when changing the function or other relevant properties of the CoilSet.
  • Removes some overdetermination inherent in direct-from-coil optimization.
  • Reduces the dimensionality of the optimization problem, speeds up optimization.

class CoilNormalField

Identical in use to a NormalField and interfaces with SPEC, but has degrees-of-freedom specified by a CoilSet or ReducedCoilSet.

  • Provides the normal field on a boundary in the SPEC conventions.
  • Uses a cache to avoid redundant Fourier transformations.
  • Handles both CoilSet and ReducedCoilSet, provides method to reduce the CoilSet using the mapping to SPEC input parameters (normal field Fourier components).

With these changes the functionality I describe in my preprint will be added to the simsopt master branch. I look forward to your suggestions to improve my code.

Copy link

codecov bot commented Jul 23, 2024

Codecov Report

Attention: Patch coverage is 90.15152% with 52 lines in your changes missing coverage. Please review.

Project coverage is 92.34%. Comparing base (c469350) to head (98b8661).
Report is 23 commits behind head on master.

Files with missing lines Patch % Lines
src/simsopt/field/coilset.py 85.25% 50 Missing ⚠️
src/simsopt/field/normal_field.py 99.30% 1 Missing ⚠️
src/simsopt/geo/surfacerzfourier.py 97.29% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #438      +/-   ##
==========================================
- Coverage   92.68%   92.34%   -0.34%     
==========================================
  Files          77       78       +1     
  Lines       13982    14503     +521     
==========================================
+ Hits        12959    13393     +434     
- Misses       1023     1110      +87     
Flag Coverage Δ
unittests 92.34% <90.15%> (-0.34%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@rogeriojorge rogeriojorge left a comment

Choose a reason for hiding this comment

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

This looks really good. I am just not sure if the "copy" method is actually needed.

@@ -421,6 +421,75 @@ def from_pyQSC(cls, stel: Qsc, r: float = 0.1, ntheta=20, mpol=10, ntor=20, **kw

surf.local_full_x = surf.get_dofs()
return surf

def copy(self, **kwargs):
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the difference between this function and simply setting the dofs of a new surface object as, for example

surf = SurfaceRZFourier.from_vmec_input(filename, range="half period", nphi=nphi, ntheta=ntheta) surf_big = SurfaceRZFourier(dofs=surf.dofs,nfp=nfp, mpol=surf.mpol,ntor=surf.ntor, quadpoints_phi=new_quadpoints_phi,quadpoints_theta=new_quadpoints_theta)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The functionality is identical; except that one needs to know how the surface was instantiated in order to produce a copy with different quadpoints, mpol, ntor, or another quantity.

My CoilSet and NormalField need to a Surface of type SurfaceRZFourier with quadpoints of a specificrange, but a general Surface object does not carry with it the information of from which filename it was instantiated, or other information to generate a new surface in the way you demonstrate above. The .copy method allows me to generate a copy that is otherwise identical to the surface it copies, except for the kwargs you have specified.

@@ -1,9 +1,8 @@
#!/usr/bin/env python

import unittest
import os
Copy link
Contributor

Choose a reason for hiding this comment

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

Did os fail in same particular case? Is it why it was removed?

Copy link
Contributor Author

@smiet smiet Aug 12, 2024

Choose a reason for hiding this comment

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

most tests use monty.ScratchDir but the test_mgrid used tempfile and os.chdir, without but did not contain a chdir out of the temporary directory.

This led my subsequent tests to fail mysteriously as the temporary directory that the test-kernel had moved into was deleted in when the test finished.

Reverted this test to the more readable and less error-prone moty.ScratchDir, that uses the with context and therefore os import was also no longer needed to effect the chdir, so I removed it.

@@ -771,6 +771,24 @@ def test_fourier_transform_scalar(self):
# Check that the result is the same as the original field:
np.testing.assert_allclose(field/2*np.pi**2, field2)

def test_copy_method(self):
Copy link
Contributor

Choose a reason for hiding this comment

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

Some lines of copy are not being tested (see codecov)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should be in the latest push, with a logic fix!

@@ -705,7 +774,7 @@ def fourier_transform_scalar(self, scalar, mpol=None, ntor=None, normalization=N
if not stellsym:
A_mnc[0, ntor] = np.sum(scalar) / (ntheta_grid * nphi_grid)
if normalization is not None:
if isinstance(normalization, float):
if not isinstance(normalization, float):
Copy link
Contributor

Choose a reason for hiding this comment

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

Good catch!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd be much happier if the mistake weren't my own ;)

@smiet
Copy link
Contributor Author

smiet commented Oct 17, 2024

Sorry to push this, but could we move forward with this PR? All tests are now pasing (after the numpy2.0 speedbump), and the tests now cover all new code.

The only exception is a plotting utility function that I am not testing because not all runners have visualization capabilities, but I do think it's inclusion is valuable as a demonstration.

@landreman, @rogeriojorge @abaillod

@rogeriojorge
Copy link
Contributor

Looks good to me. However, I'll let a day or two go by before accepting in case @landreman or @abaillod wants to add something here.

@rogeriojorge
Copy link
Contributor

There's a conflict with the recent merge. Could you fix it @smiet? I'll merge in afterwords.

@rogeriojorge rogeriojorge self-requested a review October 21, 2024 14:16
@rogeriojorge rogeriojorge merged commit c38e20c into master Oct 21, 2024
43 of 47 checks passed
@rogeriojorge
Copy link
Contributor

Merged

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.

2 participants