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

Coil forces #450

Merged
merged 84 commits into from
Oct 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
c656e63
minimal example for force metric
phuslage Jun 14, 2023
2269982
classes for self field, forces and force optimization
phuslage Jun 16, 2023
9ac48c8
fix typos
phuslage Jun 16, 2023
bd63087
rm self_field_forces
phuslage Jun 27, 2023
86c6106
Fixes
phuslage Jun 29, 2023
de8dd1b
First commmit on unit tests for coil forces
phuslage Aug 10, 2023
39db990
Add functions for comparison with Julia implementation; Fixes
phuslage Aug 18, 2023
73b8d0a
fits benchmarks now
phuslage Aug 30, 2023
c23932e
force for rectangluar xsection now correct
phuslage Sep 5, 2023
f82c05a
Split up work into individual files
phuslage Sep 19, 2023
cff04ee
remove unnecessary file
phuslage Sep 19, 2023
aa80580
recent work
phuslage Sep 25, 2023
ef3419e
Tidying self-field functions
landreman Sep 29, 2023
5e5d10a
Merge branch 'master' into ml/coil_forces
landreman Sep 29, 2023
02c600b
Another test working for self-forces
landreman Sep 29, 2023
0945965
Merge branch 'master' into coil_forces
landreman Sep 29, 2023
8e3d2e2
hsx self-force tests now pass. Also sped up integral
landreman Sep 29, 2023
26a67c5
For some reason the coil_forces.py example wasn't merged in
landreman Sep 29, 2023
02c678c
Merge branch 'coil_forces' into ml/coil_forces_merge
landreman Sep 29, 2023
5ac31ea
Fixed NANs in derivative of self-force
landreman Sep 30, 2023
d17cfaf
Self-force Taylor test now works
landreman Sep 30, 2023
a24dd43
Merge pull request #356 from hiddenSymmetries/ml/coil_forces
landreman Oct 9, 2023
54e756c
Merge pull request #364 from hiddenSymmetries/master
landreman Oct 13, 2023
1b85d0f
field alignment with analyical model for critical current and FramedC…
phuslage Oct 18, 2023
6892915
field alignment optimization
phuslage Nov 10, 2023
a8728ca
Test 2
Nov 13, 2023
2d8420a
First commit. The file force_2 is not working at the moment. J is cur…
Nov 23, 2023
b095148
Got the mutual forces working with dJ.
Nov 24, 2023
27ed8eb
The Optimizable "MeanSquaredForceOpt" is now coded up and running wit…
Dec 5, 2023
bac2d19
One bug fixed in force.py, though it currently needs to be updated in…
smhurwitz Dec 6, 2023
e0043e3
One bug fixed in force.py, though it currently needs to be updated in…
smhurwitz Dec 6, 2023
891b966
Fixed the gradient of the self-force. Now I need to fix the mathematics.
smhurwitz Dec 12, 2023
7750e54
Fixed a minus sign in force.py
smhurwitz Dec 13, 2023
b32a798
Updated force.py to reflect the sums necessary. The Taylor test is st…
smhurwitz Dec 13, 2023
5744276
MeanSquaredForce passing Taylor test for >1 coil
smhurwitz Dec 26, 2023
637db28
lpcurveforce passing taylor tests
smhurwitz Jan 9, 2024
850a5bd
set up detailed 2nd stage optimization, still gotta see if it fully w…
smhurwitz Jan 9, 2024
124b66d
example: basic no-force optimization
smhurwitz Jan 10, 2024
1321b72
example: basic no-force optimization (made plots + output cleaner)
smhurwitz Jan 10, 2024
9d7d566
fixed optimization example by using Weight object and adding __imul__…
smhurwitz Jan 10, 2024
f9240c2
determining weights: 1/11/24
smhurwitz Jan 11, 2024
55d8c2e
removed uneccesary files
smhurwitz Jan 12, 2024
4cc8be5
restored accidental deletion
smhurwitz Jan 12, 2024
0ebc286
cleaned up code for pull request to coil_forces
smhurwitz Jan 12, 2024
77fc492
added capabilities for plotting force in VTK formatting
smhurwitz Feb 23, 2024
c85a9ef
Merge branch 'coil_forces' into coil_forces_Hurwitz
smhurwitz Feb 26, 2024
46dee4e
addressed PR feedback
smhurwitz Feb 26, 2024
0b6588e
fixed linting
smhurwitz Feb 26, 2024
e0d3030
Fixed os.getcwd() error in get_*_data due to temp directory being del…
landreman Feb 29, 2024
b9adeed
coil_forces example: make surface half period, add to run_serial_exam…
landreman Feb 29, 2024
34bd8b4
linting
landreman Feb 29, 2024
23a441b
save
smhurwitz Mar 1, 2024
f581411
Merge branch 'coil_forces_Hurwitz' of https://github.com/hiddenSymmet…
smhurwitz Mar 1, 2024
468b463
fixed unit tests for when “currents[0].fix_all()” is removed
smhurwitz Mar 1, 2024
8fe15d4
fixed LpCurveForce to have an objective in alignment with LpCurveCur…
smhurwitz Mar 4, 2024
234eeee
Merge pull request #387 from hiddenSymmetries/coil_forces_Hurwitz
landreman Mar 7, 2024
641b232
Tweak for compatibility with jax 0.4.25
smhurwitz Apr 19, 2024
9eded9c
vetorized `for` loop in selffield.py
smhurwitz Apr 20, 2024
422c13b
- Solved import path issues
abaillod Jul 17, 2024
8ff14f5
wip. refactored how objective is called in CriticalCurrentOpt.
abaillod Jul 17, 2024
0f269da
wip. added external field in critical current evaluation. For some re…
abaillod Jul 17, 2024
db8a890
wip. FramedCurve is not a Curve anymore, but has a Curve as an attribute
abaillod Jul 26, 2024
363ebd8
wip. Created a SelfField class
abaillod Jul 31, 2024
7ed80ea
wip'
abaillod Aug 1, 2024
fd5c88c
wip
abaillod Aug 14, 2024
e562652
wip. Fixed selffield derivatives. There are still issues with derivat…
abaillod Aug 14, 2024
ac18ec1
merge master
abaillod Aug 16, 2024
7788e5d
solved typos. Improve documentation
abaillod Aug 19, 2024
5d7c342
Derivative of field alignement w.r.t current is now fixed
abaillod Aug 20, 2024
5b103de
Merge branch 'master' of https://github.com/hiddenSymmetries/simsopt …
smhurwitz Sep 1, 2024
e7ef2ba
changed target function for field alignement
abaillod Sep 7, 2024
eae958d
Removed stuff related to critical current optimization - now in branc…
abaillod Sep 20, 2024
f71f5b7
Merge branch 'coil_forces' of github.com:hiddenSymmetries/simsopt int…
abaillod Sep 20, 2024
52bcec0
Removing some imports for depreciated/moved codes
Oct 3, 2024
81be17b
Merge pull request #449 from hiddenSymmetries/master
smhurwitz Oct 10, 2024
7555e22
linting changes
smhurwitz Oct 11, 2024
2a78979
linting changes v2
smhurwitz Oct 11, 2024
e1c6998
reverted framedcurve.py
smhurwitz Oct 11, 2024
99db29a
test_force_objectives is now working
landreman Oct 12, 2024
bfc3ddd
test_update_points is now working
landreman Oct 12, 2024
3e266ea
Add test coverage for Weight.__iadd__
landreman Oct 12, 2024
fefaedd
Update python versions in extensive CI
landreman Oct 12, 2024
092b52f
Remove example that belongs in a separate PR
landreman Oct 12, 2024
27f3959
Removed un-tested unused functions
landreman Oct 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/extensive_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
matrix:
test-type: [unit, integrated]
packages: [all, vmec, spec, none]
python-version: [3.8, 3.9, "3.10"]
python-version: [3.9, "3.10", "3.11"]
include:
- python-version: 3.9
test-type: unit
Expand Down
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
url = https://gitlab.com/libeigen/eigen.git
[submodule "thirdparty/fmt"]
path = thirdparty/fmt
url = https://github.com/fmtlib/fmt.git
url = https://github.com/fmtlib/fmt.git
207 changes: 207 additions & 0 deletions examples/3_Advanced/coil_forces.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
#!/usr/bin/env python

