-
Notifications
You must be signed in to change notification settings - Fork 77
Cbs/fieldine integrator #555
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
base: master
Are you sure you want to change the base?
Conversation
leading zero copied over from VMEC convention, causing all coefficients off-by-one. See issue #554
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #555 +/- ##
==========================================
- Coverage 92.56% 92.45% -0.12%
==========================================
Files 83 83
Lines 16229 16798 +569
==========================================
+ Hits 15023 15530 +507
- Misses 1206 1268 +62
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
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.
Thanks Chris!
There are a few lines in code coverage that are missing, we should have unit tests for these.
By the way, we still cover plotting code in unit tests, but the canvas is just not plotted by skipping the mlab.show() command, like we do here
simsopt/src/simsopt/geo/surface.py
Line 266 in d053f90
| mlab.show() |
BoozerMagneticField is not in cartesian coordinates, and the Integrator subclasses are not set up to handle it.
|
@andrewgiuliani Thank you for your review, I have added tests to cover all the missed lines that I could. The ones that are not hit are:
I hope that we can accept the few misses, the diff is only a few percent below the current coverage. There are some other failing tests because SPEC install on pytnon 3.9 is currently broken. @landreman @mishapadidar, could I ask you for a review if you have a bit of time to spare? A few people already using it have told me this is very pleasant to work with, and I would like to make these features available in the not-too-distant-future. I know that there are many more features to add, but I will deal with these in later PRs, otherwise this one will bloat even larger than it currently is. Future plans include:
|
| and being an Optimizable, the results of integrations can be | ||
| optimized. | ||
| """ | ||
| def __init__(self, field: MagneticField, comm=None, nfp=None, stellsym=False, R0=None, test_symmetries=True): |
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 we remove test_symmetries?
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.
As in completely remove the functionality?
We can, but I thought it prudent to have a few basic checks before we create an object that a user will blindly trust. It is too easy to pass the wrong NFP or assume something is stellarator-symmetric.
These properties are relied upon by PoincarePlotter, and the test is also super fast compared to the time it takes to integrate.
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 would like to keep this functionality until in a next PR we can move it to the MagneticField class (which should carry information on NFP, symmetries, and a method to test them).
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.
The Integrator class is proving two general benefits: it makes integrators Optimizable and it provides convenience methods for unpacking the res_phi_hits data. Not all of the instances of the class adhere to this framework, so I suggest we make an attempt to align the usage. In particular, currently Simsopt traces fieldlines through the compute_fieldlines function which calls the sopp integrators. The development of the ScipyFieldlineIntegrator class suggests that there may be desire for using scipy integrators in place of the sopp integrators. To align with the current simsopt style, we should implement a function similar to sopp.fieldline_tracing but for the scipy integrators. Then we modify the compute_fieldlines function to accept a method=... argument. Finally, the Integrator class can take a method=... argument as well. This would allow us to eliminate the instances of the FieldlineIntegrator that are specific to integration methods, i.e. the ScipyFieldLineInegrator, sliming the code down to have a single FieldLineIntegrator class. In this framework, other instances of the Integrator class would solve other ODES. For example, a GuidingCenterIntegrator class.
A second point is that, since we are setting up field line integration as an Optimizable, we really should be implementing derivatives. Differentiating the field line integration is tractable and greatly enhances the usability and applicability of this class.
I have added a lot of comments, and I have not finished reviewing the PoincarePlotter.
Here are some overarching themes to the comments.
- Add clean doc strings to all functions with a succinct description of the method, arguments and returns.
- The integrator classes have some convenience functions which lead to duplication. Consider removing the duplication.
src/simsopt/field/tracing.py
Outdated
| if not periodicity_test: | ||
| raise ValueError(f"The field does not adhere to the {self.nfp} field periods it is set to be.") | ||
|
|
||
| def Interpolate_field(self, rrange, phirange, zrange, degree=2, skip=None, extrapolate=True): |
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.
The main purpose of this function is to build an interpolatedfield from a field. Since the method only depends on the field and not the inegrator, this convenience function would be most suited to the MagneticField class rather than the Integrator class.
Removing this method simplifies the Integrator class and should not effect user experience much (they could initialize the Integrator from the interpolatedField easily).
Consider removing this method.
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 remove this classmethod and will in the future make InterpolatedField self-updating.
| def generate_symmetry_planes(phis, nfp=1): | ||
| """ | ||
| Given a list of phis in [0, 2pi/nfp], generate the full list of phis | ||
| in [0, 2pi] by adding the symmetry planes. |
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.
add args
| return self._randomcolors | ||
|
|
||
| @staticmethod | ||
| def generate_phis(nplanes, nfp=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.
docstring
| return self._res_tys | ||
|
|
||
| @property | ||
| def res_phi_hits(self): |
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.
docstring
| """ | ||
| Generate a hash from dofs, self.phis, self.start_points_RZ, and self.n_transits | ||
| """ | ||
| hash_list = self.integrator.field.full_x.tolist() + self.phis.tolist() + self.start_points_RZ.flatten().tolist() + [self.n_transits] |
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.
use the uuid class or similar
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.
That would cause recompute if a second plotter with an identical field and startpoints, wanted to plot the same. Or in the next session or different kernel. This way, if a file has been generated, and has the same field with dofs, it will not recompute. Except if pythons hash function changes (it had recently), but that is too much of an edge case.
src/simsopt/field/tracing.py
Outdated
| poincare_hash = hash(tuple(hash_list)) | ||
| return poincare_hash | ||
|
|
||
| def save_to_disk(self, filename=None, name=None): |
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 seems that the functionality here is to initialize a poincare plotter from data. Rather than save the class, which potentially conflicts with Simsopt save/load structure, we could write a from_phi_hits method than initializes the class from the necessary data.
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 can call it differently, this is indeed confusing. It is only to store/retrieve poincare data to/from a file, not the entire object.
|
Thanks @mishapadidar for your points so far. You raise a few points for discussion: Implement
|
|
@andrewgiuliani @mishapadidar @landreman Think we're good to go! lmk if more changes are needed. |
Revamp of fieldline integration. Some tests still need to be written, but this should simplify the user experience in generating Poincare plots.
What’s new
Integratorbase classwith subclasses:
soppintegration routinesscipy.solve_ivpmethods).These subclasses provide methods to return Poincare sections, three dimensional field line trajectories, as well as the ability to integrate from any starting point over a given toroidal distance.
The$R$ and $Z$ using $\phi$ as the 'time' variable in the ODE, providing crisper Poincare sections and integrates about as fast as the
ScipyFieldlineIntegratorprovides methods to integratesoppmethods (which evolves the three coordinates independently).The Integrator subclasses also provide an interface to the
PoincarePlotterclass, which provides an easy interface to create Poincare plots of the field.The PoincarePlotter uses caching and the simsopt dependency graph (parent
Integratorwith parentMagneticField) to trigger recomputes when necessary.Manipulation of the
res_phi_hitsandres_tysarrays is a thing of the past!give it a spin!: