Example #1
0
    def test_read_input(self):
        """
        Try reading a VMEC input file.
        """
        ictrl[:] = 0
        ictrl[0] = restart_flag + readin_flag
        logger.info("Calling runvmec. ictrl={} comm={}".format(ictrl, fcomm))
        vmec.runvmec(ictrl, filename, verbose, fcomm, reset_file)

        self.assertEqual(ictrl[1], 0)

        self.assertEqual(vmec.vmec_input.nfp, 3)
        self.assertEqual(vmec.vmec_input.mpol, 4)
        self.assertEqual(vmec.vmec_input.ntor, 3)
        logger.info('rbc.shape: {}'.format(vmec.vmec_input.rbc.shape))
        logger.info('rbc: {}'.format(vmec.vmec_input.rbc[101:103, 0:4]))

        # n = 0, m = 0:
        self.assertAlmostEqual(vmec.vmec_input.rbc[101, 0], 1.3782)

        # n = 0, m = 1:
        self.assertAlmostEqual(vmec.vmec_input.zbs[101, 1], 4.6465E-01)

        # n = 1, m = 1:
        self.assertAlmostEqual(vmec.vmec_input.zbs[102, 1], 1.6516E-01)

        logger.info("About to cleanup(False)")
        vmec.cleanup(False)
Example #2
0
def compare_to_vmec(name, r=0.005, nphi=151):
    """
    Check that VMEC can run the input file outputed by pyQSC
    and check that the resulting VMEC output file has
    the expected parameters
    """
    # Add the directory of this file to the specified filename:
    inputFile="input."+str(name).replace(" ","")
    abs_filename = os.path.join(os.path.dirname(__file__), inputFile)
    # Run pyQsc and create a VMEC input file
    logger.info('Creating pyQSC configuration')
    order = 'r2' if name[1] == '2' else 'r1'
    py = Qsc.from_paper(name, nphi=nphi, order=order)
    logger.info('Outputing to VMEC')
    py.to_vmec(inputFile,r)
    # Run VMEC
    fcomm = MPI.COMM_WORLD.py2f()
    logger.info("Calling runvmec. comm={}".format(fcomm))
    ictrl=np.array([15,0,0,0,0], dtype=np.int32)
    vmec.runvmec(ictrl, inputFile, True, fcomm, '')
    # Check that VMEC converged
    assert ictrl[1] == 11
    # Open VMEC output file
    woutFile="wout_"+str(name).replace(" ","")+".nc"
    f = netcdf.netcdf_file(woutFile, 'r')
    # Compare the results
    logger.info('pyQSC iota on axis = '+str(py.iota))
    logger.info('VMEC iota on axis = '+str(-f.variables['iotaf'][()][0]))
    logger.info('pyQSC field on axis = '+str(py.B0))
    logger.info('VMEC bmnc[1][0] = '+str(f.variables['bmnc'][()][1][0]))
    assert np.isclose(py.iota,-f.variables['iotaf'][()][0],rtol=1e-2)
    assert np.isclose(py.B0,f.variables['bmnc'][()][1][0],rtol=1e-2)
    vmec.cleanup(True)
    f.close()
Example #3
0
    def test_run_read(self):
        """
        Try running VMEC, then reading in the output.
        """
        ictrl[:] = 0
        ictrl[0] = 1 + 2 + 4 + 8
        vmec.runvmec(ictrl, filename, verbose, fcomm, reset_file)

        self.assertEqual(ictrl[1], 11)

        self.assertEqual(vmec.vmec_input.nfp, 3)
        self.assertEqual(vmec.vmec_input.mpol, 4)
        self.assertEqual(vmec.vmec_input.ntor, 3)
        logger.info('rbc.shape: {}'.format(vmec.vmec_input.rbc.shape))
        logger.info('rbc: {}'.format(vmec.vmec_input.rbc[101:103, 0:4]))

        # n = 0, m = 0:
        self.assertAlmostEqual(vmec.vmec_input.rbc[101, 0], 1.3782)

        # n = 0, m = 1:
        self.assertAlmostEqual(vmec.vmec_input.zbs[101, 1], 4.6465E-01)

        # n = 1, m = 1:
        self.assertAlmostEqual(vmec.vmec_input.zbs[102, 1], 1.6516E-01)

        # Before trying to read the wout file, make sure master is
        # done writing it.
        MPI.COMM_WORLD.Barrier()

        # Now try reading in the output
        #wout_file = os.path.join(os.path.dirname(__file__), 'wout_li383_low_res.nc')
        wout_file = 'wout_li383_low_res.nc'
        logger.info("About to read output file {}".format(wout_file))
        #ierr = 0
        #vmec.read_wout_mod.read_wout_file(wout_file, ierr)
        #self.assertEqual(ierr, 0)
        f = netcdf.netcdf_file(wout_file, mmap=False)

        #self.assertAlmostEqual(vmec.read_wout_mod.betatot, \
        self.assertAlmostEqual(f.variables['betatotal'][()], \
                                   0.0426215030653306, places=4)

        #logger.info('iotaf.shape: {}'.format(vmec.read_wout_mod.iotaf.shape))
        #logger.info('rmnc.shape: {}'.format(vmec.read_wout_mod.rmnc.shape))

        #self.assertAlmostEqual(vmec.read_wout_mod.iotaf[-1], \
        iotaf = f.variables['iotaf'][()]
        self.assertAlmostEqual(iotaf[-1], \
                               0.6556508142482989, places=4)

        #self.assertAlmostEqual(vmec.read_wout_mod.rmnc[0, 0], \
        rmnc = f.variables['rmnc'][()]
        self.assertAlmostEqual(rmnc[0, 0], \
                               1.4760749266902973, places=4)

        f.close()
        logger.info("About to cleanup(True)")
        vmec.cleanup(True)
Example #4
0
 def tearDown(self):
     """
     Tear down the test fixture. This subroutine is automatically
     run by python's unittest module before every test. Here we
     call runvmec with the cleanup flag to deallocate arrays, such
     that runvmec can be called again later.
     """
     self.ictrl[0] = 16  # cleanup
     vmec.runvmec(self.ictrl, self.filename, self.verbose, \
                              self.fcomm, reset_file)
Example #5
0
    def test_run_read(self):
        """
        Try running VMEC, then reading in results from the wout file.
        """

        self.ictrl[0] = 1 + 2 + 4 + 8
        vmec.runvmec(self.ictrl, self.filename, self.verbose, \
                                 self.fcomm, reset_file)

        self.assertTrue(self.ictrl[1] in success_codes)

        self.assertEqual(vmec.vmec_input.nfp, 3)
        self.assertEqual(vmec.vmec_input.mpol, 4)
        self.assertEqual(vmec.vmec_input.ntor, 3)
        print('rbc.shape:', vmec.vmec_input.rbc.shape)
        print('rbc:', vmec.vmec_input.rbc[101:103, 0:4])

        # n = 0, m = 0:
        self.assertAlmostEqual(vmec.vmec_input.rbc[101, 0], 1.3782)

        # n = 0, m = 1:
        self.assertAlmostEqual(vmec.vmec_input.zbs[101, 1], 4.6465E-01)

        # n = 1, m = 1:
        self.assertAlmostEqual(vmec.vmec_input.zbs[102, 1], 1.6516E-01)

        # Now try reading in the output
        wout_file = os.path.join(os.path.dirname(__file__),
                                 'wout_li383_low_res.nc')
        ierr = 0
        vmec.read_wout_mod.read_wout_file(wout_file, ierr)
        self.assertEqual(ierr, 0)
        self.assertAlmostEqual(vmec.read_wout_mod.betatot, \
                                   0.0426215030653306, places=4)

        print('iotaf.shape:', vmec.read_wout_mod.iotaf.shape)
        print('rmnc.shape:', vmec.read_wout_mod.rmnc.shape)

        self.assertAlmostEqual(vmec.read_wout_mod.iotaf[-1], \
                                   0.654868168783638, places=4)

        self.assertAlmostEqual(vmec.read_wout_mod.rmnc[0, 0], \
                                   1.4773028173065, places=4)
