|
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +import sys |
| 4 | +sys.path.append('../src') |
| 5 | +from mpi4py import MPI |
| 6 | +import numpy as np |
| 7 | +import logging |
| 8 | +from simsopt import Spec, SurfaceGarabedian, LeastSquaresProblem, \ |
| 9 | + least_squares_serial_solve |
| 10 | + |
| 11 | +""" |
| 12 | +This script implements the "1DOF_circularCrossSection_varyAxis_targetIota" |
| 13 | +example from |
| 14 | +https://github.com/landreman/stellopt_scenarios |
| 15 | +
|
| 16 | +This example demonstrates optimizing a surface shape using the |
| 17 | +Garabedian representation instead of VMEC's RBC/ZBS representation. |
| 18 | +This optimization problem has one independent variable, the Garabedian |
| 19 | +Delta_{m=1, n=-1} coefficient, representing the helical excursion of |
| 20 | +the magnetic axis. The objective function is (iota - iota_target)^2, |
| 21 | +where iota is measured on the magnetic axis. |
| 22 | +
|
| 23 | +Details of the optimum and a plot of the objective function landscape |
| 24 | +can be found here: |
| 25 | +https://github.com/landreman/stellopt_scenarios/tree/master/1DOF_circularCrossSection_varyAxis_targetIota |
| 26 | +""" |
| 27 | + |
| 28 | +logging.basicConfig(level=logging.DEBUG) |
| 29 | + |
| 30 | +# Create a Spec object: |
| 31 | +equil = Spec('1DOF_Garabedian.sp') |
| 32 | +# If the xspec executable is not in PATH, the path to the executable |
| 33 | +# should be specified as in the following line: |
| 34 | +# equil = Spec('1DOF_Garabedian.sp', exe='/Users/mattland/SPEC/xspec') |
| 35 | + |
| 36 | +# Start with a default surface, which is axisymmetric with major |
| 37 | +# radius 1 and minor radius 0.1. |
| 38 | + |
| 39 | +# We will optimize in the space of Garabedian coefficients rather than |
| 40 | +# RBC/ZBS coefficients. To do this, we convert the boundary to the |
| 41 | +# Garabedian representation: |
| 42 | +surf = equil.boundary.to_Garabedian() |
| 43 | +equil.boundary = surf |
| 44 | + |
| 45 | +# VMEC parameters are all fixed by default, while surface parameters |
| 46 | +# are all non-fixed by default. You can choose which parameters are |
| 47 | +# optimized by setting their 'fixed' attributes. |
| 48 | +surf.all_fixed() |
| 49 | +surf.set_fixed('Delta(1,-1)', False) |
| 50 | + |
| 51 | +# Each function we want in the objective function is then equipped |
| 52 | +# with a shift and weight, to become a term in a least-squares |
| 53 | +# objective function. A list of terms are combined to form a |
| 54 | +# nonlinear-least-squares problem. |
| 55 | +desired_iota = 0.41 # Sign was + for VMEC |
| 56 | +prob = LeastSquaresProblem([(equil.iota, desired_iota, 1)]) |
| 57 | + |
| 58 | +# Solve the minimization problem. We can choose whether to use a |
| 59 | +# derivative-free or derivative-based algorithm. |
| 60 | +least_squares_serial_solve(prob, grad=False) |
| 61 | + |
| 62 | +print("At the optimum,") |
| 63 | +print(" Delta(m=1,n=-1) = ", surf.get_Delta(1, -1)) |
| 64 | +print(" iota on axis = ", equil.iota()) |
| 65 | +print(" objective function = ", prob.objective()) |
| 66 | + |
| 67 | +assert np.abs(surf.get_Delta(1, -1) - 0.08575) < 1.0e-4 |
| 68 | +assert np.abs(equil.iota() - desired_iota) < 1.0e-5 |
| 69 | +assert prob.objective() < 1.0e-15 |
0 commit comments