def test_surface_sampling(self): np.random.seed(1) nquadpoints = int(4e2) surface = SurfaceRZFourier(nfp=1, stellsym=True, mpol=1, ntor=0, quadpoints_phi=nquadpoints, quadpoints_theta=nquadpoints) dofs = surface.get_dofs() dofs[0] = 1 dofs[1] = 0.8 surface.set_dofs(dofs) n = np.linalg.norm(surface.normal(), axis=2) print(np.min(n), np.max(n)) start = int(0.2*nquadpoints) stop = int(0.5*nquadpoints) from scipy.integrate import simpson quadpoints_phi = surface.quadpoints_phi quadpoints_theta = surface.quadpoints_theta lineintegrals = [simpson(y=n[i, start:stop], x=quadpoints_theta[start:stop]) for i in range(start, stop)] area_of_subset = simpson(y=lineintegrals, x=quadpoints_phi[start:stop]) total_area = surface.area() print("area_of_subset/total_area", area_of_subset/total_area) nsamples = int(1e6) xyz, idxs = draw_uniform_on_surface(surface, nsamples, safetyfactor=10) samples_in_range = np.sum((idxs[0] >= start) * (idxs[0] < stop)*(idxs[1] >= start) * (idxs[1] < stop)) print("samples_in_range/nsamples", samples_in_range/nsamples) print("fraction of samples if uniform", (stop-start)**2/(nquadpoints**2)) assert abs(samples_in_range/nsamples - area_of_subset/total_area) < 1e-2
def test_change_resolution(self): """ Check that we can change mpol and ntor. """ for mpol in [1, 2]: for ntor in [0, 1]: s = SurfaceRZFourier(mpol=mpol, ntor=ntor) n = len(s.get_dofs()) s.set_dofs((np.random.rand(n) - 0.5) * 0.01) s.set_rc(0, 0, 1.0) s.set_rc(1, 0, 0.1) s.set_zs(1, 0, 0.13) v1 = s.volume() a1 = s.area() s.change_resolution(mpol + 1, ntor) s.recalculate = True v2 = s.volume() a2 = s.area() self.assertAlmostEqual(v1, v2) self.assertAlmostEqual(a1, a2) s.change_resolution(mpol, ntor + 1) s.recalculate = True v2 = s.volume() a2 = s.area() self.assertAlmostEqual(v1, v2) self.assertAlmostEqual(a1, a2) s.change_resolution(mpol + 1, ntor + 1) s.recalculate = True v2 = s.volume() a2 = s.area() self.assertAlmostEqual(v1, v2) self.assertAlmostEqual(a1, a2)
def test_convert_back(self): """ If we start with a SurfaceRZFourier, convert to Garabedian, and convert back to SurfaceFourier, we should get back what we started with. """ for mpol in range(1, 4): for ntor in range(5): for nfp in range(1, 4): sf1 = SurfaceRZFourier(nfp=nfp, mpol=mpol, ntor=ntor) # Set all dofs to random numbers in [-2, 2]: sf1.set_dofs( (np.random.rand(len(sf1.get_dofs())) - 0.5) * 4) sg = sf1.to_Garabedian() sf2 = sg.to_RZFourier() np.testing.assert_allclose(sf1.rc, sf2.rc) np.testing.assert_allclose(sf1.zs, sf2.zs)
def get_surface(surfacetype, stellsym, phis=None, thetas=None): np.random.seed(2) mpol = 4 ntor = 3 nfp = 2 phis = phis if phis is not None else np.linspace(0, 1, 31, endpoint=False) thetas = thetas if thetas is not None else np.linspace( 0, 1, 31, endpoint=False) if surfacetype == "SurfaceRZFourier": from simsopt.geo.surfacerzfourier import SurfaceRZFourier s = SurfaceRZFourier(nfp=nfp, stellsym=stellsym, mpol=mpol, ntor=ntor, quadpoints_phi=phis, quadpoints_theta=thetas) s.set_dofs(s.get_dofs() * 0.) s.rc[0, ntor + 0] = 1 s.rc[1, ntor + 0] = 0.3 s.zs[1, ntor + 0] = 0.3 elif surfacetype == "SurfaceXYZFourier": from simsopt.geo.surfacexyzfourier import SurfaceXYZFourier s = SurfaceXYZFourier(nfp=nfp, stellsym=stellsym, mpol=mpol, ntor=ntor, quadpoints_phi=phis, quadpoints_theta=thetas) s.set_dofs(s.get_dofs() * 0.) s.xc[0, ntor + 1] = 1. s.xc[1, ntor + 1] = 0.1 s.ys[0, ntor + 1] = 1. s.ys[1, ntor + 1] = 0.1 s.zs[1, ntor] = 0.1 elif surfacetype == "SurfaceXYZTensorFourier": from simsopt.geo.surfacexyztensorfourier import SurfaceXYZTensorFourier s = SurfaceXYZTensorFourier(nfp=nfp, stellsym=stellsym, mpol=mpol, ntor=ntor, quadpoints_phi=phis, quadpoints_theta=thetas) s.set_dofs(s.get_dofs() * 0.) s.x[0, 0] = 1.0 s.x[1, 0] = 0.1 s.z[mpol + 1, 0] = 0.1 else: assert False dofs = np.asarray(s.get_dofs()) np.random.seed(2) rand_scale = 0.01 s.set_dofs(dofs + rand_scale * np.random.rand(len(dofs)).reshape(dofs.shape)) return s
def test_set_dofs(self): """ Test that we can set the shape from a 1D vector """ # First try an axisymmetric surface for simplicity: s = SurfaceRZFourier() s.set_dofs([2.9, -1.1, 0.7]) self.assertAlmostEqual(s.rc[0, 0], 2.9) self.assertAlmostEqual(s.rc[1, 0], -1.1) self.assertAlmostEqual(s.zs[1, 0], 0.7) # Now try a nonaxisymmetric shape: s = SurfaceRZFourier(mpol=3, ntor=1) s.set_dofs(np.array(list(range(21))) + 1) self.assertAlmostEqual(s.rc[0, 0], 0) self.assertAlmostEqual(s.rc[0, 1], 1) self.assertAlmostEqual(s.rc[0, 2], 2) self.assertAlmostEqual(s.rc[1, 0], 3) self.assertAlmostEqual(s.rc[1, 1], 4) self.assertAlmostEqual(s.rc[1, 2], 5) self.assertAlmostEqual(s.rc[2, 0], 6) self.assertAlmostEqual(s.rc[2, 1], 7) self.assertAlmostEqual(s.rc[2, 2], 8) self.assertAlmostEqual(s.rc[3, 0], 9) self.assertAlmostEqual(s.rc[3, 1], 10) self.assertAlmostEqual(s.rc[3, 2], 11) self.assertAlmostEqual(s.zs[0, 0], 0) self.assertAlmostEqual(s.zs[0, 1], 0) self.assertAlmostEqual(s.zs[0, 2], 12) self.assertAlmostEqual(s.zs[1, 0], 13) self.assertAlmostEqual(s.zs[1, 1], 14) self.assertAlmostEqual(s.zs[1, 2], 15) self.assertAlmostEqual(s.zs[2, 0], 16) self.assertAlmostEqual(s.zs[2, 1], 17) self.assertAlmostEqual(s.zs[2, 2], 18) self.assertAlmostEqual(s.zs[3, 0], 19) self.assertAlmostEqual(s.zs[3, 1], 20) self.assertAlmostEqual(s.zs[3, 2], 21)
def test_derivatives(self): """ Check the automatic differentiation for area and volume. """ for mpol in range(1, 3): for ntor in range(2): for nfp in range(1, 4): s = SurfaceRZFourier(nfp=nfp, mpol=mpol, ntor=ntor) x0 = s.get_dofs() x = np.random.rand(len(x0)) - 0.5 x[0] = np.random.rand() + 2 # This surface will probably self-intersect, but I # don't think this actually matters here. s.set_dofs(x) dofs = Dofs([s.area, s.volume]) jac = dofs.jac() fd_jac = dofs.fd_jac() print('difference for surface test_derivatives:', jac - fd_jac) np.testing.assert_allclose(jac, fd_jac, rtol=1e-4, atol=1e-4)