Example #6
0
    def test_read_input(self):
        """
        Try reading a VMEC input file.
        """
        self.ictrl[0] = run_modes['input']
        vmec.runvmec(self.ictrl, self.filename, self.verbose, \
                                 self.fcomm, reset_file)

        self.assertTrue(self.ictrl[1] in success_codes)

        self.assertEqual(vmec.vmec_input.nfp, 3)
        self.assertEqual(vmec.vmec_input.mpol, 4)
        self.assertEqual(vmec.vmec_input.ntor, 3)
        print('rbc.shape:', vmec.vmec_input.rbc.shape)
        print('rbc:', vmec.vmec_input.rbc[101:103, 0:4])

        # n = 0, m = 0:
        self.assertAlmostEqual(vmec.vmec_input.rbc[101, 0], 1.3782)

        # n = 0, m = 1:
        self.assertAlmostEqual(vmec.vmec_input.zbs[101, 1], 4.6465E-01)

        # n = 1, m = 1:
        self.assertAlmostEqual(vmec.vmec_input.zbs[102, 1], 1.6516E-01)
Example #7
0
    def run(self):
        """
        Run VMEC, if ``need_to_run_code`` is ``True``.
        """
        if not self.need_to_run_code:
            logger.info("run() called but no need to re-run VMEC.")
            return
        logger.info("Preparing to run VMEC.")
        # Transfer values from Parameters to VMEC's fortran modules:
        vi = vmec.vmec_input  # Shorthand
        # Convert boundary to RZFourier if needed:
        boundary_RZFourier = self.boundary.to_RZFourier()
        # VMEC does not allow mpol or ntor above 101:
        if vi.mpol > 101:
            raise ValueError("VMEC does not allow mpol > 101")
        if vi.ntor > 101:
            raise ValueError("VMEC does not allow ntor > 101")
        vi.rbc[:, :] = 0
        vi.zbs[:, :] = 0
        mpol_capped = np.min([boundary_RZFourier.mpol, 101])
        ntor_capped = np.min([boundary_RZFourier.ntor, 101])
        # Transfer boundary shape data from the surface object to VMEC:
        for m in range(mpol_capped + 1):
            for n in range(-ntor_capped, ntor_capped + 1):
                vi.rbc[101 + n, m] = boundary_RZFourier.get_rc(m, n)
                vi.zbs[101 + n, m] = boundary_RZFourier.get_zs(m, n)

        # Set axis shape to something that is obviously wrong (R=0) to
        # trigger vmec's internal guess_axis.f to run. Otherwise the
        # initial axis shape for run N will be the final axis shape
        # from run N-1, which makes VMEC results depend slightly on
        # the history of previous evaluations, confusing the finite
        # differencing.
        vi.raxis_cc[:] = 0
        vi.raxis_cs[:] = 0
        vi.zaxis_cc[:] = 0
        vi.zaxis_cs[:] = 0

        self.iter += 1
        input_file = self.input_file + '_{:03d}_{:06d}'.format(
            self.mpi.group, self.iter)
        self.output_file = os.path.join(
            os.getcwd(),
            os.path.basename(input_file).replace('input.', 'wout_') + '.nc')
        mercier_file = os.path.join(
            os.getcwd(),
            os.path.basename(input_file).replace('input.', 'mercier.'))
        jxbout_file = os.path.join(
            os.getcwd(),
            os.path.basename(input_file).replace('input.', 'jxbout_') + '.nc')

        # I should write an input file here.
        logger.info("Calling VMEC reinit().")
        vmec.reinit()

        logger.info("Calling runvmec().")
        self.ictrl[0] = restart_flag + reset_jacdt_flag \
            + timestep_flag + output_flag
        self.ictrl[1] = 0  # ierr
        self.ictrl[2] = 0  # numsteps
        self.ictrl[3] = 0  # ns_index
        self.ictrl[4] = 0  # iseq
        verbose = True
        reset_file = ''
        vmec.runvmec(self.ictrl, input_file, verbose, self.fcomm, reset_file)
        ierr = self.ictrl[1]

        # Deallocate arrays, even if vmec did not converge:
        logger.info("Calling VMEC cleanup().")
        vmec.cleanup(True)

        # See VMEC2000/Sources/General/vmec_params.f for ierr codes.
        # 11 = successful_term_flag.
        # Error codes that are expected to occur due to lack of
        # convergence cause ObjectiveFailure, which the optimizer
        # handles gracefully by treating the point as bad. But the
        # user/developer should know if an error codes arises that
        # should logically never occur, so these codes raise a
        # different exception.
        if ierr in [0, 5]:
            raise RuntimeError(f"runvmec returned an error code that should " \
                               "never occur: ierr={ierr}")
        if ierr != 11:
            raise ObjectiveFailure(f"VMEC did not converge. ierr={ierr}")

        logger.info("VMEC run complete. Now loading output.")
        self.load_wout()
        logger.info("Done loading VMEC output.")

        # Group leaders handle deletion of files:
        if self.mpi.proc0_groups:
            # Delete some files produced by VMEC that we never care
            # about. For some reason the os.remove statements give a 'file
            # not found' error in the CI, hence the try-except blocks.
            try:
                os.remove(mercier_file)
            except FileNotFoundError:
                logger.debug(f'Tried to delete the file {mercier_file} but it was not found')
                raise

            try:
                os.remove(jxbout_file)
            except FileNotFoundError:
                logger.debug(f'Tried to delete the file {jxbout_file} but it was not found')
                raise

            try:
                os.remove("fort.9")
            except FileNotFoundError:
                logger.debug('Tried to delete the file fort.9 but it was not found')

            # If the worker group is not 0, delete all wout files, unless
            # keep_all_files is True:
            if (not self.keep_all_files) and (self.mpi.group > 0):
                os.remove(self.output_file)

            # Delete the previous output file, if desired:
            for filename in self.files_to_delete:
                os.remove(filename)
            self.files_to_delete = []

            # Record the latest output file to delete if we run again:
            if (self.mpi.group == 0) and (self.iter > 0) and (not self.keep_all_files):
                self.files_to_delete.append(self.output_file)

        self.need_to_run_code = False