"""
Example script for the force metric in a stage-two coil optimization
"""
import os
from pathlib import Path
from scipy.optimize import minimize
import numpy as np
from simsopt.geo import curves_to_vtk, create_equally_spaced_curves
from simsopt.geo import SurfaceRZFourier
from simsopt.field import Current, coils_via_symmetries
from simsopt.objectives import SquaredFlux, Weight, QuadraticPenalty
from simsopt.geo import (CurveLength, CurveCurveDistance, CurveSurfaceDistance,
MeanSquaredCurvature, LpCurveCurvature)
from simsopt.field import BiotSavart
from simsopt.field.force import coil_force, LpCurveForce
from simsopt.field.selffield import regularization_circ
from simsopt.util import in_github_actions


###############################################################################
# INPUT PARAMETERS
###############################################################################

# Number of unique coil shapes, i.e. the number of coils per half field period:
# (Since the configuration has nfp = 2, multiply by 4 to get the total number of coils.)
ncoils = 4

# Major radius for the initial circular coils:
R0 = 1.0

# Minor radius for the initial circular coils:
R1 = 0.5

# Number of Fourier modes describing each Cartesian component of each coil:
order = 5

# Weight on the curve lengths in the objective function. We use the `Weight`
# class here to later easily adjust the scalar value and rerun the optimization
# without having to rebuild the objective.
LENGTH_WEIGHT = Weight(1e-03)
LENGTH_TARGET = 17.4

