def __init__(self, coils, currents, nfp, stellarator_symmetry):
     self._base_coils = coils
     self._base_currents = currents
     self.coils = []
     self.currents = []
     flip_list = [False, True] if stellarator_symmetry else [False]
     self.map = []
     self.current_sign = []
     for k in range(0, nfp):
         for flip in flip_list:
             for i in range(len(coils)):
                 if k == 0 and not flip:
                     self.coils.append(self._base_coils[i])
                     self.currents.append(self._base_currents[i])
                 else:
                     rotcoil = RotatedCurve(coils[i], 2 * pi * k / nfp,
                                            flip)
                     self.coils.append(rotcoil)
                     self.currents.append(
                         -self._base_currents[i] if flip else currents[i])
                 self.map.append(i)
                 self.current_sign.append(-1 if flip else +1)
     dof_ranges = [(0, len(self._base_coils[0].get_dofs()))]
     for i in range(1, len(self._base_coils)):
         dof_ranges.append(
             (dof_ranges[-1][1],
              dof_ranges[-1][1] + len(self._base_coils[i].get_dofs())))
     self.dof_ranges = dof_ranges
Exemple #2
0
    def create_curve(self, curvetype, rotated):
        np.random.seed(1)
        rand_scale = 0.01
        order = 4
        nquadpoints = 200

        if curvetype == "CurveXYZFourier":
            coil = CurveXYZFourier(nquadpoints, order)
        elif curvetype == "JaxCurveXYZFourier":
            coil = JaxCurveXYZFourier(nquadpoints, order)
        elif curvetype == "CurveRZFourier":
            coil = CurveRZFourier(nquadpoints, order, 2, False)
        else:
            # print('Could not find' + curvetype)
            assert False
        dofs = np.zeros((coil.num_dofs(), ))
        if curvetype in ["CurveXYZFourier", "JaxCurveXYZFourier"]:
            dofs[1] = 1.
            dofs[2 * order + 3] = 1.
            dofs[4 * order + 3] = 1.
        elif curvetype in ["CurveRZFourier"]:
            dofs[0] = 1.
            dofs[1] = 0.1
            dofs[order + 1] = 0.1
        else:
            assert False
        coil.set_dofs(dofs)

        dofs = np.asarray(coil.get_dofs())
        coil.set_dofs(dofs + rand_scale *
                      np.random.rand(len(dofs)).reshape(dofs.shape))
        if rotated:
            coil = RotatedCurve(coil, 0.5, flip=False)
        return coil
Exemple #3
0
 def subtest_curve_minimum_distance_taylor_test(self, curve):
     ncurves = 3
     curve_t = curve.curve.__class__.__name__ if isinstance(
         curve, RotatedCurve) else curve.__class__.__name__
     curves = [curve] + [
         RotatedCurve(self.create_curve(curve_t, False), 0.1 * i, True)
         for i in range(1, ncurves)
     ]
     J = MinimumDistance(curves, 0.2)
     for k in range(ncurves):
         curve_dofs = np.asarray(curves[k].get_dofs())
         h = 1e-3 * np.random.rand(len(curve_dofs)).reshape(
             curve_dofs.shape)
         J0 = J.J()
         dJ = J.dJ()[k]
         deriv = np.sum(dJ * h)
         assert np.abs(deriv) > 1e-10
         err = 1e6
         for i in range(5, 15):
             eps = 0.5**i
             curves[k].set_dofs(curve_dofs + eps * h)
             Jh = J.J()
             deriv_est = (Jh - J0) / eps
             err_new = np.linalg.norm(deriv_est - deriv)
             # print("err_new %s" % (err_new))
             assert err_new < 0.55 * err
             err = err_new
    def subtest_curve_length_optimisation(self, rotated):
        nquadrature = 100
        nfourier = 4
        nfp = 5
        curve = CurveRZFourier(nquadrature, nfourier, nfp, True)
        if rotated:
            curve = RotatedCurve(curve, 0.5, flip=False)

        # Initialize the Fourier amplitudes to some random values
        x0 = np.random.rand(curve.num_dofs()) - 0.5
        x0[0] = 3.0
        curve.set_dofs(x0)
        print('Initial curve dofs: ', curve.get_dofs())

        # Tell the curve object that the first Fourier mode is fixed, whereas
        # all the other dofs are not.
        curve.all_fixed(False)
        curve.fixed[0] = True

        # Presently in simsgeo, the length objective is a separate object
        # rather than a function of Curve itself.
        obj = make_optimizable(CurveLength(curve))

        # For now, we need to add this attribute to CurveLength. Eventually
        # this would hopefully be done in simsgeo, but for now I'll put it here.
        obj.depends_on = ['curve']

        print('Initial curve length: ', obj.J())

        # Each target function is then equipped with a shift and weight, to
        # become a term in a least-squares objective function.
        # A list of terms are combined to form a nonlinear-least-squares
        # problem.
        prob = LeastSquaresProblem([(obj, 0.0, 1.0)])

        # At the initial condition, get the Jacobian two ways: analytic
        # derivatives and finite differencing. The difference should be small.
        fd_jac = prob.dofs.fd_jac()
        jac = prob.dofs.jac()
        print('finite difference Jacobian:')
        print(fd_jac)
        print('Analytic Jacobian:')
        print(jac)
        print('Difference:')
        print(fd_jac - jac)
        assert np.allclose(fd_jac, jac, rtol=1e-4, atol=1e-4)

        # Solve the minimization problem:
        least_squares_serial_solve(prob, ftol=1e-10, xtol=1e-10, gtol=1e-10)

        print('At the optimum, x: ', prob.x)
        print(' Final curve dofs: ', curve.get_dofs())
        print(' Final curve length:    ', obj.J())
        print(' Expected final length: ', 2 * np.pi * x0[0])
        print(' objective function: ', prob.objective())
        assert abs(obj.J() - 2 * np.pi * x0[0]) < 1e-8