Example #8
0
    def __init__(self,
                 filename: Union[str, None] = None,
                 mpi: Union[MpiPartition, None] = None,
                 keep_all_files: bool = False):
        if MPI is None:
            raise RuntimeError("mpi4py needs to be installed for running VMEC")
        if vmec is None:
            raise RuntimeError(
                "Running VMEC from simsopt requires VMEC python extension. "
                "Install the VMEC python extension from "
                "https://https://github.com/hiddenSymmetries/VMEC2000")

        if filename is None:
            # Read default input file, which should be in the same
            # directory as this file:
            filename = os.path.join(os.path.dirname(__file__), 'input.default')
            logger.info("Initializing a VMEC object from defaults in "
                        + filename)
        else:
            logger.info("Initializing a VMEC object from file: " + filename)
        self.input_file = filename

        # Get MPI communicator:
        if mpi is None:
            self.mpi = MpiPartition(ngroups=1)
        else:
            self.mpi = mpi
        comm = self.mpi.comm_groups
        self.fcomm = comm.py2f()

        self.ictrl = np.zeros(5, dtype=np.int32)
        self.iter = -1
        self.keep_all_files = keep_all_files
        self.files_to_delete = []
        self.wout = Struct()

        self.ictrl[0] = restart_flag + readin_flag
        self.ictrl[1] = 0  # ierr
        self.ictrl[2] = 0  # numsteps
        self.ictrl[3] = 0  # ns_index
        self.ictrl[4] = 0  # iseq
        verbose = True
        reset_file = ''
        logger.info('About to call runvmec to readin')
        vmec.runvmec(self.ictrl, filename, verbose, self.fcomm, reset_file)
        ierr = self.ictrl[1]
        logger.info('Done with runvmec. ierr={}. Calling cleanup next.'.format(ierr))
        # Deallocate arrays allocated by VMEC's fixaray():
        vmec.cleanup(False)
        if ierr != 0:
            raise RuntimeError("Failed to initialize VMEC from input file {}. "
                               "error code {}".format(filename, ierr))

        objstr = " for Vmec " + str(hex(id(self)))

        # Create an attribute for each VMEC input parameter in VMEC's fortran
        # modules,
        self.indata = vmec.vmec_input  # Shorthand
        vi = vmec.vmec_input  # Shorthand
        # A vmec object has mpol and ntor attributes independent of
        # the boundary. The boundary surface object is initialized
        # with mpol and ntor values that match those of the vmec
        # object, but the mpol/ntor values of either the vmec object
        # or the boundary surface object can be changed independently
        # by the user.
        self.boundary = SurfaceRZFourier(nfp=vi.nfp,
                                         stellsym=not vi.lasym,
                                         mpol=vi.mpol,
                                         ntor=vi.ntor)
        self.free_boundary = bool(vi.lfreeb)

        # Transfer boundary shape data from fortran to the ParameterArray:
        for m in range(vi.mpol + 1):
            for n in range(-vi.ntor, vi.ntor + 1):
                self.boundary.rc[m, n + vi.ntor] = vi.rbc[101 + n, m]
                self.boundary.zs[m, n + vi.ntor] = vi.zbs[101 + n, m]
        # Handle a few variables that are not Parameters:
        self.depends_on = ["boundary"]
        self.need_to_run_code = True

        self.fixed = np.full(len(self.get_dofs()), True)
        self.names = ['delt', 'tcon0', 'phiedge', 'curtor', 'gamma']