# Threshold and weight for the coil-to-coil distance penalty in the objective function:
CC_THRESHOLD = 0.1
CC_WEIGHT = 1000

# Threshold and weight for the coil-to-surface distance penalty in the objective function:
CS_THRESHOLD = 0.3
CS_WEIGHT = 10

# Threshold and weight for the curvature penalty in the objective function:
CURVATURE_THRESHOLD = 5.
CURVATURE_WEIGHT = 1e-6

# Threshold and weight for the mean squared curvature penalty in the objective function:
MSC_THRESHOLD = 5
MSC_WEIGHT = 1e-6

# Weight on the mean squared force penalty in the objective function
FORCE_WEIGHT = Weight(1e-26)

# Number of iterations to perform:
MAXITER = 50 if in_github_actions else 400

# File for the desired boundary magnetic surface:
TEST_DIR = (Path(__file__).parent / ".." / ".." / "tests" / "test_files").resolve()
filename = TEST_DIR / 'input.LandremanPaul2021_QA'

# Directory for output
OUT_DIR = "./output/"
os.makedirs(OUT_DIR, exist_ok=True)


###############################################################################
# SET UP OBJECTIVE FUNCTION
###############################################################################

# Initialize the boundary magnetic surface:
nphi = 32
ntheta = 32
s = SurfaceRZFourier.from_vmec_input(filename, range="half period", nphi=nphi, ntheta=ntheta)

# Create the initial coils:
base_curves = create_equally_spaced_curves(ncoils, s.nfp, stellsym=True, R0=R0, R1=R1, order=order)
base_currents = [Current(1e5) for i in range(ncoils)]
# Since the target field is zero, one possible solution is just to set all
# currents to 0. To avoid the minimizer finding that solution, we fix one
# of the currents:
base_currents[0].fix_all()

coils = coils_via_symmetries(base_curves, base_currents, s.nfp, True)
base_coils = coils[:ncoils]
bs = BiotSavart(coils)
bs.set_points(s.gamma().reshape((-1, 3)))

curves = [c.curve for c in coils]
curves_to_vtk(curves, OUT_DIR + "curves_init", close=True)
pointData = {"B_N": np.sum(bs.B().reshape((nphi, ntheta, 3)) * s.unitnormal(), axis=2)[:, :, None]}
s.to_vtk(OUT_DIR + "surf_init", extra_data=pointData)

# Define the individual terms objective function:
Jf = SquaredFlux(s, bs)
Jls = [CurveLength(c) for c in base_curves]
Jccdist = CurveCurveDistance(curves, CC_THRESHOLD, num_basecurves=ncoils)
Jcsdist = CurveSurfaceDistance(curves, s, CS_THRESHOLD)
Jcs = [LpCurveCurvature(c, 2, CURVATURE_THRESHOLD) for c in base_curves]
Jmscs = [MeanSquaredCurvature(c) for c in base_curves]
Jforce = [LpCurveForce(c, coils, regularization_circ(0.05), p=4) for c in base_coils]
# Jforce = [MeanSquaredForce(c, coils, regularization_circ(0.05)) for c in base_coils]


# Form the total objective function. To do this, we can exploit the
# fact that Optimizable objects with J() and dJ() functions can be
# multiplied by scalars and added:
JF = Jf \
+ LENGTH_WEIGHT * QuadraticPenalty(sum(Jls), LENGTH_TARGET, "max") \
+ CC_WEIGHT * Jccdist \
+ CS_WEIGHT * Jcsdist \
+ CURVATURE_WEIGHT * sum(Jcs) \
+ MSC_WEIGHT * sum(QuadraticPenalty(J, MSC_THRESHOLD, "max") for J in Jmscs) \
+ FORCE_WEIGHT * sum(Jforce)

# We don't have a general interface in SIMSOPT for optimisation problems that
# are not in least-squares form, so we write a little wrapper function that we
# pass directly to scipy.optimize.minimize


def fun(dofs):
JF.x = dofs
J = JF.J()
grad = JF.dJ()
return J, grad


# print("""
# ###############################################################################
# # Perform a Taylor test
# ###############################################################################
# """)
# print("(It make take jax several minutes to compile the objective for the first evaluation.)")
# f = fun
# dofs = JF.x
# np.random.seed(1)
# h = np.random.uniform(size=dofs.shape)
# J0, dJ0 = f(dofs)
# dJh = sum(dJ0 * h)
# for eps in [1e-3, 1e-4, 1e-5, 1e-6, 1e-7]:
# J1, _ = f(dofs + eps*h)
# J2, _ = f(dofs - eps*h)
# print("err", (J1-J2)/(2*eps) - dJh)