Exemple #5
0
def get_curve(curvetype, rotated, x=np.asarray([0.5])):
    np.random.seed(2)
    rand_scale = 0.01
    order = 4

    if curvetype == "CurveXYZFourier":
        curve = CurveXYZFourier(x, order)
    elif curvetype == "JaxCurveXYZFourier":
        curve = JaxCurveXYZFourier(x, order)
    elif curvetype == "CurveRZFourier":
        curve = CurveRZFourier(x, order, 2, True)
    elif curvetype == "CurveHelical":
        curve = CurveHelical(x, order, 5, 2, 1.0, 0.3)
    else:
        assert False
    dofs = np.zeros((curve.num_dofs(), ))
    if curvetype in ["CurveXYZFourier", "JaxCurveXYZFourier"]:
        dofs[1] = 1.
        dofs[2 * order + 3] = 1.
        dofs[4 * order + 3] = 1.
    elif curvetype in ["CurveRZFourier"]:
        dofs[0] = 1.
        dofs[1] = 0.1
        dofs[order + 1] = 0.1
    elif curvetype in ["CurveHelical"]:
        dofs[0] = np.pi / 2
    else:
        assert False
    curve.set_dofs(dofs)

    dofs = np.asarray(curve.get_dofs())
    curve.set_dofs(dofs +
                   rand_scale * np.random.rand(len(dofs)).reshape(dofs.shape))
    if rotated:
        curve = RotatedCurve(curve, 0.5, flip=False)
    return curve
