Adding QUADCOIL integration to DESC as an objective#2110
Conversation
…also coming soon.
… optimizable_io.py and objective_funs.py to avoid error for static, empty tuple attributes and _objectives with self.coordinates == \'\'
…nversion, but it's not working because quadcoilproxy does not directly store the input dictionary, and desc static dict cannot contain strings (?)
…: Colorbar layout of new layout engine not compatible with old engine, and a colorbar has been created. Engine not changed.
…for merging into main
…for merging into main
| "_use_jit", | ||
| ] | ||
| _static_attrs = [ | ||
| "_static_attrs", # A bug as of Oct 10 2025 |
There was a problem hiding this comment.
specifically, for some reason one needs static_attrs included in the static_attrs or one will get JAX errors. This seems unintuitive but is empirically what we've found
There was a problem hiding this comment.
Should I keep this? I don't need to do anything about this right?
There was a problem hiding this comment.
_static_attrs is already included couple lines below. You probably had to add it before we added in master.
| notebooks/tutorials/coil_optimization_QUADCOIL.ipynb | ||
| notebooks/tutorials/omnigenity.ipynb | ||
| notebooks/tutorials/bootstrap_current.ipynb | ||
| notebooks/tutorials/coil_stage_two_optimization.ipynb |
There was a problem hiding this comment.
Also need to add QUADCOIL stuff to the API, I think the file is https://github.com/PlasmaControl/DESC/blob/master/docs/api_objectives.rst?plain=1
There was a problem hiding this comment.
add the objectives, I mean
| targets for each objective terms individually besides using ``target`` | ||
| and ``bounds`` that comes with other DESC objectives. | ||
| Targets of each property. 0 by default. | ||
| metric_weight : scalar or ndarray, default=None |
There was a problem hiding this comment.
Should be fixed now.
| return Bnormal | ||
|
|
||
|
|
||
| # Flipping the current potential of a quadcoil |
There was a problem hiding this comment.
what are the different coordinate directions/representations/normalizations used? it woudl be good to list these all somewhere, even if just as a comment or in this PR, of the differences in convention btwn DESC and QUADCOIL. Just so we have it clearly down somewhere. Seems phi is flipped?
| return phi_swapped | ||
|
|
||
|
|
||
| def _quadcoil_phi_to_desc_phi(phi_mn_quadcoil, stellsym, mpol, ntor): |
There was a problem hiding this comment.
this is converting just phi_mn quadcoil to desc?
| return Phi_mn, modes_M, modes_N | ||
|
|
||
|
|
||
| def _create_source(eq, source_grid, eval_grid): |
There was a problem hiding this comment.
this could be placed perhaps into the integrals/singularities.py or in a util file there. Also could be used again inside of the other places like BoundaryError. I think it is a reasonable util function, with perhaps some additional documentation attached to it
| return (source_profiles, source_transforms, interpolator) | ||
|
|
||
|
|
||
| def _quadcoil_kwargs_to_field_kwargs( # noqa: C901 |
There was a problem hiding this comment.
would be nice to document what is being done here exactly?
| plasma_dofs = jnp.concatenate([rc, rs, zc, zs]) | ||
| return plasma_dofs | ||
|
|
||
| def compute_surface_current_field(self, *all_params): |
There was a problem hiding this comment.
instead of compute_surface_current_field maybe something more like run_regcoil or solve_quadcoil_surface_current_field or something is better?
There was a problem hiding this comment.
I renamed this to solve_quadcoil_surface_current_field, I also renamed compute_full to solve_quadcoil.
…ield to solve_quadcoil_surface_current_field
…maControl/DESC into ffu/quadcoil-qss-pre-commit
lankef
left a comment
There was a problem hiding this comment.
Addressed the comments
…coil_surface) and fixing the tutorial with refactorings suggested during the PR.
…maControl/DESC into ffu/quadcoil-qss-pre-commit
|
qp_nescoil.winding_surface.plot() requires simsopt. Please replace with DESC surface plotter. |
YigitElma
left a comment
There was a problem hiding this comment.
Thanks fore adding this!
I will review rest of the code later, but it would be nice if you can resolve the problem with CI tests first. It looks like CI and pip in general cannot install quadcoil, I couldn't install it locally either.
| "_use_jit", | ||
| ] | ||
| _static_attrs = [ | ||
| "_static_attrs", # A bug as of Oct 10 2025 |
There was a problem hiding this comment.
_static_attrs is already included couple lines below. You probably had to add it before we added in master.
| if hasattr(self, "_constants") and ("quad_weights" not in self._constants): | ||
| grid = self._constants["transforms"]["grid"] | ||
| if self._coordinates == "rtz": | ||
| grid = self._constants["transforms"]["grid"] |
There was a problem hiding this comment.
can you revert these changes?
| else: | ||
| im = ax.contourf(xx, yy, data, **contourf_kwargs) | ||
| cax = divider.append_axes("right", **cax_kwargs) | ||
| _set_tight_layout(fig) |
There was a problem hiding this comment.
This change is also unnecessary
| def _unmake_hashable(x): | ||
| # turn tuple of ints and shape to ndarray | ||
| if isinstance(x, tuple) and x[0] == "ndarray": | ||
| if isinstance(x, tuple) and len(x) and x[0] == "ndarray": |
There was a problem hiding this comment.
I don't think it hurts to have it, but why did you need this change?
There was a problem hiding this comment.
The default values for some QUADCOIL arguments are empty list/tuples (I had to do it because the adjoint derivative is wrapped in a vjp rule before handed to DESC and this way some flows are simpler). Without this check it breaks DESC Optimizable.
| qicna @ git+https://github.com/rogeriojorge/pyQIC/ | ||
| qsc <= 0.1.3 | ||
| shapely >= 1.8.2, <= 2.1.2 | ||
| quadcoil >= 0.1.0 |
There was a problem hiding this comment.
Sorry if I am missing but is quadcoil a pip package? I coulnd't find it on PyPI and our CI cannot install it and the tests don't run because of that.
There was a problem hiding this comment.
It wasn't but now it is!
| @@ -0,0 +1,916 @@ | |||
| import warnings | |||
|
|
|||
| from jax import jit | |||
There was a problem hiding this comment.
| from jax import jit | |
| from desc.backend import jit |
| metric_name, | ||
| value_only, | ||
| verbose, | ||
| plasma_M_theta : int, optional, default=eq.M_grid |
There was a problem hiding this comment.
We usually give defaults at the end of the description
| bs_chunk_size : int, optional, default=None | ||
| Size to split Biot-Savart computation into chunks of evaluation points. | ||
|
|
||
| Attributes |
There was a problem hiding this comment.
These don't seem necessary
| # we set them here. This is necessary because we are calling quadcoil | ||
| # through quadcoil.io.quadcoil_for_diff, which cannot see their default | ||
| # values in quadcoil.quadcoil. | ||
| if "net_toroidal_current_amperes" in quadcoil_kwargs.keys(): |
There was a problem hiding this comment.
You can replace all if/else statements by something = dict.pop("something", default)
| if not plasma_N_phi: | ||
| plasma_N_phi = eq.N_grid | ||
| elif plasma_N_phi <= eq.N_grid: | ||
| warnings.warn( |
| self._quadcoil_for_diff = jit(_quadcoil_for_diff) | ||
| self._quadcoil_values = jit(_quadcoil_values) | ||
| # ----- Setting and registering keyword arguments ----- | ||
| self._static_attrs = _Objective._static_attrs + [ |
There was a problem hiding this comment.
You can move this to line 165, right after docs. We usually define it there
| "_field_fixed", | ||
| "_bs_chunk_size", | ||
| # Basics | ||
| "_static_attrs", |
There was a problem hiding this comment.
_Objective._static_attrs have the _static_attrs and most of these other keys in it. You can remove them safely
| from functools import partial | ||
|
|
||
| import numpy as np | ||
| from jax import jit |
There was a problem hiding this comment.
| from jax import jit | |
| from desc.backend import jit |
| """Computes the scalar value of the QUADCOIL proxy. | ||
|
|
||
| Computes the scalar value of the QUADCOIL proxy. A wrapper for | ||
| ``compute_full``. |
There was a problem hiding this comment.
Why do you have a wrapper here? And what is compute_full? You actually pass full_mode=False.
Co-authored-by: Yigit Gunsur Elmacioglu <102380275+YigitElma@users.noreply.github.com>
…maControl/DESC into ffu/quadcoil-qss-pre-commit
…dict popping with defaults using pop(default=**) to improve readability
|
@lankef https://github.com/PlasmaControl/DESC/actions/runs/25258105132/job/74060745121?pr=2110 this error happens if you commit a notebook from cluster or after running notebook in Jupyter Lab session (tbh it is very annoying and we have a long standing issue about this #1215, but it is actually not our problem but |
This pull request adds two files under /objective,
_quadcoil.pyand_quadcoil_utils.py. These allows one to call QUADCOIL using a DESC equilibrium and filament coil set as input, and perform quasi-single-stage optimization on the equilibrium and/or the coilset. There are some additions in coils and curves for conversion from simsopt objects. (since quadcoil is written in the simsopt convention). Winding surface can be either fixed or auto-generated, but optimizing the geometry of the winding surface is not yet implemented. The unit test compares quadcoil to desc's built-in REGCOIL.