Example #9
0
    def test_regression(self):
        """
        Read in multiple input files, running VMEC various numbers of
        times in between.  Several output quantities are compared to
        known reference values.  This test case covers both cases in
        which VMEC converges and cases in which it does not, due to an
        unachievably small ftol.  This test case includes several
        geometries, covering both axisymmetry and non-axisymmetry, and
        it covers both stellarator-symmetry and
        non-stellarator-symmetry.
        """
        files = [
            'circular_tokamak', 'up_down_asymmetric_tokamak', 'li383_low_res',
            'LandremanSenguptaPlunk_section5p3_low_res'
        ]

        #file_indices = list(range(len(files)))

        for nrun in range(4):
            for jfile in range(len(files)):
                filename = os.path.join(os.path.dirname(__file__),
                                        'input.' + files[jfile])

                # Read in the input file:
                ictrl[:] = 0
                ictrl[0] = restart_flag + readin_flag
                logger.info("Calling runvmec to read input file {}. ictrl={} comm={}" \
                            .format(filename, ictrl, fcomm))
                vmec.runvmec(ictrl, filename, verbose, fcomm, reset_file)
                self.assertEqual(ictrl[1], 0)
                ftol_array_orig = np.copy(vmec.vmec_input.ftol_array)

                # Deallocate arrays:
                logger.info("About to cleanup(False)")
                vmec.cleanup(False)  # False because we have not time-stepped.

                for jrun in range(nrun):
                    # Set axis to 0 to force VMEC to recompute its
                    # initial guess for the axis. This way each run of
                    # the code should produce independent results, no
                    # matter how many times the code is run.
                    vmec.vmec_input.raxis_cc = 0
                    vmec.vmec_input.raxis_cs = 0
                    vmec.vmec_input.zaxis_cc = 0
                    vmec.vmec_input.zaxis_cs = 0

                    # Cover both cases in which VMEC converges and
                    # cases in which it does not. To do this, for
                    # every other run we set ftol to a value so small
                    # that it is unattainable.
                    should_converge = (np.mod(jrun, 2) == 0)
                    vmec.vmec_input.ftol_array = ftol_array_orig
                    if not should_converge:
                        vmec.vmec_input.ftol_array[:3] = 1.0e-30

                    logger.info("About to reinit. nrun={} jfile={} jrun={} should_converge={}" \
                                .format(nrun, jfile, jrun, should_converge))
                    logger.info("ftol_array_orig: {}".format(
                        ftol_array_orig[:5]))
                    logger.info("ftol_array: {}".format(
                        vmec.vmec_input.ftol_array[:5]))
                    vmec.reinit()

                    # Time-step:
                    ictrl[:] = 0
                    ictrl[
                        0] = restart_flag + reset_jacdt_flag + timestep_flag + output_flag
                    logger.info(
                        "Calling runvmec to timestep. ictrl={} comm={}".format(
                            ictrl, fcomm))
                    vmec.runvmec(ictrl, filename, verbose, fcomm, reset_file)

                    # Expected return codes for ictrl[1], from vmec_params.f:
                    # 2 = more_iter_flag
                    # 11 = successful_term_flag
                    if should_converge:
                        self.assertEqual(ictrl[1], 11)
                    else:
                        self.assertEqual(ictrl[1], 2)

                    # Regression tests: compare output to reference files.
                    if should_converge:
                        # Before trying to read the wout file, make sure master is
                        # done writing it.
                        MPI.COMM_WORLD.Barrier()

                        # New wout output file is in the current
                        # working directory, whereas the reference
                        # wout file is in a fixed directory.
                        wout_file = 'wout_' + files[jfile] + '.nc'
                        reference_file = os.path.join(
                            os.path.dirname(__file__),
                            'wout_' + files[jfile] + '_reference.nc')

                        ierr = 0
                        logger.info(
                            'About to read output file {}'.format(wout_file))
                        f1 = netcdf.netcdf_file(wout_file, mmap=False)
                        #vmec.read_wout_mod.read_wout_file(wout_file, ierr)
                        #self.assertEqual(ierr, 0)

                        logger.info(
                            'About to read reference output file {}'.format(
                                reference_file))
                        f2 = netcdf.netcdf_file(reference_file, mmap=False)

                        compare(f1, f2, 'iotaf')
                        compare(f1, f2, 'rmnc')
                        compare(f1, f2, 'zmns')
                        compare(f1, f2, 'lmns')
                        compare(f1, f2, 'bmnc')
                        #np.testing.assert_allclose(vmec.read_wout_mod.iotaf, f2.variables['iotaf'][()])

                        f1.close()
                        f2.close()

                    # Deallocate arrays:
                    logger.info("About to cleanup(True). nrun={} jfile={} jrun={}" \
                                .format(nrun, jfile, jrun))
                    vmec.cleanup(True)