Exemple #6
0
    def subtest_minimize_qfm(self, surfacetype, stellsym):
        """
        For each configuration, test to verify that minimize_qfm() yields same
        result as minimize_qfm_exact_constraints_SLSQP or
        minimize_qfm_penalty_constraints_LBFGS separately. Test that InputError
        is raised if 'LBFGS' or 'SLSQP' is passed.
        """
        coils, currents, ma = get_ncsx_data()

        if stellsym:
            stellarator = CoilCollection(coils, currents, 3, True)
        else:
            # Create a stellarator that still has rotational symmetry but
            # doesn't have stellarator symmetry. We do this by first applying
            # stellarator symmetry, then breaking this slightly, and then
            # applying rotational symmetry
            from simsopt.geo.curve import RotatedCurve
            coils_flipped = [RotatedCurve(c, 0, True) for c in coils]
            currents_flipped = [-cur for cur in currents]
            for c in coils_flipped:
                c.rotmat += 0.001 * np.random.uniform(
                    low=-1., high=1., size=c.rotmat.shape)
                c.rotmatT = c.rotmat.T
            stellarator = CoilCollection(coils + coils_flipped,
                                         currents + currents_flipped, 3, False)

        bs = BiotSavart(stellarator.coils, stellarator.currents)
        bs_tf = BiotSavart(stellarator.coils, stellarator.currents)

        nfp = 3
        phis = np.linspace(0, 1 / nfp, 30, endpoint=False)
        thetas = np.linspace(0, 1, 30, endpoint=False)
        constraint_weight = 1e0

        s = get_surface(surfacetype,
                        stellsym,
                        phis=phis,
                        thetas=thetas,
                        ntor=3,
                        mpol=3)
        s.fit_to_curve(ma, 0.2)

        vol = Volume(s)
        vol_target = vol.J()
        qfm_surface = QfmSurface(bs, s, vol, vol_target)

        # Compute surface first using LBFGS and a volume constraint
        res = qfm_surface.minimize_qfm_penalty_constraints_LBFGS(
            tol=1e-10, maxiter=10000, constraint_weight=constraint_weight)

        grad1 = np.linalg.norm(res['gradient'])
        fun1 = res['fun']
        vol1 = vol.J()

        # Perform same calculation by calling qfm_minimize
        s = get_surface(surfacetype,
                        stellsym,
                        phis=phis,
                        thetas=thetas,
                        ntor=3,
                        mpol=3)
        s.fit_to_curve(ma, 0.2)

        res = qfm_surface.minimize_qfm(method='LBFGS',
                                       tol=1e-10,
                                       maxiter=10000,
                                       constraint_weight=constraint_weight)

        grad2 = np.linalg.norm(res['gradient'])
        fun2 = res['fun']
        vol2 = vol.J()

        # Test for preservation of results
        np.allclose(grad1, grad2)
        np.allclose(fun1, fun2)
        np.allclose(vol1, vol2)

        # Perform calculation with SLSQP
        s = get_surface(surfacetype,
                        stellsym,
                        phis=phis,
                        thetas=thetas,
                        ntor=3,
                        mpol=3)
        s.fit_to_curve(ma, 0.2)
        res = qfm_surface.minimize_qfm_exact_constraints_SLSQP(tol=1e-11,
                                                               maxiter=1000)

        grad1 = np.linalg.norm(res['gradient'])
        fun1 = res['fun']
        vol1 = vol.J()

        # Perform same calculation by calling qfm_minimize
        s = get_surface(surfacetype,
                        stellsym,
                        phis=phis,
                        thetas=thetas,
                        ntor=3,
                        mpol=3)
        s.fit_to_curve(ma, 0.2)
        res = qfm_surface.minimize_qfm(method='SLSQP', tol=1e-11, maxiter=1000)

        grad2 = np.linalg.norm(res['gradient'])
        fun2 = res['fun']
        vol2 = vol.J()

        # Test for preservation of results
        np.allclose(grad1, grad2)
        np.allclose(fun1, fun2)
        np.allclose(vol1, vol2)

        # Test that InputError raised
        with self.assertRaises(ValueError):
            res = qfm_surface.minimize_qfm(method='SLSQPP',
                                           tol=1e-11,
                                           maxiter=1000)