###############################################################################
# RUN THE OPTIMIZATION
###############################################################################


def pointData_forces(coils):
forces = []
for c in coils:
force = np.linalg.norm(coil_force(c, coils, regularization_circ(0.05)), axis=1)
force = np.append(force, force[0])
forces = np.concatenate([forces, force])
point_data = {"F": forces}
return point_data


dofs = JF.x
print(f"Optimization with FORCE_WEIGHT={FORCE_WEIGHT.value} and LENGTH_WEIGHT={LENGTH_WEIGHT.value}")
# print("INITIAL OPTIMIZATION")
res = minimize(fun, dofs, jac=True, method='L-BFGS-B', options={'maxiter': MAXITER, 'maxcor': 300}, tol=1e-15)
curves_to_vtk(curves, OUT_DIR + "curves_opt_short", close=True, extra_data=pointData_forces(coils))
pointData_surf = {"B_N": np.sum(bs.B().reshape((nphi, ntheta, 3)) * s.unitnormal(), axis=2)[:, :, None]}
s.to_vtk(OUT_DIR + "surf_opt_short", extra_data=pointData_surf)

# We now use the result from the optimization as the initial guess for a
# subsequent optimization with reduced penalty for the coil length. This will
# result in slightly longer coils but smaller `B·n` on the surface.
dofs = res.x
LENGTH_WEIGHT *= 0.1
# print("OPTIMIZATION WITH REDUCED LENGTH PENALTY\n")
res = minimize(fun, dofs, jac=True, method='L-BFGS-B', options={'maxiter': MAXITER, 'maxcor': 300}, tol=1e-15)
curves_to_vtk(curves, OUT_DIR + f"curves_opt_force_FWEIGHT={FORCE_WEIGHT.value:e}_LWEIGHT={LENGTH_WEIGHT.value*10:e}", close=True, extra_data=pointData_forces(coils))
pointData_surf = {"B_N": np.sum(bs.B().reshape((nphi, ntheta, 3)) * s.unitnormal(), axis=2)[:, :, None]}
s.to_vtk(OUT_DIR + f"surf_opt_force_WEIGHT={FORCE_WEIGHT.value:e}_LWEIGHT={LENGTH_WEIGHT.value*10:e}", extra_data=pointData_surf)

# Save the optimized coil shapes and currents so they can be loaded into other scripts for analysis:
bs.save(OUT_DIR + "biot_savart_opt.json")

#Print out final important info:
JF.x = dofs
J = JF.J()
grad = JF.dJ()
jf = Jf.J()
BdotN = np.mean(np.abs(np.sum(bs.B().reshape((nphi, ntheta, 3)) * s.unitnormal(), axis=2)))
force = [np.max(np.linalg.norm(coil_force(c, coils, regularization_circ(0.05)), axis=1)) for c in base_coils]
outstr = f"J={J:.1e}, Jf={jf:.1e}, ⟨B·n⟩={BdotN:.1e}"
cl_string = ", ".join([f"{J.J():.1f}" for J in Jls])
kap_string = ", ".join(f"{np.max(c.kappa()):.1f}" for c in base_curves)
msc_string = ", ".join(f"{J.J():.1f}" for J in Jmscs)
jforce_string = ", ".join(f"{J.J():.2e}" for J in Jforce)
force_string = ", ".join(f"{f:.2e}" for f in force)
outstr += f", Len=sum([{cl_string}])={sum(J.J() for J in Jls):.1f}, ϰ=[{kap_string}], ∫ϰ²/L=[{msc_string}], Jforce=[{jforce_string}], force=[{force_string}]"
outstr += f", C-C-Sep={Jccdist.shortest_distance():.2f}, C-S-Sep={Jcsdist.shortest_distance():.2f}"
outstr += f", ║∇J║={np.linalg.norm(grad):.1e}"
print(outstr)
1 change: 1 addition & 0 deletions examples/run_serial_examples
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ set -ex
./2_Intermediate/permanent_magnet_QA.py
./2_Intermediate/permanent_magnet_PM4Stell.py
./3_Advanced/stage_two_optimization_finitebuild.py
./3_Advanced/coil_forces.py
2 changes: 2 additions & 0 deletions src/simsopt/field/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .mgrid import *
from .normal_field import *
from .tracing import *
from .selffield import *
from .magnetic_axis_helpers import *

__all__ = (
Expand All @@ -17,5 +18,6 @@
+ mgrid.__all__
+ normal_field.__all__
+ tracing.__all__
+ selffield.__all__
+ magnetic_axis_helpers.__all__
)
1 change: 0 additions & 1 deletion src/simsopt/field/coil.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ def __init__(self, current, dofs=None, **kwargs):

def vjp(self, v_current):
return Derivative({self: v_current})

@property
def current(self):
return self.get_value()
Expand Down
Loading
Loading