Example #10
0
    def run(self,
            mode='main',
            ier=0,
            numsteps=-1,
            ns_index=-1,
            iseq=0,
            input_file=None,
            verbose=None,
            comm=None,
            reset_file='',
            **kwargs):
        """Interface for Fortran subroutine runvmec in Sources/TimeStep/runvmec.f

        Args:
            mode (str): The running mode of VMEC. It should be one of the
                following options,
                ('all', 'input', 'output', 'main'). (default: 'main').
            ier (int): Flag for error condition. (default: 0).
            numsteps (int): Number time steps to evolve the equilibrium.
                Iterations will stop EITHER if numsteps > 0 and when the
                number of vmec iterations exceeds numsteps; OR if the ftol
                condition is satisfied, whichever comes first. The
                timestep_flag must be set (in ictrl_flag) for this to be
                in effect. If numsteps <= 0, then vmec will choose
                consecutive (and increasing) values from the ns_array
                until ftol is satisfied on each successive multi-grid.
                (default: -1).
            ns_index (int): if > 0 on entry, specifies index (in ns_array)
                of the radial grid to be used for the present iteration
                phase. If ns_index <= 0, vmec will use the previous value
                of this index (if the ftol condition was not satisfied
                during the last call to runvmec) or the next value of this
                index, and it will iterate through each successive
                non-zero member of the ns_array until ftol-convergence
                occurs on each multigrid.
                (default: -1).
            iseq (int): specifies a unique sequence label for identifying
                output files in a sequential vmec run.
                (default: 0).
            input_file (str): Filename for VMEC input namelist.
                (default: None -> self.input_file).
            verbose (bool): If wants scree outputs.
                (default: None -> self.verbose).
            comm (int): MPI_Communicater, should be converted to Fortran
                format using MPI.py2f().
                (default: None -> self.comm).
            reset_file (str): Filename for reset runs. (default: '').

        Returns:
            None
        """
        # check arguments
        mode = mode.lower()
        assert mode in run_modes, ("Unsupported running mode. Should "
                                   "be one of [{:}].").format(','.join(
                                       run_modes.keys()))
        assert ier == 0, "Error flag should be zero at input."
        if input_file is None:
            input_file = self.input_file + '_{:06d}'.format(self.iter)
        else:
            if 'input.' not in input_file:
                input_file = 'input.' + input_file
        self.output_file = input_file.replace('input.', 'wout_') + '.nc'
        if verbose is None:
            verbose = self.verbose
        if comm is None:
            comm = self.comm
        # prepare Fortran arguments
        self.ictrl[0] = run_modes[mode]
        self.ictrl[1] = ier
        self.ictrl[2] = numsteps
        self.ictrl[3] = ns_index
        self.ictrl[4] = iseq
        # run VMEC
        # print("before: ", self.ictrl)
        vmec.runvmec(self.ictrl, input_file, verbose, comm, reset_file)
        self.iter += 1
        self.success = self.ictrl[1] in self.success_code
        return self.success