Exemple #7
0
    def subtest_qfm_surface_optimization_convergence(self, surfacetype,
                                                     stellsym):
        """
        For each configuration, first reduce penalty objective using LBFGS at
        fixed volume. Then solve constrained problem using SLSQP. Repeat
        both steps for fixed area. Check that volume is preserved.
        """
        coils, currents, ma = get_ncsx_data()

        if stellsym:
            stellarator = CoilCollection(coils, currents, 3, True)
        else:
            # Create a stellarator that still has rotational symmetry but
            # doesn't have stellarator symmetry. We do this by first applying
            # stellarator symmetry, then breaking this slightly, and then
            # applying rotational symmetry
            from simsopt.geo.curve import RotatedCurve
            coils_flipped = [RotatedCurve(c, 0, True) for c in coils]
            currents_flipped = [-cur for cur in currents]
            for c in coils_flipped:
                c.rotmat += 0.001 * np.random.uniform(
                    low=-1., high=1., size=c.rotmat.shape)
                c.rotmatT = c.rotmat.T
            stellarator = CoilCollection(coils + coils_flipped,
                                         currents + currents_flipped, 3, False)

        bs = BiotSavart(stellarator.coils, stellarator.currents)
        bs_tf = BiotSavart(stellarator.coils, stellarator.currents)

        nfp = 3
        phis = np.linspace(0, 1 / nfp, 30, endpoint=False)
        thetas = np.linspace(0, 1, 30, endpoint=False)
        constraint_weight = 1e0

        s = get_surface(surfacetype,
                        stellsym,
                        phis=phis,
                        thetas=thetas,
                        ntor=3,
                        mpol=3)
        s.fit_to_curve(ma, 0.2)

        vol = Volume(s)
        vol_target = vol.J()
        qfm_surface = QfmSurface(bs, s, vol, vol_target)

        # Compute surface first using LBFGS and a volume constraint
        res = qfm_surface.minimize_qfm_penalty_constraints_LBFGS(
            tol=1e-10, maxiter=10000, constraint_weight=constraint_weight)

        assert res['success']
        assert np.linalg.norm(res['gradient']) < 1e-2
        assert res['fun'] < 1e-5
        assert np.abs(vol_target - vol.J()) < 1e-4

        # As a second step, optimize with SLSQP

        res = qfm_surface.minimize_qfm_exact_constraints_SLSQP(tol=1e-11,
                                                               maxiter=1000)

        assert res['success']
        assert np.linalg.norm(res['gradient']) < 1e-3
        assert res['fun'] < 1e-5
        assert np.abs(vol_target - vol.J()) < 1e-5

        vol_opt1 = vol.J()

        # Now optimize with area constraint

        ar = Area(s)
        ar_target = ar.J()
        qfm_surface = QfmSurface(bs, s, ar, ar_target)

        res = qfm_surface.minimize_qfm_penalty_constraints_LBFGS(
            tol=1e-10, maxiter=1000, constraint_weight=constraint_weight)

        assert res['success']
        assert res['fun'] < 1e-5
        assert np.linalg.norm(res['gradient']) < 1e-2
        assert np.abs(ar_target - ar.J()) < 1e-5

        res = qfm_surface.minimize_qfm_exact_constraints_SLSQP(tol=1e-11,
                                                               maxiter=1000)

        assert res['success']
        assert res['fun'] < 1e-5
        assert np.linalg.norm(res['gradient']) < 1e-3
        assert np.abs(ar_target - ar.J()) < 1e-4

        vol_opt2 = vol.J()

        # Check that volume after second opt does not change

        assert np.abs(vol_opt2 - vol_opt1) < 1e-3
    def subtest_boozer_surface_optimisation_convergence(
            self, surfacetype, stellsym, optimize_G, second_stage):
        coils, currents, ma = get_ncsx_data()

        if stellsym:
            stellarator = CoilCollection(coils, currents, 3, True)
        else:
            # Create a stellarator that still has rotational symmetry but
            # doesn't have stellarator symmetry. We do this by first applying
            # stellarator symmetry, then breaking this slightly, and then
            # applying rotational symmetry
            from simsopt.geo.curve import RotatedCurve
            coils_flipped = [RotatedCurve(c, 0, True) for c in coils]
            currents_flipped = [-cur for cur in currents]
            for c in coils_flipped:
                c.rotmat += 0.001 * np.random.uniform(
                    low=-1., high=1., size=c.rotmat.shape)
                c.rotmatT = c.rotmat.T
            stellarator = CoilCollection(coils + coils_flipped,
                                         currents + currents_flipped, 3, False)

        bs = BiotSavart(stellarator.coils, stellarator.currents)

        s = get_surface(surfacetype, stellsym)
        s.fit_to_curve(ma, 0.1)
        iota = -0.3

        ar = Area(s)
        ar_target = ar.J()
        boozer_surface = BoozerSurface(bs, s, ar, ar_target)

        if optimize_G:
            G = 2. * np.pi * np.sum(np.abs(
                bs.coil_currents)) * (4 * np.pi * 10**(-7) / (2 * np.pi))
        else:
            G = None

        # compute surface first using LBFGS exact and an area constraint
        res = boozer_surface.minimize_boozer_penalty_constraints_LBFGS(
            tol=1e-9, maxiter=500, constraint_weight=100., iota=iota, G=G)
        print('Squared residual after LBFGS', res['fun'])
        if second_stage == 'ls':
            res = boozer_surface.minimize_boozer_penalty_constraints_ls(
                tol=1e-9,
                maxiter=100,
                constraint_weight=100.,
                iota=res['iota'],
                G=res['G'])
        elif second_stage == 'newton':
            res = boozer_surface.minimize_boozer_penalty_constraints_newton(
                tol=1e-9,
                maxiter=10,
                constraint_weight=100.,
                iota=res['iota'],
                G=res['G'],
                stab=1e-4)
        elif second_stage == 'newton_exact':
            res = boozer_surface.minimize_boozer_exact_constraints_newton(
                tol=1e-9, maxiter=10, iota=res['iota'], G=res['G'])

        print('Residual after second stage', np.linalg.norm(res['residual']))
        assert res['success']
        # For the stellsym case we have z(0, 0) = y(0, 0) = 0. For the not
        # stellsym case, we enforce z(0, 0) = 0, but expect y(0, 0) \neq 0
        gammazero = s.gamma()[0, 0, :]
        assert np.abs(gammazero[2]) < 1e-10
        if stellsym:
            assert np.abs(gammazero[1]) < 1e-10
        else:
            assert np.abs(gammazero[1]) > 1e-6

        if surfacetype == 'SurfaceXYZTensorFourier':
            assert np.linalg.norm(res['residual']) < 1e-9

        if second_stage == 'newton_exact' or surfacetype == 'SurfaceXYZTensorFourier':
            assert np.abs(ar_target - ar.J()) < 1e-9