From 62b88a052f191b1c253555807c0fae5c2ad7bae9 Mon Sep 17 00:00:00 2001 From: d-burg Date: Tue, 20 Feb 2024 18:03:27 -0500 Subject: [PATCH 1/5] TokaMaker: Add functionality to compute loop voltage (#18) Add function to compute required loop voltage to sustain an equilibrium with given resistivity and non-inductive current profiles --- src/physics/grad_shaf.F90 | 6 +- src/physics/grad_shaf_util.F90 | 65 +++++++++ src/python/OpenFUSIONToolkit/TokaMaker.py | 158 +++++++++++++++------- src/python/wrappers/tokamaker_f.F90 | 25 +++- 4 files changed, 196 insertions(+), 58 deletions(-) diff --git a/src/physics/grad_shaf.F90 b/src/physics/grad_shaf.F90 index ea0a752..d70d2ae 100644 --- a/src/physics/grad_shaf.F90 +++ b/src/physics/grad_shaf.F90 @@ -235,8 +235,10 @@ MODULE oft_gs CLASS(oft_matrix), POINTER :: dels_dt => NULL() CLASS(oft_matrix), POINTER :: mrop => NULL() CLASS(oft_matrix), POINTER :: mop => NULL() - CLASS(flux_func), POINTER :: I => NULL() !< - CLASS(flux_func), POINTER :: P => NULL() !< + CLASS(flux_func), POINTER :: I => NULL() !< F*F' flux function + CLASS(flux_func), POINTER :: P => NULL() !< Pressure flux function + CLASS(flux_func), POINTER :: eta => NULL() !< Resistivity flux function + CLASS(flux_func), POINTER :: I_NI => NULL() !< Non-inductive F*F' flux function PROCEDURE(region_eta_set), NOPASS, POINTER :: set_eta => NULL() CONTAINS ! diff --git a/src/physics/grad_shaf_util.F90 b/src/physics/grad_shaf_util.F90 index b2f20fc..8ed5012 100644 --- a/src/physics/grad_shaf_util.F90 +++ b/src/physics/grad_shaf_util.F90 @@ -653,6 +653,71 @@ subroutine gs_comp_globals(self,itor,centroid,vol,pvol,dflux,tflux,li) CALL psi_geval%delete end subroutine gs_comp_globals !--------------------------------------------------------------------------- +!> Compute plasma loop voltage +!--------------------------------------------------------------------------- +subroutine gs_calc_vloop(self,vloop) +class(gs_eq), intent(inout) :: self !< G-S object +real(8), intent(out) :: vloop !< loop voltage +type(oft_lag_brinterp), target :: psi_eval +type(oft_lag_bginterp), target :: psi_geval +real(8) :: itor_loc !< local toroidal current in integration +real(8) :: itor !< toroidal current +real(8) :: j_NI_loc !< local non-inductive current in integration +real(8) :: I_NI !< non-inductive F*F' +real(8) :: eta_jsq !< eta*j_NI**2 +real(8) :: goptmp(3,3) !< needs docs +real(8) :: v !< volume +real(8) :: pt(3) !< radial coordinate +real(8) :: curr_cent(2) !< needs docs +real(8) :: psitmp(1) !< magnetic flux coordinate +real(8) :: gpsitmp(3) !< needs docs +integer(4) :: i,m +!--- +psi_eval%u=>self%psi +CALL psi_eval%setup +CALL psi_geval%shared_setup(psi_eval) +!--- +eta_jsq = 0.d0 +I_NI = 0.d0 +itor = 0.d0 +vloop = 0.d0 +!!$omp parallel do private(m,goptmp,v,psitmp,gpsitmp,pt,itor_loc) & +!!$omp reduction(+:itor) reduction(+:vol) & +do i=1,smesh%nc + IF(smesh%reg(i)/=1)CYCLE + do m=1,oft_blagrange%quad%np + call smesh%jacobian(i,oft_blagrange%quad%pts(:,m),goptmp,v) + call psi_eval%interp(i,oft_blagrange%quad%pts(:,m),goptmp,psitmp) + IF(psitmp(1) Needs Docs diff --git a/src/python/OpenFUSIONToolkit/TokaMaker.py b/src/python/OpenFUSIONToolkit/TokaMaker.py index b56b9c7..56eead8 100644 --- a/src/python/OpenFUSIONToolkit/TokaMaker.py +++ b/src/python/OpenFUSIONToolkit/TokaMaker.py @@ -71,7 +71,7 @@ class tokamaker_settings_struct(c_struct): # G-S load flux functions (f_file,f_offset,p_file) tokamaker_load_profiles = ctypes_subroutine(oftpy_lib.tokamaker_load_profiles, - [c_char_p, c_double, c_char_p]) + [c_char_p, c_double, c_char_p, c_char_p, c_char_p]) # G-S run function tokamaker_run = ctypes_subroutine(oftpy_lib.tokamaker_run, @@ -142,6 +142,10 @@ class tokamaker_settings_struct(c_struct): tokamaker_get_globals = ctypes_subroutine(oftpy_lib.tokamaker_get_globals, # (Itor,centroid,vol,pvol,dflux,tflux,li) [c_double_ptr, ctypes_numpy_array(numpy.float64,1), c_double_ptr, c_double_ptr, c_double_ptr, c_double_ptr, c_double_ptr]) +# +tokamaker_gs_calc_vloop = ctypes_subroutine(oftpy_lib.tokamaker_gs_calc_vloop, # (V_loop) + [c_double_ptr]) + # tokamaker_get_profs = ctypes_subroutine(oftpy_lib.tokamaker_get_profs, # (npsi,psi_in,f,fp,p,pp) [c_int,ctypes_numpy_array(numpy.float64,1), ctypes_numpy_array(numpy.float64,1), @@ -301,6 +305,51 @@ def read_2d(fid, n, m): eqdsk_obj['rzlim'] = read_2d(fid, eqdsk_obj['nlim'], 2) return eqdsk_obj +def create_prof_file(self, filename, profile_dict, name): + '''! Create profile input file to be read by load_profiles() + + @param filename Name of input file, see options in set_profiles() + @param profile_dict Dictionary object containing profile values ['y'] and sampled locations + in normalized Psi ['x'] + @param filename Name of input quantity, see options in set_profiles() + ''' + file_lines = [profile_dict['type']] + if profile_dict['type'] == 'flat': + pass + elif profile_dict['type'] == 'linterp': + x = profile_dict.get('x',None) + if x is None: + raise KeyError('No array "x" for piecewise linear profile.') + else: + x = numpy.array(x.copy()) + y = profile_dict.get('y',None) + if y is None: + raise KeyError('No array "y" for piecewise linear profile.') + else: + y = numpy.array(y.copy()) + if numpy.min(numpy.diff(x)) < 0.0: + raise ValueError("psi values in {0} profile must be monotonically increasing".format(name)) + if (x[0] < 0.0) or (x[-1] > 1.0): + raise ValueError("Invalid psi values in {0} profile ({1}, {2})".format(name, x[0], x[-1])) + if self.psi_convention == 0: + x = 1.0 - x + sort_inds = x.argsort() + x = x[sort_inds] + y = y[sort_inds] + elif self.psi_convention == 1: + pass + else: + raise ValueError('Unknown convention type, must be 0 (tokamak) or 1 (spheromak)') + file_lines += [ + "{0} {1}".format(x.shape[0]-1, y[0]), + "{0}".format(" ".join(["{0}".format(val) for val in x[1:]])), + "{0}".format(" ".join(["{0}".format(val) for val in y[1:]])) + ] + else: + raise KeyError('Invalid profile type ("flat", "linterp")') + with open(filename, 'w+') as fid: + fid.write("\n".join(file_lines)) + oft_in_template = """&runtime_options debug={DEBUG_LEVEL} / @@ -657,77 +706,64 @@ def init_psi(self, r0=-1.0, z0=0.0, a=0.0, kappa=0.0, delta=0.0): tokamaker_init_psi(c_double(r0),c_double(z0),c_double(a),c_double(kappa),c_double(delta),ctypes.byref(error_flag)) return error_flag.value - def load_profiles(self, f_file='f_prof.in', foffset=None, p_file='p_prof.in'): + def load_profiles(self, f_file='f_prof.in', foffset=None, p_file='p_prof.in', eta_file='eta_prof.in', f_NI_file='f_NI_prof.in'): r'''! Load flux function profiles (\f$F*F'\f$ and \f$P'\f$) from files @param f_file File containing \f$F*F'\f$ (or \f$F'\f$ if `mode=0`) definition @param foffset Value of \f$F0=R0*B0\f$ @param p_file File containing \f$P'\f$ definition + @param eta_file File containing $\eta$ definition + @param f_NI_file File containing non-inductive \f$F*F'\f$ definition ''' if foffset is not None: self._F0 = foffset - tokamaker_load_profiles(c_char_p(f_file.encode()),c_double(self._F0),c_char_p(p_file.encode())) + tokamaker_load_profiles(c_char_p(f_file.encode()),c_double(self._F0),c_char_p(p_file.encode()),c_char_p(eta_file.encode()),c_char_p(f_NI_file.encode())) - def set_profiles(self, ffp_prof=None, foffset=None, pp_prof=None): + def set_profiles(self, ffp_prof=None, foffset=None, pp_prof=None, ffp_NI_prof=None): r'''! Set flux function profiles (\f$F*F'\f$ and \f$P'\f$) using a piecewise linear definition - Arrays should have the form array[i,:] = (\f$\hat{\psi}_i\f$, \f$f(\hat{\psi}_i)\f$) and span - \f$\hat{\psi}_i = [0,1]\f$. - - @param ffp_prof Values defining \f$F*F'\f$ (or \f$F'\f$ if `mode=0`) [:,2] + @param ffp_prof Dictionary object containing FF' profile ['y'] and sampled locations + in normalized Psi ['x'] @param foffset Value of \f$F0=R0*B0\f$ - @param pp_prof Values defining \f$P'\f$ [:,2] + @param pp_prof Dictionary object containing P' profile ['y'] and sampled locations + in normalized Psi ['x'] + @param ffp_NI_prof Dictionary object containing non-inductive FF' profile ['y'] and sampled locations + in normalized Psi ['x'] ''' - def create_prof_file(filename, profile_dict, name): - file_lines = [profile_dict['type']] - if profile_dict['type'] == 'flat': - pass - elif profile_dict['type'] == 'linterp': - x = profile_dict.get('x',None) - if x is None: - raise KeyError('No array "x" for piecewise linear profile.') - else: - x = numpy.array(x.copy()) - y = profile_dict.get('y',None) - if y is None: - raise KeyError('No array "y" for piecewise linear profile.') - else: - y = numpy.array(y.copy()) - if numpy.min(numpy.diff(x)) < 0.0: - raise ValueError("psi values in {0} profile must be monotonically increasing".format(name)) - if (x[0] < 0.0) or (x[-1] > 1.0): - raise ValueError("Invalid psi values in {0} profile ({1}, {2})".format(name, x[0], x[-1])) - if self.psi_convention == 0: - x = 1.0 - x - sort_inds = x.argsort() - x = x[sort_inds] - y = y[sort_inds] - elif self.psi_convention == 1: - pass - else: - raise ValueError('Unknown convention type, must be 0 (tokamak) or 1 (spheromak)') - file_lines += [ - "{0} {1}".format(x.shape[0]-1, y[0]), - "{0}".format(" ".join(["{0}".format(val) for val in x[1:]])), - "{0}".format(" ".join(["{0}".format(val) for val in y[1:]])) - ] - else: - raise KeyError('Invalid profile type ("flat", "linterp")') - with open(filename, 'w+') as fid: - fid.write("\n".join(file_lines)) - # ffp_file = 'none' if ffp_prof is not None: ffp_file = 'tokamaker_f.prof' - create_prof_file(ffp_file, ffp_prof, "F*F'") + create_prof_file(self, ffp_file, ffp_prof, "F*F'") pp_file = 'none' if pp_prof is not None: pp_file = 'tokamaker_p.prof' - create_prof_file(pp_file, pp_prof, "P'") + create_prof_file(self, pp_file, pp_prof, "P'") + eta_file = 'none' + ffp_NI_file = 'none' + if ffp_NI_prof is not None: + ffp_NI_file = 'tokamaker_ffp_NI.prof' + create_prof_file(self, ffp_NI_file, ffp_NI_prof, "ffp_NI") if foffset is not None: self._F0 = foffset - self.load_profiles(ffp_file,foffset,pp_file) - + self.load_profiles(ffp_file,foffset,pp_file,eta_file,ffp_NI_file) + + def set_resistivity(self, eta_prof=None): + r'''! Set flux function profile $\eta$ using a piecewise linear definition + + Arrays should have the form array[i,:] = (\f$\hat{\psi}_i\f$, \f$f(\hat{\psi}_i)\f$) and span + \f$\hat{\psi}_i = [0,1]\f$. + + @param eta_prof Values defining $\eta$ [:,2] + ''' + ffp_file = 'none' + pp_file = 'none' + eta_file = 'none' + if eta_prof is not None: + eta_file = 'tokamaker_eta.prof' + create_prof_file(self, eta_file, eta_prof, "eta'") + ffp_NI_file = 'none' + self.load_profiles(ffp_file,None,pp_file,eta_file,ffp_NI_file) + def solve(self, vacuum=False): '''! Solve G-S equation with specified constraints, profiles, etc.''' error_flag = c_int() @@ -1024,7 +1060,25 @@ def get_globals(self): tokamaker_get_globals(ctypes.byref(Ip),centroid,ctypes.byref(vol),ctypes.byref(pvol), ctypes.byref(dflux),ctypes.byref(tflux),ctypes.byref(Li)) return Ip.value, centroid, vol.value, pvol.value, dflux.value, tflux.value, Li.value - + + def calc_loopvoltage(self): + r'''! Get plasma loop voltage + + @param eta Dictionary object containing resistivity profile ['y'] and sampled locations + in normalized Psi ['x'] + @param ffp_NI Dictionary object containing non-inductive FF' profile ['y'] and sampled locations + in normalized Psi ['x'] + @result Vloop [Volts] + ''' + V_loop = c_double() + + tokamaker_gs_calc_vloop(ctypes.byref(V_loop)) + + if V_loop.value < 0.: + raise ValueError('eta array not specified') + else: + return V_loop.value + def get_profiles(self,psi=None,psi_pad=1.E-8,npsi=50): r'''! Get G-S source profiles diff --git a/src/python/wrappers/tokamaker_f.F90 b/src/python/wrappers/tokamaker_f.F90 index 4ae0034..628fe74 100644 --- a/src/python/wrappers/tokamaker_f.F90 +++ b/src/python/wrappers/tokamaker_f.F90 @@ -24,7 +24,7 @@ MODULE tokamaker_f USE oft_gs, ONLY: gs_eq, gs_save_fields, gs_save_fgrid, gs_setup_walls, gs_save_prof, & gs_fixed_vflux, gs_load_regions, gs_get_qprof, gs_trace_surf USE oft_gs_util, ONLY: gs_save, gs_load, gs_analyze, gs_comp_globals, gs_save_eqdsk, & - gs_profile_load, sauter_fc + gs_profile_load, sauter_fc, gs_calc_vloop USE oft_gs_td, ONLY: setup_gs_td, step_gs_td, eig_gs_td USE oft_base_f, ONLY: copy_string, copy_string_rev, oftpy_init IMPLICIT NONE @@ -207,9 +207,11 @@ END SUBROUTINE tokamaker_setup !------------------------------------------------------------------------------ !> Needs docs !------------------------------------------------------------------------------ -SUBROUTINE tokamaker_load_profiles(f_file,f_offset,p_file) BIND(C,NAME="tokamaker_load_profiles") -CHARACTER(KIND=c_char), INTENT(in) :: f_file(80) !< Needs docs -CHARACTER(KIND=c_char), INTENT(in) :: p_file(80) !< Needs docs +SUBROUTINE tokamaker_load_profiles(f_file,f_offset,p_file,eta_file,f_NI_file) BIND(C,NAME="tokamaker_load_profiles") +CHARACTER(KIND=c_char), INTENT(in) :: f_file(80) !< F*F' prof.in file +CHARACTER(KIND=c_char), INTENT(in) :: p_file(80) !< P' prof.in file +CHARACTER(KIND=c_char), INTENT(in) :: eta_file(80) !< Resistivity (eta) prof.in file +CHARACTER(KIND=c_char), INTENT(in) :: f_NI_file(80) !< Non-inductive F*F' prof.in file REAL(c_double), VALUE, INTENT(in) :: f_offset !< Needs docs CHARACTER(LEN=80) :: tmp_str CALL copy_string_rev(f_file,tmp_str) @@ -217,6 +219,10 @@ SUBROUTINE tokamaker_load_profiles(f_file,f_offset,p_file) BIND(C,NAME="tokamake IF(f_offset>-1.d98)gs_global%I%f_offset=f_offset CALL copy_string_rev(p_file,tmp_str) IF(TRIM(tmp_str)/='none')CALL gs_profile_load(tmp_str,gs_global%P) +CALL copy_string_rev(eta_file,tmp_str) +IF(TRIM(tmp_str)/='none')CALL gs_profile_load(tmp_str,gs_global%eta) +CALL copy_string_rev(f_NI_file,tmp_str) +IF(TRIM(tmp_str)/='none')CALL gs_profile_load(tmp_str,gs_global%I_NI) END SUBROUTINE tokamaker_load_profiles !------------------------------------------------------------------------------ !> Needs docs @@ -471,6 +477,17 @@ END SUBROUTINE tokamaker_get_globals !------------------------------------------------------------------------------ !> Needs docs !------------------------------------------------------------------------------ +SUBROUTINE tokamaker_gs_calc_vloop(vloop) BIND(C,NAME="tokamaker_gs_calc_vloop") +REAL(c_double), INTENT(out) :: vloop +IF(.NOT.ASSOCIATED(gs_global%eta))THEN + vloop=-1.d0 + RETURN +END IF +CALL gs_calc_vloop(gs_global,vloop) +END SUBROUTINE tokamaker_gs_calc_vloop +!------------------------------------------------------------------------------ +!> Needs docs +!------------------------------------------------------------------------------ SUBROUTINE tokamaker_get_profs(npsi,psi_in,f,fp,p,pp) BIND(C,NAME="tokamaker_get_profs") INTEGER(c_int), VALUE, INTENT(in) :: npsi !< Needs docs REAL(c_double), INTENT(in) :: psi_in(npsi) !< Needs docs From 9672fc20588626b96173fe8864155b869b281f2e Mon Sep 17 00:00:00 2001 From: Chris Hansen Date: Thu, 22 Feb 2024 21:19:53 -0500 Subject: [PATCH 2/5] Add support for code coverage and MPI CI tests (#33) - Add code coverage GitHub actions workflow (for Codecov.io) - Increase timeout to 20 minutes on MPICH build - Add naming to more steps in CI workflow --- .../{build_test.yaml => ci_build.yaml} | 28 ++++-- .github/workflows/cov_build.yaml | 94 +++++++++++++++++++ README.md | 1 + codecov.yml | 16 ++++ src/CMakeLists.txt | 17 +++- src/ext_libs/dlsode.f | 4 +- src/tests/CMakeLists.txt | 7 ++ src/tests/fem/test_blag.py | 8 ++ src/tests/fem/test_h0.py | 2 + src/tests/fem/test_h1.py | 2 + src/tests/fem/test_hcurl.py | 2 + src/tests/fem/test_hcurl_sop.py | 4 + src/tests/fem/test_lag.py | 2 + src/tests/fem/test_lag_2d.py | 8 ++ src/tests/fem/test_vlag.py | 2 + src/tests/grid/test_cubit.py | 17 ++++ src/tests/grid/test_gmsh.py | 3 + src/tests/grid/test_mapping.py | 4 + src/tests/grid/test_quad.py | 1 + src/tests/grid/test_t3d.py | 4 + src/tests/lin_alg/test_arpack.py | 3 + src/tests/lin_alg/test_native_bjacobi.py | 4 + src/tests/lin_alg/test_solver_xml.py | 4 + src/tests/physics/conftest.py | 1 + src/tests/physics/test_TokaMaker.py | 4 + src/tests/physics/test_alfven.py | 4 + src/tests/physics/test_alfven_lag.py | 4 + src/tests/physics/test_sound.py | 4 + src/tests/physics/test_sound_lag.py | 4 + src/tests/physics/test_taylor.py | 2 + src/tests/physics/test_taylor_inj.py | 2 + src/tests/physics/test_thin_wall.py | 9 ++ src/utilities/build_libs.py | 9 +- 33 files changed, 265 insertions(+), 15 deletions(-) rename .github/workflows/{build_test.yaml => ci_build.yaml} (71%) create mode 100644 .github/workflows/cov_build.yaml create mode 100644 codecov.yml diff --git a/.github/workflows/build_test.yaml b/.github/workflows/ci_build.yaml similarity index 71% rename from .github/workflows/build_test.yaml rename to .github/workflows/ci_build.yaml index d51e9c3..cac3b02 100644 --- a/.github/workflows/build_test.yaml +++ b/.github/workflows/ci_build.yaml @@ -9,16 +9,18 @@ on: jobs: build: - name: ${{ matrix.config.name }} + name: ${{ format('{0} ({1})', matrix.config.name, matrix.parallel) }} runs-on: ${{ matrix.config.os }} env: CC: ${{ matrix.config.cc }} CXX: ${{ matrix.config.cxx }} FC: ${{ matrix.config.fc }} + OMP_NUM_THREADS: 2 strategy: fail-fast: false matrix: + parallel: ["openmp", "mpi_openmp"] config: - { name: "Ubuntu 20.04 GCC 10", @@ -42,7 +44,8 @@ jobs: } steps: - - uses: actions/checkout@v4 + - name: Checkout repo + uses: actions/checkout@v4 - name: Install prerequisites run: ${{ matrix.config.pip }} install pytest numpy scipy h5py triangle @@ -52,10 +55,10 @@ jobs: uses: actions/cache@v4 with: path: libs - key: ${{ runner.os }}-build-${{ hashFiles('src/utilities/build_libs.py') }} + key: ${{ matrix.config.os }}-build-${{ matrix.parallel }}-${{ hashFiles('src/utilities/build_libs.py') }} - - if: ${{ steps.cache-ext-libs.outputs.cache-hit != 'true' }} - name: Create build dir + - name: Create build dir + if: ${{ steps.cache-ext-libs.outputs.cache-hit != 'true' }} run: mkdir libs - name: Check compilers @@ -64,14 +67,25 @@ jobs: $CXX --version $FC --version - - name: Build external + - name: Build external (OpenMP) + if: ${{ matrix.parallel == 'openmp' }} shell: bash working-directory: libs run: > ${{ matrix.config.python }} ../src/utilities/build_libs.py --no_dl_progress --nthread=2 --ref_blas --build_umfpack=1 --build_arpack=1 --oft_build_tests=1 + + - name: Build external (MPI+OpenMP) + if: ${{ matrix.parallel == 'mpi_openmp' }} + shell: bash + working-directory: libs + run: > + ${{ matrix.config.python }} ../src/utilities/build_libs.py --no_dl_progress --nthread=2 --ref_blas + --build_mpi=1 --build_umfpack=1 --build_superlu=1 --build_superlu_dist=1 + --build_arpack=1 --oft_build_tests=1 - - uses: actions/upload-artifact@v4 + - name: Upload library failure log + uses: actions/upload-artifact@v4 if: failure() with: name: Library failure log diff --git a/.github/workflows/cov_build.yaml b/.github/workflows/cov_build.yaml new file mode 100644 index 0000000..1dfec9b --- /dev/null +++ b/.github/workflows/cov_build.yaml @@ -0,0 +1,94 @@ +name: Open FUSION Toolkit code coverage build + +on: + push: + branches: + - main + pull_request: + types: [opened, reopened, synchronize] + +jobs: + build: + name: Code coverage + runs-on: ubuntu-22.04 + env: + CC: gcc-13 + CXX: g++-13 + FC: gfortran-13 + OMP_NUM_THREADS: 1 + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Install prerequisites + run: | + sudo apt -y install lcov + pip3 install pytest numpy scipy h5py triangle + + - name: Cache external libraries + id: cache-ext-libs + uses: actions/cache@v4 + with: + path: libs + key: cov_build-${{ hashFiles('src/utilities/build_libs.py') }} + + - name: Create build dir + if: ${{ steps.cache-ext-libs.outputs.cache-hit != 'true' }} + run: mkdir libs + + - name: Check compilers + run: | + $CC --version + $CXX --version + $FC --version + + - name: Build external + shell: bash + working-directory: libs + run: > + python3 ../src/utilities/build_libs.py --no_dl_progress --nthread=2 + --ref_blas --build_mpi=1 --build_umfpack=1 --build_superlu=1 --build_superlu_dist=1 + --build_arpack=1 --oft_build_tests=1 --oft_build_coverage + + - name: Upload library failure log + uses: actions/upload-artifact@v4 + if: failure() + with: + name: Library failure log + path: libs/build/build_error.log + + - name: Remove external build directory + shell: bash + working-directory: libs + run: rm -rf build + + - name: Configure OFT + shell: bash + working-directory: libs + run: bash config_cmake.sh + + - name: Build OFT + shell: bash + working-directory: libs/build_release + run: make + + - name: Test OFT + shell: bash + working-directory: libs/build_release + run: make test_cov + + - name: Generate coverage report + shell: bash + working-directory: libs/build_release + run: | + lcov -c --directory . --output-file main_coverage.info --gcov-tool gcov-13 + lcov --ignore-errors unused -r main_coverage.info -o cleaned_coverage.txt '/opt/*' '/usr/*' + + - name: Upload coverage report to Codecov + uses: codecov/codecov-action@v4 + with: + file: libs/build_release/cleaned_coverage.txt + token: ${{ secrets.CODECOV_TOKEN }} + slug: hansec/OpenFUSIONToolkit + verbose: true \ No newline at end of file diff --git a/README.md b/README.md index 719933a..e12b4b1 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ Open Flexible Unstructured Simulation Infrastructure with Open Numerics (FUSION) ===================================== ![CI status](https://github.com/hansec/OpenFUSIONToolkit/actions/workflows/build_test.yaml/badge.svg?branch=main) +[![codecov](https://codecov.io/gh/hansec/OpenFUSIONToolkit/graph/badge.svg?token=GG282HKNAO)](https://codecov.io/gh/hansec/OpenFUSIONToolkit) [![DOI](https://zenodo.org/badge/710415041.svg)](https://zenodo.org/doi/10.5281/zenodo.10306801) The Open FUSION Toolkit (OFT) is a suite of modeling tools, and their underlying finite element diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..b55297d --- /dev/null +++ b/codecov.yml @@ -0,0 +1,16 @@ +coverage: + # Set ranges for status displays + range: 50..80 + round: down + precision: 1 + # Make check informational only (not blocking/failing) + status: + project: + default: + informational: true + patch: + default: + informational: true +# GitHub specific settings +github_checks: + annotations: false # Disable code annotations \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0bf4657..91ae2c8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,6 +17,7 @@ option(OFT_BUILD_DOCS "Build documentation?" OFF) option(OFT_DOCS_ONLY "Build documentation only?" OFF) option(OFT_PACKAGE_BUILD "Perform a destributable package?" OFF) option(OFT_PACKAGE_NIGHTLY "Perform a nightly build and include git hash in package filename?" ON) +option(OFT_COVERAGE "Build with code coverage information?" OFF) # Internal variables set(OFT_SHARED_LIBS CACHE INTERNAL "package_shared_libs") @@ -91,12 +92,20 @@ if ("${CMAKE_Fortran_COMPILER_ID}" MATCHES "Intel") set( EXT_LIBS "-lstdc++" ) endif() elseif("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU") - set( CMAKE_C_FLAGS_DEBUG "-g -O0" ) + if(OFT_COVERAGE) + set( CMAKE_C_FLAGS_DEBUG "-Og --coverage -fprofile-update=atomic -g -Wuninitialized" ) # -fprofile-arcs -ftest-coverage + set( CMAKE_C_FLAGS_RELEASE "-Og --coverage -fprofile-update=atomic -g" ) # -fprofile-arcs -ftest-coverage + set(CMAKE_C_OUTPUT_EXTENSION_REPLACE ON) + set(CMAKE_CXX_OUTPUT_EXTENSION_REPLACE ON) + set(CMAKE_Fortran_OUTPUT_EXTENSION_REPLACE ON) + else() + set( CMAKE_C_FLAGS_DEBUG "-g -O0 -Wuninitialized" ) + set( CMAKE_C_FLAGS_RELEASE "-O2" ) + endif() set( CMAKE_CXX_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG} ) - set( CMAKE_Fortran_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG} ) - set( CMAKE_C_FLAGS_RELEASE "-O2" ) set( CMAKE_CXX_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE} ) - set( CMAKE_Fortran_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE} ) + set( CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Werror=line-truncation -Wconversion" ) + set( CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Werror=line-truncation -Wconversion" ) if(NOT OFT_PACKAGE_BUILD) set( CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -fcheck=all" ) endif() diff --git a/src/ext_libs/dlsode.f b/src/ext_libs/dlsode.f index 81cd502..f381f45 100644 --- a/src/ext_libs/dlsode.f +++ b/src/ext_libs/dlsode.f @@ -3714,8 +3714,8 @@ DOUBLE PRECISION FUNCTION DDOT_LSODE (N, DX, INCX, DY, INCY) IF (N .LT. 5) RETURN 40 MP1 = M + 1 DO 50 I = MP1,N,5 - DDOT_LSODE = DDOT_LSODE + DX(I)*DY(I) + DX(I+1)*DY(I+1) + DX(I+2)*DY(I+2) + - 1 DX(I+3)*DY(I+3) + DX(I+4)*DY(I+4) + DDOT_LSODE = DDOT_LSODE + DX(I)*DY(I) + DX(I+1)*DY(I+1) + 1 + DX(I+2)*DY(I+2) + DX(I+3)*DY(I+3) + DX(I+4)*DY(I+4) 50 CONTINUE RETURN C diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 8b34bd8..7b391ec 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -56,3 +56,10 @@ add_custom_command( COMMAND PATH=${OFT_TEST_PATH} OFT_HAVE_MPI=${OFT_MPI_TEST} OFT_DEBUG_TEST=${OFT_DEBUG_TEST} ${OFT_PYTEST_EXE} grid lin_alg fem physics WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/tests ) +# Add "full" testing command to Makefile +add_custom_target(test_cov) +add_custom_command( + TARGET test_cov + COMMAND PATH=${OFT_TEST_PATH} OFT_HAVE_MPI=${OFT_MPI_TEST} OFT_DEBUG_TEST=${OFT_DEBUG_TEST} ${OFT_PYTEST_EXE} -m \"coverage\" grid lin_alg fem physics + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/tests +) diff --git a/src/tests/fem/test_blag.py b/src/tests/fem/test_blag.py index 71076ab..af85f30 100644 --- a/src/tests/fem/test_blag.py +++ b/src/tests/fem/test_blag.py @@ -81,6 +81,7 @@ def single_level(nlevels,order,exp_its,exp_error,grid_type=1,mpi=False,petsc_fla @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r2_p1(petsc_flag): single_level(2, 1, 1, 2.3437500000000000E-002, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("mpi", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r3_p1(mpi, petsc_flag): @@ -91,6 +92,7 @@ def test_r3_p1(mpi, petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r2_p2(petsc_flag): single_level(2, 2, 3, 0.15562500000000043, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("mpi", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r3_p2(mpi, petsc_flag): @@ -101,6 +103,7 @@ def test_r3_p2(mpi, petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r2_p3(petsc_flag): single_level(2, 3, 9, 0.36475123309992658, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("mpi", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r3_p3(mpi, petsc_flag): @@ -111,6 +114,7 @@ def test_r3_p3(mpi, petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r2_p4(petsc_flag): single_level(2, 4, 18, 0.65348841031767191, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("mpi", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r3_p4(mpi, petsc_flag): @@ -121,6 +125,7 @@ def test_r3_p4(mpi, petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_r2_p1(petsc_flag): single_level(2, 1, 1, 5.2734375000000000E-002, grid_type=2, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("mpi", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_r3_p1(mpi, petsc_flag): @@ -131,6 +136,7 @@ def test_hex_r3_p1(mpi, petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_r2_p2(petsc_flag): single_level(2, 2, 3, 0.15991066475591753, grid_type=2, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("mpi", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_r3_p2(mpi, petsc_flag): @@ -141,6 +147,7 @@ def test_hex_r3_p2(mpi, petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_r2_p3(petsc_flag): single_level(2, 3, 6, 0.36786823385360656, grid_type=2, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("mpi", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_r3_p3(mpi, petsc_flag): @@ -151,6 +158,7 @@ def test_hex_r3_p3(mpi, petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_r2_p4(petsc_flag): single_level(2, 4, 12, 0.65297513559274001, grid_type=2, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("mpi", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_r3_p4(mpi, petsc_flag): diff --git a/src/tests/fem/test_h0.py b/src/tests/fem/test_h0.py index 7389b37..e9ee251 100644 --- a/src/tests/fem/test_h0.py +++ b/src/tests/fem/test_h0.py @@ -147,6 +147,7 @@ def test_r2_p5(petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r3_p5(mpi, petsc_flag): single_level(3, 5, 373, 4.1329169495863446E-002, mpi=mpi, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r3_p5_mg(petsc_flag): multi_level(3, 5, 197, 4.1329169495863446E-002, petsc_flag=petsc_flag) @@ -212,6 +213,7 @@ def test_hex_r2_p5(petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_r3_p5(mpi, petsc_flag): single_level(3, 5, 410, 0.29647336194316470, grid_type=2, mpi=mpi, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_r3_p5_mg(petsc_flag): multi_level(3, 5, 277, 0.29647336194316470, grid_type=2, petsc_flag=petsc_flag) diff --git a/src/tests/fem/test_h1.py b/src/tests/fem/test_h1.py index aea78d9..185c616 100644 --- a/src/tests/fem/test_h1.py +++ b/src/tests/fem/test_h1.py @@ -133,6 +133,7 @@ def test_r2_p4(petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r3_p4(mpi, petsc_flag): single_level(3, 4, iteration_count, converged_error, mpi=mpi, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r3_p4_mg(petsc_flag): multi_level(3, 4, iteration_count, converged_error, petsc_flag=petsc_flag) @@ -183,6 +184,7 @@ def test_hex_r3_p3(mpi, petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_r2_p4(petsc_flag): single_level(2, 4, iteration_count, converged_error, grid_type=2, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("mpi", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_r3_p4(mpi, petsc_flag): diff --git a/src/tests/fem/test_hcurl.py b/src/tests/fem/test_hcurl.py index 394bfe5..14d8016 100644 --- a/src/tests/fem/test_hcurl.py +++ b/src/tests/fem/test_hcurl.py @@ -134,6 +134,7 @@ def test_r2_p4(petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r3_p4(mpi, petsc_flag): single_level(3, 4, 264, -5.54338137661367580E-004, mpi=mpi, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r3_p4_mg(petsc_flag): multi_level(3, 4, 93, -5.54338137661367580E-004, petsc_flag=petsc_flag) @@ -186,6 +187,7 @@ def test_hex_r2_p4(petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_r3_p4(mpi, petsc_flag): single_level(3, 4, 185, -1.6139281906695047E-004, grid_type=2, mpi=mpi, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_r3_p4_mg(petsc_flag): multi_level(3, 4, 65, -1.6139281906695047E-004, grid_type=2, petsc_flag=petsc_flag) diff --git a/src/tests/fem/test_hcurl_sop.py b/src/tests/fem/test_hcurl_sop.py index cee5b07..c5db189 100644 --- a/src/tests/fem/test_hcurl_sop.py +++ b/src/tests/fem/test_hcurl_sop.py @@ -71,6 +71,7 @@ def single_level(nlevels, order, mpi=False, grid_type=1, petsc_flag='F'): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r2_p1(grid_type, petsc_flag): single_level(2, 1, grid_type=grid_type, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("grid_type", (1, 2)) @pytest.mark.parametrize("mpi", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) @@ -83,6 +84,7 @@ def test_r3_p1(grid_type, mpi, petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r2_p2(grid_type, petsc_flag): single_level(2, 2, grid_type=grid_type, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("grid_type", (1, 2)) @pytest.mark.parametrize("mpi", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) @@ -95,6 +97,7 @@ def test_r3_p2(grid_type, mpi, petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r2_p3(grid_type, petsc_flag): single_level(2, 3, grid_type=grid_type, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("grid_type", (1, 2)) @pytest.mark.parametrize("mpi", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) @@ -107,6 +110,7 @@ def test_r3_p3(grid_type, mpi, petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r2_p4(grid_type, petsc_flag): single_level(2, 4, grid_type=grid_type, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("grid_type", (1, 2)) @pytest.mark.parametrize("mpi", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) diff --git a/src/tests/fem/test_lag.py b/src/tests/fem/test_lag.py index 371de0a..8e120b8 100644 --- a/src/tests/fem/test_lag.py +++ b/src/tests/fem/test_lag.py @@ -134,6 +134,7 @@ def test_r2_p4(petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r3_p4(mpi, petsc_flag): single_level(3, 4, 154, 5.1147216970434615, mpi=mpi, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r3_p4_mg(petsc_flag): multi_level(3, 4, 21, 5.1147216970434615, petsc_flag=petsc_flag) @@ -186,6 +187,7 @@ def test_hex_r2_p4(petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_r3_p4(mpi, petsc_flag): single_level(3, 4, 97, 2.5568335937210307, grid_type=2, mpi=mpi, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_r3_p4_mg(petsc_flag): multi_level(3, 4, 15, 2.5568335937210307, grid_type=2, petsc_flag=petsc_flag) diff --git a/src/tests/fem/test_lag_2d.py b/src/tests/fem/test_lag_2d.py index 83516ef..f10d2dc 100644 --- a/src/tests/fem/test_lag_2d.py +++ b/src/tests/fem/test_lag_2d.py @@ -77,6 +77,7 @@ def single_level(levels,order,exp_its,exp_error,grid_type=1,petsc_flag='F'): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_tri_p1_base(levels, petsc_flag): single_level(levels, 1, 3, 3.0669984042999903E-002, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("levels", ((2,2), (2,3), (1,3))) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_tri_p1_1ref(levels, petsc_flag): @@ -88,6 +89,7 @@ def test_tri_p1_1ref(levels, petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_tri_p2_base(levels, petsc_flag): single_level(levels, 2, 12, 0.12223325061763389, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("levels", ((2,2), (2,3), (1,3))) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_tri_p2_1ref(levels, petsc_flag): @@ -99,6 +101,7 @@ def test_tri_p2_1ref(levels, petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_tri_p3_base(levels, petsc_flag): single_level(levels, 3, 26, 0.27578221875222847, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("levels", ((2,2), (2,3), (1,3))) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_tri_p3_1ref(levels, petsc_flag): @@ -110,6 +113,7 @@ def test_tri_p3_1ref(levels, petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_tri_p4_base(levels, petsc_flag): single_level(levels, 4, 50, 0.49032174402311279, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("levels", ((2,2), (2,3), (1,3))) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_tri_p4_1ref(levels, petsc_flag): @@ -121,6 +125,7 @@ def test_tri_p4_1ref(levels, petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_quad_p1_base(levels, petsc_flag): single_level(levels, 1, 1, 1.7777777777777774E-002, grid_type=2, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("levels", ((2,2), (2,3), (1,3))) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_quad_p1_1ref(levels, petsc_flag): @@ -132,6 +137,7 @@ def test_quad_p1_1ref(levels, petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_quad_p2_base(levels, petsc_flag): single_level(levels, 2, 6, 6.0977378920293084E-002, grid_type=2, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("levels", ((2,2), (2,3), (1,3))) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_quad_p2_1ref(levels, petsc_flag): @@ -143,6 +149,7 @@ def test_quad_p2_1ref(levels, petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_quad_p3_base(levels, petsc_flag): single_level(levels, 3, 10, 0.13790992889879716, grid_type=2, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("levels", ((2,2), (2,3), (1,3))) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_quad_p3_1ref(levels, petsc_flag): @@ -154,6 +161,7 @@ def test_quad_p3_1ref(levels, petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_quad_p4_base(levels, petsc_flag): single_level(levels, 4, 25, 0.24510302835546499, grid_type=2, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("levels", ((2,2), (2,3), (1,3))) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_quad_p4_1ref(levels, petsc_flag): diff --git a/src/tests/fem/test_vlag.py b/src/tests/fem/test_vlag.py index 6277ab6..5956ed8 100644 --- a/src/tests/fem/test_vlag.py +++ b/src/tests/fem/test_vlag.py @@ -126,6 +126,7 @@ def test_r2_p4(petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r3_p4(mpi, petsc_flag): single_level(3, 4, mpi=mpi, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r3_p4_mg(petsc_flag): multi_level(3, 4, 3.*(5.1147216970434615), petsc_flag=petsc_flag) @@ -174,6 +175,7 @@ def test_hex_r3_p3(mpi, petsc_flag): @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_r2_p4(petsc_flag): single_level(2, 4, grid_type=2, petsc_flag=petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("mpi", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_r3_p4(mpi, petsc_flag): diff --git a/src/tests/grid/test_cubit.py b/src/tests/grid/test_cubit.py index f2ab523..507137d 100644 --- a/src/tests/grid/test_cubit.py +++ b/src/tests/grid/test_cubit.py @@ -90,12 +90,14 @@ def test_base(top_lev,cad_type): area_cubit = 12.1195 assert cubit_setup(1,top_lev,'sphere_tet4_test',cad_type=cad_type) assert check_result(volume_cubit, area_cubit) +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (1, 2)) def test_quad(top_lev): volume_cubit = 4.18656 area_cubit = 12.56197 assert cubit_setup(1,top_lev,'sphere_tet4_test','sphere_test',grid_order=2) assert check_result(volume_cubit, area_cubit) +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (2, 3)) def test_1ref(top_lev): volume_cubit = 4.11948 @@ -120,6 +122,7 @@ def test_tet10_quad(top_lev,cad_type): area_cubit = 12.56197 assert cubit_setup(1,top_lev,'sphere_tet10_test',grid_order=2,cad_type=cad_type) assert check_result(volume_cubit, area_cubit) +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (2, 3)) @pytest.mark.parametrize("cad_type", (0, 2)) @pytest.mark.parametrize("grid_order", (1, 2)) @@ -136,6 +139,7 @@ def test_hex27_quad(top_lev,cad_type): area_cubit = 12.56542 assert cubit_setup(1,top_lev,'sphere_hex27_test',grid_order=2,cad_type=cad_type) assert check_result(volume_cubit, area_cubit) +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (2, 3)) @pytest.mark.parametrize("cad_type", (0, 2)) @pytest.mark.parametrize("grid_order", (1, 2)) @@ -155,6 +159,7 @@ def test_cut_base(top_lev,cad_type): area_cubit = 12.16156 assert cubit_setup(1,top_lev,'sphere_cut_test',cad_type=cad_type) assert check_result(volume_cubit, area_cubit) +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (2, 3)) def test_cut_1ref(top_lev): volume_cubit = 4.12584 @@ -179,6 +184,7 @@ def test_reflect_hex_base(top_lev,cad_type): area_cubit = 12.21166 assert cubit_setup(1,top_lev,'ref_hex8_test',reflect='T',cad_type=cad_type) assert check_result(volume_cubit, area_cubit) +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (2, 3)) def test_reflect_1ref(top_lev): volume_cubit = 3.11110 @@ -186,6 +192,7 @@ def test_reflect_1ref(top_lev): minlev = 4 - top_lev assert cubit_setup(minlev,top_lev,'ref_tet4_test','ref_test',reflect='T') assert check_result(volume_cubit, area_cubit) +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (1, 2)) def test_reflect_quad(top_lev): volume_cubit = 3.14123 @@ -209,6 +216,7 @@ def test_perreflect_hex_base(top_lev,cad_type): area_cubit = 6.21166 assert cubit_setup(1,top_lev,'ref_hex8_test',reflect='T',per_ns=1,cad_type=cad_type) assert check_result(volume_cubit, area_cubit) +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (2, 3)) def test_perreflect_1ref(top_lev): volume_cubit = 3.11110 @@ -216,6 +224,7 @@ def test_perreflect_1ref(top_lev): minlev = 4 - top_lev assert cubit_setup(minlev,top_lev,'ref_tet4_test','ref_test',reflect='T',per_ns=1) assert check_result(volume_cubit, area_cubit) +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (1, 2)) def test_perreflect_quad(top_lev): volume_cubit = 3.14123 @@ -232,6 +241,7 @@ def test_tet10reflect_quad(top_lev,cad_type): area_cubit = 12.56531 assert cubit_setup(1,top_lev,'ref_tet10_test',grid_order=2,reflect='T',cad_type=cad_type) assert check_result(volume_cubit, area_cubit) +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (2, 3)) @pytest.mark.parametrize("cad_type", (0, 2)) def test_tet10reflect_1ref(top_lev,cad_type): @@ -247,6 +257,7 @@ def test_hex27reflect_quad(top_lev,cad_type): area_cubit = 12.56491 assert cubit_setup(1,top_lev,'ref_hex27_test',grid_order=2,reflect='T',cad_type=cad_type) assert check_result(volume_cubit, area_cubit) +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (2, 3)) @pytest.mark.parametrize("cad_type", (0, 2)) def test_hex27reflect_1ref(top_lev,cad_type): @@ -264,6 +275,7 @@ def test_stretch_base(top_lev): area_cubit = 12.26361 assert cubit_setup(1,top_lev,'ref_tet4_test',zstretch=2.) assert check_result(volume_cubit, area_cubit) +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (2, 3)) def test_stretch_1ref(top_lev): volume_cubit = 3.11110 @@ -271,6 +283,7 @@ def test_stretch_1ref(top_lev): minlev = 4 - top_lev assert cubit_setup(minlev,top_lev,'ref_tet4_test','ref_test',zstretch=2.) assert check_result(volume_cubit, area_cubit) +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (1, 2)) def test_stretch_quad(top_lev): volume_cubit = 3.14123 @@ -288,6 +301,7 @@ def test_surf_circle_base(mesh_type,top_lev,cad_type): area_cubit = 3.1403 assert cubit_setup(1,top_lev,'circle_{0}_test'.format(mesh_type),test_2d='T',cad_type=cad_type) assert check_result(volume_cubit, area_cubit, tol=1.E-3) +@pytest.mark.coverage @pytest.mark.parametrize("mesh_type", ("tri3", "tri6", "quad4", "quad9")) @pytest.mark.parametrize("levels", ((2, 2), (2, 3), (1, 3))) def test_surf_circle_1ref(mesh_type,levels): @@ -295,6 +309,7 @@ def test_surf_circle_1ref(mesh_type,levels): area_cubit = 3.1403 assert cubit_setup(levels[0],levels[1],'circle_{0}_test'.format(mesh_type),test_2d='T') assert check_result(volume_cubit, area_cubit, tol=1.E-3) +@pytest.mark.coverage @pytest.mark.parametrize("mesh_type", ("tri6", "quad9")) @pytest.mark.parametrize("top_lev", (1, 2)) @pytest.mark.parametrize("cad_type", (0, 2)) @@ -311,6 +326,7 @@ def test_surf_sphere_base(mesh_type,top_lev,cad_type): area_cubit = 12.5595 assert cubit_setup(1,top_lev,'sphere_{0}_test'.format(mesh_type),test_2d='T',cad_type=cad_type) assert check_result(volume_cubit, area_cubit, tol=1.E-3) +@pytest.mark.coverage @pytest.mark.parametrize("mesh_type", ("tri3", "quad4")) @pytest.mark.parametrize("levels", ((2, 2), (2, 3), (1, 3))) def test_surf_sphere_1ref(mesh_type,levels): @@ -318,6 +334,7 @@ def test_surf_sphere_1ref(mesh_type,levels): area_cubit = 12.5595 assert cubit_setup(levels[0],levels[1],'sphere_{0}_test'.format(mesh_type),test_2d='T') assert check_result(volume_cubit, area_cubit, tol=1.E-3) +@pytest.mark.coverage @pytest.mark.parametrize("mesh_type", ("tri6", "quad9")) @pytest.mark.parametrize("top_lev", (1, 2)) @pytest.mark.parametrize("cad_type", (0, 2)) diff --git a/src/tests/grid/test_gmsh.py b/src/tests/grid/test_gmsh.py index 1d965ad..eae481a 100644 --- a/src/tests/grid/test_gmsh.py +++ b/src/tests/grid/test_gmsh.py @@ -63,6 +63,7 @@ def check_result(volume_test, area_test): #============================================================================ # Test runners for basic Cylinder mesh +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (1, 2)) @pytest.mark.parametrize("cad_type", (0, 3)) def test_base(top_lev,cad_type): @@ -73,6 +74,7 @@ def test_base(top_lev,cad_type): #============================================================================ # Test runner for quadratic Cylinder mesh +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (1, 2)) @pytest.mark.parametrize("cad_type", (0, 3)) def test_quad(top_lev,cad_type): @@ -83,6 +85,7 @@ def test_quad(top_lev,cad_type): #============================================================================ # Test runner for single refinement Cylinder mesh +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (2, 3)) @pytest.mark.parametrize("cad_type", (0, 3)) def test_1ref(top_lev,cad_type): diff --git a/src/tests/grid/test_mapping.py b/src/tests/grid/test_mapping.py index af55b5c..b518445 100644 --- a/src/tests/grid/test_mapping.py +++ b/src/tests/grid/test_mapping.py @@ -56,6 +56,7 @@ def check_result(filename): #============================================================================ # Test runner for find_cell test on linear elements +@pytest.mark.coverage @pytest.mark.parametrize("grid_type", (1, 2)) def test_find_lin(grid_type): assert mapping_setup(1,1,1,91,'find',grid_type=grid_type) @@ -63,6 +64,7 @@ def test_find_lin(grid_type): #============================================================================ # Test runner for find_cell test on quadratic elements +@pytest.mark.coverage @pytest.mark.parametrize("grid_type", (1, 2)) def test_find_quad(grid_type): assert mapping_setup(1,1,2,91,'find',grid_type=grid_type) @@ -70,6 +72,7 @@ def test_find_quad(grid_type): #============================================================================ # Test runner for jacobian tests on linear elements +@pytest.mark.coverage @pytest.mark.parametrize("grid_type", (1, 2)) @pytest.mark.parametrize("elem_order", (2, 3, 4)) def test_jac_lin(grid_type, elem_order): @@ -78,6 +81,7 @@ def test_jac_lin(grid_type, elem_order): #============================================================================ # Test runner for jacobian tests on quadratic elements +@pytest.mark.coverage @pytest.mark.parametrize("grid_type", (1, 2)) @pytest.mark.parametrize("elem_order", (2, 3, 4)) def test_jac_quad(grid_type, elem_order): diff --git a/src/tests/grid/test_quad.py b/src/tests/grid/test_quad.py index 6ac84ad..be0366e 100644 --- a/src/tests/grid/test_quad.py +++ b/src/tests/grid/test_quad.py @@ -47,6 +47,7 @@ def quad_case(order,grid_type): else: return [99., 99., 99.] +@pytest.mark.coverage @pytest.mark.parametrize("grid_type", range(1,3)) @pytest.mark.parametrize("order", range(1,12)) def test_quadrature(order,grid_type): diff --git a/src/tests/grid/test_t3d.py b/src/tests/grid/test_t3d.py index 33ac2d1..d04a578 100644 --- a/src/tests/grid/test_t3d.py +++ b/src/tests/grid/test_t3d.py @@ -68,12 +68,14 @@ def test_base(top_lev): area_t3d = 12.5187 assert t3d_setup(1,top_lev,'cyl') assert check_result(volume_t3d, area_t3d) +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (1, 2)) def test_quad(top_lev): volume_t3d = 3.1416 area_t3d = 12.5663 assert t3d_setup(1,top_lev,'cyl',2) assert check_result(volume_t3d, area_t3d) +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (2, 3)) def test_1ref(top_lev): volume_t3d = 3.1378 @@ -90,12 +92,14 @@ def test_stretch_base(top_lev): area_t3d = 18.7916 assert t3d_setup(1,top_lev,'cyl',zstretch=2.) assert check_result(volume_t3d, area_t3d) +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (1, 2)) def test_stretch_quad(top_lev): volume_t3d = 2*3.1416 area_t3d = 18.8495 assert t3d_setup(1,top_lev,'cyl',2,zstretch=2.) assert check_result(volume_t3d, area_t3d) +@pytest.mark.coverage @pytest.mark.parametrize("top_lev", (2, 3)) def test_stretch_1ref(top_lev): volume_t3d = 2*3.1378 diff --git a/src/tests/lin_alg/test_arpack.py b/src/tests/lin_alg/test_arpack.py index 9aa8dbe..e98ab02 100644 --- a/src/tests/lin_alg/test_arpack.py +++ b/src/tests/lin_alg/test_arpack.py @@ -1,6 +1,7 @@ from __future__ import print_function import os import sys +import pytest test_dir = os.path.abspath(os.path.dirname(__file__)) sys.path.append(os.path.abspath(os.path.join(test_dir, '..'))) from oft_testing import run_OFT @@ -67,11 +68,13 @@ def validate_result(expected_lams): #============================================================================ expected_lams = (1.7109941816427034, 2.1784222152553760, 2.7901970304756087, 3.6590583531008862) # Test runner for base test case +@pytest.mark.coverage def test_base(): assert arpack_setup(4) assert validate_result(expected_lams) # Test runner for MPI test case +@pytest.mark.coverage def test_mpi(): assert arpack_setup(4,True) assert validate_result(expected_lams) diff --git a/src/tests/lin_alg/test_native_bjacobi.py b/src/tests/lin_alg/test_native_bjacobi.py index b1ecc4a..81f08c6 100644 --- a/src/tests/lin_alg/test_native_bjacobi.py +++ b/src/tests/lin_alg/test_native_bjacobi.py @@ -70,6 +70,7 @@ def check_solver(nprocs,nlocal,sol_type,iteration_count,converged_error): assert validate_result(iteration_count,converged_error) # Test runner for base test case +@pytest.mark.coverage @pytest.mark.parametrize("solver_type", range(nSolvers)) def test_base(solver_type): iteration_count = 1 @@ -82,12 +83,14 @@ def test_base_nlocal2(solver_type): converged_error = 1.4688646289584524 check_solver(1, 2, solver_type+1, iteration_count, converged_error) # Test runner for base test case +@pytest.mark.coverage @pytest.mark.parametrize("solver_type", range(nSolvers)) def test_base_nlocal2_part(solver_type): iteration_count = 47 converged_error = 1.4688646289584524 check_solver(1, -2, solver_type+1, iteration_count, converged_error) # Test runner for base test case +@pytest.mark.coverage @pytest.mark.parametrize("solver_type", range(nSolvers)) def test_mpi2(solver_type): iteration_count = 50 @@ -100,6 +103,7 @@ def test_mpi2_nlocal2(solver_type): converged_error = 1.4688646289584524 check_solver(2, 2, solver_type+1, iteration_count, converged_error) # Test runner for base test case +@pytest.mark.coverage @pytest.mark.parametrize("solver_type", range(nSolvers)) def test_mpi2_nlocal2_part(solver_type): iteration_count = 57 diff --git a/src/tests/lin_alg/test_solver_xml.py b/src/tests/lin_alg/test_solver_xml.py index d4db5f5..039988b 100644 --- a/src/tests/lin_alg/test_solver_xml.py +++ b/src/tests/lin_alg/test_solver_xml.py @@ -63,6 +63,7 @@ def validate_result(iteration_count,converged_error): #============================================================================ # Test runner for base test case +@pytest.mark.coverage @pytest.mark.parametrize("petsc_flag", ('F', 'T')) def test_gmres(petsc_flag): xml_file = """ @@ -78,6 +79,7 @@ def test_gmres(petsc_flag): #============================================================================ # Test runner for base test case +@pytest.mark.coverage @pytest.mark.parametrize("petsc_flag", ('F', 'T')) def test_gmres_jacobi(petsc_flag): xml_file = """ @@ -94,6 +96,7 @@ def test_gmres_jacobi(petsc_flag): #============================================================================ # Test runner for base test case +@pytest.mark.coverage @pytest.mark.parametrize("petsc_flag", ('F', 'T')) def test_cg(petsc_flag): xml_file = """ @@ -108,6 +111,7 @@ def test_cg(petsc_flag): #============================================================================ # Test runner for base test case +@pytest.mark.coverage @pytest.mark.parametrize("petsc_flag", ('F', 'T')) def test_cg_jacobi(petsc_flag): xml_file = """ diff --git a/src/tests/physics/conftest.py b/src/tests/physics/conftest.py index 762c440..54d1a71 100644 --- a/src/tests/physics/conftest.py +++ b/src/tests/physics/conftest.py @@ -2,5 +2,6 @@ def pytest_configure(config): config.addinivalue_line("markers", "slow: Mark test as too slow for CI testing") + config.addinivalue_line("markers", "coverage: Mark test for use in code coverage") config.addinivalue_line("markers", "mpi: Mark test as parallel using MPI") config.addinivalue_line("markers", "linear: Mark MHD test as linear") diff --git a/src/tests/physics/test_TokaMaker.py b/src/tests/physics/test_TokaMaker.py index 83c445a..75c9527 100644 --- a/src/tests/physics/test_TokaMaker.py +++ b/src/tests/physics/test_TokaMaker.py @@ -104,6 +104,7 @@ def validate_solo(results,psi_err_exp,X_err_exp): # Test runners for Solov'ev cases +@pytest.mark.coverage @pytest.mark.parametrize("order", (2,3,4)) def test_solo_h1(order): errs = [ @@ -188,6 +189,7 @@ def validate_sph(results,psi_err_exp): # Test runners for Spheromak cases +@pytest.mark.coverage @pytest.mark.parametrize("order", (2,3,4)) def test_spheromak_h1(order): errs = [2.039674417912789e-05, 5.103597862537552e-07, 8.088772274705608e-09] @@ -266,6 +268,7 @@ def validate_coil(results,psi_err_exp): # Test runners for vacuum coil cases +@pytest.mark.coverage @pytest.mark.parametrize("order", (2,3,4)) def test_coil_h1(order): errs = [0.010800921782063938, 0.0002851010669736233, 1.8185396736818836e-05] @@ -419,6 +422,7 @@ def validate_ITER(results,dict_exp): # Test runners for ITER test cases +@pytest.mark.coverage @pytest.mark.parametrize("order", (2,3))#,4)) def test_ITER(order): exp_dict = { diff --git a/src/tests/physics/test_alfven.py b/src/tests/physics/test_alfven.py index 5d605a9..e76dba2 100644 --- a/src/tests/physics/test_alfven.py +++ b/src/tests/physics/test_alfven.py @@ -148,6 +148,7 @@ def test_nl_r1_p2(petsc_flag): assert alfven_setup(1,1,2,2,petsc=petsc_flag) assert validate_result(berr_exp,verr_exp) @pytest.mark.mpi +@pytest.mark.coverage @pytest.mark.parametrize("mf", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_nl_r1_p2_mpi(mf,petsc_flag): @@ -204,6 +205,7 @@ def test_lin_r1_p2(petsc_flag): assert validate_result(berr_exp,verr_exp,linear=True) @pytest.mark.linear @pytest.mark.mpi +@pytest.mark.coverage @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_lin_r1_p2_mpi(petsc_flag): berr_exp = 3.6339799347819605E-002 @@ -259,6 +261,7 @@ def test_hex_nl_r1_p2(petsc_flag): assert alfven_setup(1,1,2,2,petsc=petsc_flag,hex_mesh=True) assert validate_result(berr_exp,verr_exp) @pytest.mark.mpi +@pytest.mark.coverage @pytest.mark.parametrize("mf", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_nl_r1_p2_mpi(mf,petsc_flag): @@ -319,6 +322,7 @@ def test_hex_lin_r1_p2(petsc_flag): assert validate_result(berr_exp,verr_exp,linear=True) @pytest.mark.linear @pytest.mark.mpi +@pytest.mark.coverage @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_lin_r1_p2_mpi(petsc_flag): berr_exp = 1.8214844434637032E-002 diff --git a/src/tests/physics/test_alfven_lag.py b/src/tests/physics/test_alfven_lag.py index a37aaaf..fa8946d 100644 --- a/src/tests/physics/test_alfven_lag.py +++ b/src/tests/physics/test_alfven_lag.py @@ -147,6 +147,7 @@ def test_nl_r1_p2(petsc_flag): assert alfven_setup(1,1,2,2,petsc=petsc_flag) assert validate_result(berr_exp,verr_exp) @pytest.mark.mpi +@pytest.mark.coverage @pytest.mark.parametrize("mf", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_nl_r1_p2_mpi(mf,petsc_flag): @@ -203,6 +204,7 @@ def test_lin_r1_p2(petsc_flag): assert validate_result(berr_exp,verr_exp,linear=True) @pytest.mark.linear @pytest.mark.mpi +@pytest.mark.coverage @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_lin_r1_p2_mpi(petsc_flag): berr_exp = 4.2445515350299369E-002 @@ -258,6 +260,7 @@ def test_hex_nl_r1_p2(petsc_flag): assert alfven_setup(1,1,2,2,petsc=petsc_flag,hex_mesh=True) assert validate_result(berr_exp,verr_exp) @pytest.mark.mpi +@pytest.mark.coverage @pytest.mark.parametrize("mf", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_nl_r1_p2_mpi(mf,petsc_flag): @@ -318,6 +321,7 @@ def test_hex_lin_r1_p2(petsc_flag): assert validate_result(berr_exp,verr_exp,linear=True) @pytest.mark.linear @pytest.mark.mpi +@pytest.mark.coverage @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_lin_r1_p2_mpi(petsc_flag): berr_exp = 1.8185749172894013E-002 diff --git a/src/tests/physics/test_sound.py b/src/tests/physics/test_sound.py index 363d239..3ce323c 100644 --- a/src/tests/physics/test_sound.py +++ b/src/tests/physics/test_sound.py @@ -165,6 +165,7 @@ def test_nl_r1_p2(petsc_flag): assert sound_setup(1,1,2,2,petsc=petsc_flag) assert validate_result(nerr_exp,terr_exp,verr_exp) @pytest.mark.mpi +@pytest.mark.coverage @pytest.mark.parametrize("mf", (False, True)) @pytest.mark.parametrize("two_temp", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) @@ -230,6 +231,7 @@ def test_lin_r1_p2(petsc_flag): assert validate_result(nerr_exp,terr_exp,verr_exp,linear=True) @pytest.mark.linear @pytest.mark.mpi +@pytest.mark.coverage @pytest.mark.parametrize("two_temp", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_lin_r1_p2_mpi(two_temp,petsc_flag): @@ -294,6 +296,7 @@ def test_hex_nl_r1_p2(petsc_flag): assert sound_setup(1,1,2,2,petsc=petsc_flag,hex_mesh=True) assert validate_result(nerr_exp,terr_exp,verr_exp) @pytest.mark.mpi +@pytest.mark.coverage @pytest.mark.parametrize("mf", (False, True)) @pytest.mark.parametrize("two_temp", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) @@ -359,6 +362,7 @@ def test_hex_lin_r1_p2(petsc_flag): assert validate_result(nerr_exp,terr_exp,verr_exp,linear=True) @pytest.mark.linear @pytest.mark.mpi +@pytest.mark.coverage @pytest.mark.parametrize("two_temp", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_lin_r1_p2_mpi(two_temp,petsc_flag): diff --git a/src/tests/physics/test_sound_lag.py b/src/tests/physics/test_sound_lag.py index a14b8b9..7f4bc96 100644 --- a/src/tests/physics/test_sound_lag.py +++ b/src/tests/physics/test_sound_lag.py @@ -165,6 +165,7 @@ def test_nl_r1_p2(petsc_flag): assert sound_setup(1,1,2,2,petsc=petsc_flag) assert validate_result(nerr_exp,terr_exp,verr_exp) @pytest.mark.mpi +@pytest.mark.coverage @pytest.mark.parametrize("mf", (False, True)) @pytest.mark.parametrize("two_temp", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) @@ -230,6 +231,7 @@ def test_lin_r1_p2(petsc_flag): assert validate_result(nerr_exp,terr_exp,verr_exp,linear=True) @pytest.mark.linear @pytest.mark.mpi +@pytest.mark.coverage @pytest.mark.parametrize("two_temp", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_lin_r1_p2_mpi(two_temp,petsc_flag): @@ -294,6 +296,7 @@ def test_hex_nl_r1_p2(petsc_flag): assert sound_setup(1,1,2,2,petsc=petsc_flag,hex_mesh=True) assert validate_result(nerr_exp,terr_exp,verr_exp) @pytest.mark.mpi +@pytest.mark.coverage @pytest.mark.parametrize("mf", (False, True)) @pytest.mark.parametrize("two_temp", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) @@ -359,6 +362,7 @@ def test_hex_lin_r1_p2(petsc_flag): assert validate_result(nerr_exp,terr_exp,verr_exp,linear=True) @pytest.mark.linear @pytest.mark.mpi +@pytest.mark.coverage @pytest.mark.parametrize("two_temp", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_hex_lin_r1_p2_mpi(two_temp,petsc_flag): diff --git a/src/tests/physics/test_taylor.py b/src/tests/physics/test_taylor.py index 1d9b957..bcdd15e 100644 --- a/src/tests/physics/test_taylor.py +++ b/src/tests/physics/test_taylor.py @@ -148,6 +148,7 @@ def test_r2_p1(parallel, petsc_flag): lam = 7.8527035135303347 tflux = None #0.39122960997664691 run_r2_base(lam, tflux, 1, parallel, petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("parallel", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r3_p1(parallel, petsc_flag): @@ -220,6 +221,7 @@ def test_r3_p4(parallel, petsc_flag): lam = 4.4949864203192 tflux = None #0.06331006366265143 run_r3_base(lam, tflux, 4, parallel, petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r3_p4_mg(petsc_flag): lam = 4.4949864203192 diff --git a/src/tests/physics/test_taylor_inj.py b/src/tests/physics/test_taylor_inj.py index 000a63f..08e6bd4 100644 --- a/src/tests/physics/test_taylor_inj.py +++ b/src/tests/physics/test_taylor_inj.py @@ -126,6 +126,7 @@ def test_r1_p1(parallel, petsc_flag): energy = 7.969787207500343 mags = (111.78525848354835, 7.3927838909183474E-2, 2.3689570294000877) run_r1_base(energy, mags, 1, parallel, petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("parallel", (False, True)) @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r2_p1(parallel, petsc_flag): @@ -154,6 +155,7 @@ def test_r2_p2(parallel, petsc_flag): energy = 7.4654554644593762 mags = (159.33067882086129, 0.15585267451452800, 11.630373174047200) run_r2_base(energy, mags, 2, parallel, petsc_flag) +@pytest.mark.coverage @pytest.mark.parametrize("petsc_flag", ('F','T')) def test_r2_p2_mg(petsc_flag): energy = 7.4654554644593762 diff --git a/src/tests/physics/test_thin_wall.py b/src/tests/physics/test_thin_wall.py index d5713a3..7506944 100644 --- a/src/tests/physics/test_thin_wall.py +++ b/src/tests/physics/test_thin_wall.py @@ -228,6 +228,7 @@ def test_td_plate(direct_flag): floops=((0.5, -0.05), (0.5, -0.1)), curr_waveform=((-1.0, -1.0), (0.0, 0.0), (1.0, 1.0))) assert validate_td(sigs_final) + @pytest.mark.parametrize("direct_flag", ('F', 'T')) def test_td_plate_volt(direct_flag): sigs_final = (4.E-3, 2.383774E+1, 2.005698E+1) @@ -255,6 +256,7 @@ def test_td_cyl_volt(direct_flag): volt_waveform=((-1.0, 1.0, 1.0), (0.0, 1.0, 1.0), (1.0, 1.0, 1.0))) assert validate_td(sigs_final) +@pytest.mark.coverage @pytest.mark.parametrize("direct_flag", ('F', 'T')) def test_td_torus(direct_flag): sigs_final = (4.E-3, 4.772879E-4, 3.408103E-5) @@ -264,6 +266,7 @@ def test_td_torus(direct_flag): curr_waveform=((-1.0, -1.0), (0.0, 0.0), (1.0, 1.0))) assert validate_td(sigs_final) +@pytest.mark.coverage @pytest.mark.parametrize("direct_flag", ('F', 'T')) def test_td_torus_volt(direct_flag): sigs_final = (4.E-3, 3.249705E0, 2.3204651E-1) @@ -273,6 +276,7 @@ def test_td_torus_volt(direct_flag): volt_waveform=((-1.0, 1.0, 1.0), (0.0, 1.0, 1.0), (1.0, 1.0, 1.0))) assert validate_td(sigs_final) +@pytest.mark.coverage @pytest.mark.parametrize("direct_flag", ('F', 'T')) def test_td_passive(direct_flag): sigs_final = (4.E-3, 7.685703E-4, 7.888816E-4) @@ -283,6 +287,7 @@ def test_td_passive(direct_flag): curr_waveform=((-1.0, -1.0), (0.0, 0.0), (1.0, 1.0))) assert validate_td(sigs_final) +@pytest.mark.coverage @pytest.mark.parametrize("direct_flag", ('F', 'T')) def test_td_passive_volt(direct_flag): sigs_final = (4.E-3, 2.114789E+1, 2.170003E+1) @@ -306,12 +311,14 @@ def test_eig_cyl(direct_flag): assert thin_wall_setup("tw_test-cyl.h5",2,direct_flag) assert validate_eigs(eigs) +@pytest.mark.coverage @pytest.mark.parametrize("direct_flag", ('F', 'T')) def test_eig_torus(direct_flag): eigs = (4.751344E-2, 2.564491E-2, 2.555695E-2, 2.285850E-2) assert thin_wall_setup("tw_test-torus.h5",2,direct_flag) assert validate_eigs(eigs) +@pytest.mark.coverage @pytest.mark.parametrize("direct_flag", ('F', 'T')) def test_eig_passive(direct_flag): eigs = (1.483589E-1, 6.207849E-2, 2.942791E-2, 2.693574E-2) @@ -340,6 +347,7 @@ def test_fr_cyl(direct_flag): floops=((0.9, 0.5), (0.9, 0.0))) assert validate_fr(fr_real, fr_imag) +@pytest.mark.coverage @pytest.mark.parametrize("direct_flag", ('F', 'T')) def test_fr_torus(direct_flag): fr_real = (-2.806665E-3, -1.194625E-4) @@ -349,6 +357,7 @@ def test_fr_torus(direct_flag): floops=((1.4, 0.0), (0.6, 0.0))) assert validate_fr(fr_real, fr_imag) +@pytest.mark.coverage @pytest.mark.parametrize("direct_flag", ('F', 'T')) def test_fr_passive(direct_flag): fr_real = (1.777366E-1, 1.868689E-1) diff --git a/src/utilities/build_libs.py b/src/utilities/build_libs.py index 0f3570f..677d3b1 100644 --- a/src/utilities/build_libs.py +++ b/src/utilities/build_libs.py @@ -343,7 +343,7 @@ def build_make_include(mydict): fid.write(string) -def build_cmake_script(mydict,build_debug=False,build_python=False,build_tests=False,build_examples=False,build_docs=False,package_build=False): +def build_cmake_script(mydict,build_debug=False,build_python=False,build_tests=False,build_examples=False,build_docs=False,build_coverage=False,package_build=False): def bool_to_string(val): if val: return "TRUE" @@ -366,6 +366,7 @@ def bool_to_string(val): "-DOFT_BUILD_PYTHON:BOOL={0}".format(bool_to_string(build_python)), "-DOFT_BUILD_DOCS:BOOL={0}".format(bool_to_string(build_docs)), "-DOFT_PACKAGE_BUILD:BOOL={0}".format(bool_to_string(package_build)), + "-DOFT_COVERAGE:BOOL={0}".format(bool_to_string(build_coverage)), "-DOFT_USE_OpenMP:BOOL=TRUE", "-DCMAKE_C_COMPILER:FILEPATH={CC}", "-DCMAKE_CXX_COMPILER:FILEPATH={CXX}", @@ -462,6 +463,7 @@ class package: root_path = None root_build_path = None skip = False + build_timeout = 15 extra_fetch = [] patch_files = [] install_chk_files = [] @@ -595,7 +597,7 @@ def run_build(self, build_lines, config_dict): fid.write("\n".join(script_lines)) if self.config_dict['SETUP_ONLY']: return - result, _ = run_command("bash build_tmp.sh", timeout=15*60) + result, _ = run_command("bash build_tmp.sh", timeout=self.build_timeout*60) with open("build_tmp.log", "w+") as fid: fid.write(result) # Check for build success @@ -727,6 +729,7 @@ class MPI(package): def __init__(self): self.name = "MPI" self.url = "http://faculty.washington.edu/hansec/libs/mpich-3.3.2.tar.gz" + self.build_timeout = 20 def setup(self, config_dict): self.config_dict = config_dict.copy() @@ -1697,6 +1700,7 @@ def build(self): group.add_argument("--oft_build_examples", default=0, type=int, choices=(0,1), help="Build OFT examples?") group.add_argument("--oft_build_docs", default=0, type=int, choices=(0,1), help="Build OFT documentation? (requires doxygen)") group.add_argument("--oft_package", action="store_true", default=False, help="Perform a packaging build of OFT?") +group.add_argument("--oft_build_coverage", action="store_true", default=False, help="Build OFT with code coverage flags?") # group = parser.add_argument_group("MPI", "MPI package options") group.add_argument("--build_mpi", default=0, type=int, choices=(0,1), help="Build MPI libraries?") @@ -1869,5 +1873,6 @@ def build(self): build_tests=(options.oft_build_tests == 1), build_examples=(options.oft_build_examples == 1), build_docs=(options.oft_build_docs == 1), + build_coverage=(options.oft_build_coverage == 1), package_build=options.oft_package ) From 0d5876fe90f0c4365b560a613bb36fcf60e41ef9 Mon Sep 17 00:00:00 2001 From: Chris Hansen Date: Thu, 22 Feb 2024 21:59:18 -0500 Subject: [PATCH 3/5] Fix CI badge on README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e12b4b1..5a3ce81 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Open Flexible Unstructured Simulation Infrastructure with Open Numerics (FUSION) Toolkit (OFT) ===================================== -![CI status](https://github.com/hansec/OpenFUSIONToolkit/actions/workflows/build_test.yaml/badge.svg?branch=main) +![CI status](https://github.com/hansec/OpenFUSIONToolkit/actions/workflows/ci_build.yaml/badge.svg?branch=main) [![codecov](https://codecov.io/gh/hansec/OpenFUSIONToolkit/graph/badge.svg?token=GG282HKNAO)](https://codecov.io/gh/hansec/OpenFUSIONToolkit) [![DOI](https://zenodo.org/badge/710415041.svg)](https://zenodo.org/doi/10.5281/zenodo.10306801) From e2fd89acf7db61dafd51563d7a5ce1f523fb8775 Mon Sep 17 00:00:00 2001 From: Chris Hansen Date: Fri, 23 Feb 2024 13:54:26 -0500 Subject: [PATCH 4/5] Fix typos in citations and add to publication list --- src/docs/website/index.html | 2 +- src/docs/website/publications.html | 49 +++++++++++++++++------------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/docs/website/index.html b/src/docs/website/index.html index cae129c..7abe54f 100644 --- a/src/docs/website/index.html +++ b/src/docs/website/index.html @@ -72,7 +72,7 @@

How To Cite

  • MUG:
    • C. Hansen et al., Phys. Plasmas 22, 042505 (2015) [DOI]
    • -
    • C. Hansen et al., PhD Thesis: U. Washington (2014) [URI]
    • +
    • C. Hansen, PhD Thesis: U. Washington (2014) [URI]
  • Marklin: diff --git a/src/docs/website/publications.html b/src/docs/website/publications.html index 66147bc..973d780 100644 --- a/src/docs/website/publications.html +++ b/src/docs/website/publications.html @@ -32,27 +32,34 @@

    Publications

    Publications using predecessor codes

      -
    1. P. Hughes et al., Toroidal plasma acceleration due to NBI fast ion losses in LTX-β, Plasma Physics & Controlled Fusion 63, 085020 (2021)
    2. -
    3. D. Sutherland et al., Driven resonant current amplification in self-organized plasma configurations with uniform λ and plasma pressure confinement, Physics of Plasmas 28, 022507 (2021)
    4. -
    5. T. Benedett et al., Effect of geometric and magnetic boundary conditions on magnetic islands in 3D force-free ideal MHD equilibria, Nuclear Fusion 61 036022 (2021)
    6. -
    7. A. Maan et al., Oxidation of lithium plasma facing components and its effect on plasma performance in the Lithium Tokamak eXperiment-β, Plasma Physics & Controlled Fusion 63, 025007 (2021)
    8. -
    9. A. Kaptanoglu et al., Two-temperature effects in Hall-MHD simulations of the HIT-SI Experiment, Physics of Plasmas 27, 072505 (2020)
    10. -
    11. D. Elliot et al., Initial Results from the newly upgraded LTX-β, IEEE Transactions on Plasma Science 27, 6:1382-1387 (2020)
    12. -
    13. T. Benedett et al., Effects of temperature and density evolution in MHD simulations of HIT-SI, Physics of Plasmas 27, 042508 (2020)
    14. -
    15. P. Hughes et al., Magnetic Perturbation Diagnostics in the High-Temperature, Lithiated Environment of LTX-β, Review of Scientific Instruments 89, 10J104 (2018)
    16. -
    17. D. Boyle et al., Observation of flat electron temperature profiles in the Lithium Tokamak Experiment, Physical Review Letters 119, 015001 (2017)
    18. -
    19. J. Levesque et al., Measurement of scrape-off-layer current dynamics during MHD activity and disruptions in HBT-EP, Nuclear Fusion 57, 086035 (2017)
    20. -
    21. C. Hansen et al., Equilibrium reconstruction with 3D eddy currents in the Lithium Tokamak eXperiment, Physics of Plasmas 24, 042513 (2017)
    22. -
    23. R. Majeski et al., Compatibility of lithium plasma-facing surfaces with high edge temperatures in the Lithium Tokamak Experiment, Physics of Plasmas 24, 056110 (2017)
    24. -
    25. C. Hansen et al., Numerical studies and metric development for validation of magnetohydrodynamic models on the HIT-SI experiment, Physics of Plasmas 22, 056105 (2015)
    26. -
    27. C. Hansen et al., Simulation of injector dynamics during steady inductive helicity injection current drive in the HIT-SI experiment, Physics of Plasmas 22, 042505 (2015)
    28. -
    29. B. Victor et al., Development of validation metrics using biorthogonal decomposition for the comparison of magnetic field measurements, Plasma Physics & Controlled Fusion 57, 045010 (2015)
    30. -
    31. C. Hansen et al., MHD Modeling in Complex 3D Geometries: Towards Predictive Simulation of SIHI Current Drive, PhD Thesis: U. Washington (2014)
    32. -
    33. T. Jarboe et al., A Proof of Principle of Imposed Dynamo Current Drive: Demonstration of Sufficient Confinement, Fusion Science & Technology 66, 3 (369-384) (2014)
    34. -
    35. B. Victor et al., Sustained spheromaks with ideal n=1 kink stability and pressure confinement, Physics of Plasmas 21, 082504 (2014)
    36. -
    37. J. Wrobel et al., Relaxation-time measurement via a time-dependent helicity balance model, Physics of Plasmas 20, 012503 (2013)
    38. -
    39. T. Jarboe et al., Imposed-dynamo current drive, Nuclear Fusion 52, 083017 (2012)
    40. -
    41. B. Victor et al., Evidence for Separatrix Formation and Sustainment with Steady Inductive Helicity Injection, Physical Review Letters 16, 165005 (2011)
    42. +
    43. A. Maan et al., Estimates of global recycling coefficients for LTX-β discharges, Physics of Plasmas 31, 022505 (2024)
    44. +
    45. D. Boyle et al., Extending the low-recycling, flat temperature profile regime in the Lithium Tokamak Experiment-β (LTX-β) with ohmic and neutral beam heating, Nuclear Fusion 63, 056020 (2023)
    46. +
    47. A. Maan et al., Improved neutral and plasma density control with increasing lithium wall coatings in the Lithium Tokamak Experiment-β (LTX-β), Nuclear Materials and Energy 35, 101408 (2023)
    48. +
    49. R. Chandra et al., An Optical-Input Maximum Likelihood Estimation Feedback System Demonstrated on Tokamak Horizontal Equilibrium Control, Fusion Engineering and Design 191, 113565 (2023)
    50. +
    51. S. Banerjee et al., Feasibility study of a high spatial and time resolution beam emission spectroscopy diagnostic for localized density fluctuation measurements in Lithium Tokamak eXperiment-β (LTX-β), Review of Scientific Instruments 93, 113523 (2022)
    52. +
    53. W. Capecchi et al., Neutral beam prompt loss in LTX-β, Nuclear Fusion 61, 126014 (2021)
    54. +
    55. J. Brooks et al., Suppression of MHD modes with active phase-control of probe-injected currents, Nuclear Fusion 61, 096017 (2021)
    56. +
    57. P. Hughes et al., Toroidal plasma acceleration due to NBI fast ion losses in LTX-β, Plasma Physics & Controlled Fusion 63, 085020 (2021)
    58. +
    59. D. Sutherland et al., Driven resonant current amplification in self-organized plasma configurations with uniform λ and plasma pressure confinement, Physics of Plasmas 28, 022507 (2021)
    60. +
    61. T. Benedett et al., Effect of geometric and magnetic boundary conditions on magnetic islands in 3D force-free ideal MHD equilibria, Nuclear Fusion 61 036022 (2021)
    62. +
    63. A. Maan et al., Oxidation of lithium plasma facing components and its effect on plasma performance in the Lithium Tokamak eXperiment-β, Plasma Physics & Controlled Fusion 63, 025007 (2021)
    64. +
    65. A. Kaptanoglu et al., Two-temperature effects in Hall-MHD simulations of the HIT-SI Experiment, Physics of Plasmas 27, 072505 (2020)
    66. +
    67. D. Elliot et al., Initial Results from the newly upgraded LTX-β, IEEE Transactions on Plasma Science 27, 6:1382-1387 (2020)
    68. +
    69. T. Benedett et al., Effects of temperature and density evolution in MHD simulations of HIT-SI, Physics of Plasmas 27, 042508 (2020)
    70. +
    71. P. Hughes et al., Magnetic Perturbation Diagnostics in the High-Temperature, Lithiated Environment of LTX-β, Review of Scientific Instruments 89, 10J104 (2018)
    72. +
    73. D. Boyle et al., Observation of flat electron temperature profiles in the Lithium Tokamak Experiment, Physical Review Letters 119, 015001 (2017)
    74. +
    75. J. Levesque et al., Measurement of scrape-off-layer current dynamics during MHD activity and disruptions in HBT-EP, Nuclear Fusion 57, 086035 (2017)
    76. +
    77. C. Hansen et al., Equilibrium reconstruction with 3D eddy currents in the Lithium Tokamak eXperiment, Physics of Plasmas 24, 042513 (2017)
    78. +
    79. R. Majeski et al., Compatibility of lithium plasma-facing surfaces with high edge temperatures in the Lithium Tokamak Experiment, Physics of Plasmas 24, 056110 (2017)
    80. +
    81. C. Hansen et al., Numerical studies and metric development for validation of magnetohydrodynamic models on the HIT-SI experiment, Physics of Plasmas 22, 056105 (2015)
    82. +
    83. C. Hansen et al., Simulation of injector dynamics during steady inductive helicity injection current drive in the HIT-SI experiment, Physics of Plasmas 22, 042505 (2015)
    84. +
    85. B. Victor et al., Development of validation metrics using biorthogonal decomposition for the comparison of magnetic field measurements, Plasma Physics & Controlled Fusion 57, 045010 (2015)
    86. +
    87. C. Hansen, MHD Modeling in Complex 3D Geometries: Towards Predictive Simulation of SIHI Current Drive, PhD Thesis: U. Washington (2014)
    88. +
    89. T. Jarboe et al., A Proof of Principle of Imposed Dynamo Current Drive: Demonstration of Sufficient Confinement, Fusion Science & Technology 66, 3 (369-384) (2014)
    90. +
    91. B. Victor et al., Sustained spheromaks with ideal n=1 kink stability and pressure confinement, Physics of Plasmas 21, 082504 (2014)
    92. +
    93. J. Wrobel et al., Relaxation-time measurement via a time-dependent helicity balance model, Physics of Plasmas 20, 012503 (2013)
    94. +
    95. T. Jarboe et al., Imposed-dynamo current drive, Nuclear Fusion 52, 083017 (2012)
    96. +
    97. B. Victor et al., Evidence for Separatrix Formation and Sustainment with Steady Inductive Helicity Injection, Physical Review Letters 16, 165005 (2011)
    From 69b27042529b5f4ce8ff088ec3b7fd2a746804d5 Mon Sep 17 00:00:00 2001 From: Chris Hansen Date: Fri, 23 Feb 2024 14:52:00 -0500 Subject: [PATCH 5/5] ThinCurr: Remove requirement for t<0 point in current/voltage waveforms - Add extrapolation to `linterp` function - Update tests to correct for typo --- src/base/oft_base.F90 | 33 +++++++++++++++++++++++----- src/bin/thincurr_td.F90 | 26 +++++++++++----------- src/docs/thincurr_example2.md | 3 +-- src/examples/ThinCurr/cyl/curr.drive | 3 +-- src/tests/physics/test_thin_wall.py | 24 ++++++++++---------- 5 files changed, 55 insertions(+), 34 deletions(-) diff --git a/src/base/oft_base.F90 b/src/base/oft_base.F90 index 22a6511..ecaad11 100644 --- a/src/base/oft_base.F90 +++ b/src/base/oft_base.F90 @@ -922,20 +922,43 @@ END SUBROUTINE orient_listn_inv !! @warning This function requires `x` be sorted lowest to highest. !! @note This function performs an interval search each time it is called. !! -!! @returns \f$ F(xx) \f$ +!! @returns \f$ F(xx) \f$ (-1.E99 if outside domain and `extrap=0`) !------------------------------------------------------------------------------ -PURE FUNCTION linterp(x,y,n,xx) result(yy) +FUNCTION linterp(x,y,n,xx,extrap) result(yy) REAL(r8), INTENT(in) :: x(n) !< Paramaterizing array \f$ x_i \f$ [n] REAL(r8), INTENT(in) :: y(n) !< Function values \f$ F(x_i) \f$ [n] -REAL(r8), INTENT(in) :: xx !< Length of function parameterization -INTEGER(i4), INTENT(in) :: n !< Location to perform interpolation +REAL(r8), INTENT(in) :: xx !< Location to perform interpolation +INTEGER(i4), INTENT(in) :: n !< Length of function parameterization +INTEGER(i4), OPTIONAL, INTENT(in) :: extrap !< Extrapolation mode (0: none, 1: constant, 2: linear) INTEGER(i4) :: i REAL(r8) :: yy yy=-1.d99 DO i=2,n IF(x(i-1)<=xx.AND.x(i)>=xx)EXIT END DO -IF(i<=n)yy=(y(i)-y(i-1))*(xx-x(i-1))/(x(i)-x(i-1)) + y(i-1) +IF(i<=n)THEN + yy=(y(i)-y(i-1))*(xx-x(i-1))/(x(i)-x(i-1)) + y(i-1) +ELSE + IF(PRESENT(extrap))THEN + SELECT CASE(extrap) + CASE(0) + CASE(1) + IF(xxx(n))THEN + yy=y(n) + END IF + CASE(2) + IF(xxx(n))THEN + yy=(y(n)-y(n-1))*(xx-x(n-1))/(x(n)-x(n-1)) + y(n-1) + END IF + CASE DEFAULT + CALL oft_abort("Invalid extrapolation type","linterp",__FILE__) + END SELECT + END IF +END IF END FUNCTION linterp !--------------------------------------------------------------------------- !> Reset the stack diff --git a/src/bin/thincurr_td.F90 b/src/bin/thincurr_td.F90 index 3af1de8..8d24a68 100644 --- a/src/bin/thincurr_td.F90 +++ b/src/bin/thincurr_td.F90 @@ -303,7 +303,7 @@ SUBROUTINE run_td_sim(self,dt,nsteps,vec,direct,nplot,sensors) t=0.d0 IF(ntimes_curr>0)THEN DO j=1,self%n_icoils - icoil_curr(j)=linterp(curr_waveform(:,1),curr_waveform(:,j+1),ntimes_curr,t) + icoil_curr(j)=linterp(curr_waveform(:,1),curr_waveform(:,j+1),ntimes_curr,t,1) END DO END IF !---Save sensor data for t=0 @@ -373,32 +373,32 @@ SUBROUTINE run_td_sim(self,dt,nsteps,vec,direct,nplot,sensors) IF(ntimes_curr>0)THEN DO j=1,self%n_icoils ! Start of step - icoil_dcurr(j)=linterp(curr_waveform(:,1),curr_waveform(:,j+1),ntimes_curr,t+dt/4.d0) - icoil_dcurr(j)=icoil_dcurr(j)-linterp(curr_waveform(:,1),curr_waveform(:,j+1),ntimes_curr,t-dt/4.d0) + icoil_dcurr(j)=linterp(curr_waveform(:,1),curr_waveform(:,j+1),ntimes_curr,t+dt/4.d0,1) + icoil_dcurr(j)=icoil_dcurr(j)-linterp(curr_waveform(:,1),curr_waveform(:,j+1),ntimes_curr,t-dt/4.d0,1) ! End of step - icoil_dcurr(j)=icoil_dcurr(j)+linterp(curr_waveform(:,1),curr_waveform(:,j+1),ntimes_curr,t+dt*5.d0/4.d0) - icoil_dcurr(j)=icoil_dcurr(j)-linterp(curr_waveform(:,1),curr_waveform(:,j+1),ntimes_curr,t+dt*3.d0/4.d0) + icoil_dcurr(j)=icoil_dcurr(j)+linterp(curr_waveform(:,1),curr_waveform(:,j+1),ntimes_curr,t+dt*5.d0/4.d0,1) + icoil_dcurr(j)=icoil_dcurr(j)-linterp(curr_waveform(:,1),curr_waveform(:,j+1),ntimes_curr,t+dt*3.d0/4.d0,1) END DO END IF IF(ntimes_volt>0)THEN DO j=1,self%n_vcoils ! Start of step - pcoil_volt(j)=linterp(volt_waveform(:,1),volt_waveform(:,j+1),ntimes_volt,t)/2.d0 - pcoil_volt(j)=pcoil_volt(j)+linterp(volt_waveform(:,1),volt_waveform(:,j+1),ntimes_volt,t+dt)/2.d0 + pcoil_volt(j)=linterp(volt_waveform(:,1),volt_waveform(:,j+1),ntimes_volt,t,1)/2.d0 + pcoil_volt(j)=pcoil_volt(j)+linterp(volt_waveform(:,1),volt_waveform(:,j+1),ntimes_volt,t+dt,1)/2.d0 END DO END IF ELSE CALL Lmat%apply(u,g) IF(ntimes_curr>0)THEN DO j=1,self%n_icoils - icoil_dcurr(j)=linterp(curr_waveform(:,1),curr_waveform(:,j+1),ntimes_curr,t+dt*5.d0/4.d0) - icoil_dcurr(j)=icoil_dcurr(j)-linterp(curr_waveform(:,1),curr_waveform(:,j+1),ntimes_curr,t+dt*3.d0/4.d0) + icoil_dcurr(j)=linterp(curr_waveform(:,1),curr_waveform(:,j+1),ntimes_curr,t+dt*5.d0/4.d0,1) + icoil_dcurr(j)=icoil_dcurr(j)-linterp(curr_waveform(:,1),curr_waveform(:,j+1),ntimes_curr,t+dt*3.d0/4.d0,1) END DO icoil_dcurr=icoil_dcurr*2.d0 END IF IF(ntimes_volt>0)THEN DO j=1,self%n_vcoils - pcoil_volt(j)=linterp(volt_waveform(:,1),volt_waveform(:,j+1),ntimes_volt,t+dt) + pcoil_volt(j)=linterp(volt_waveform(:,1),volt_waveform(:,j+1),ntimes_volt,t+dt,1) END DO END IF END IF @@ -425,7 +425,7 @@ SUBROUTINE run_td_sim(self,dt,nsteps,vec,direct,nplot,sensors) CALL u%get_local(vals) IF(ntimes_curr>0)THEN DO j=1,self%n_icoils - icoil_curr(j)=linterp(curr_waveform(:,1),curr_waveform(:,j+1),ntimes_curr,t) + icoil_curr(j)=linterp(curr_waveform(:,1),curr_waveform(:,j+1),ntimes_curr,t,1) END DO END IF IF(sensors%nfloops>0)THEN @@ -531,7 +531,7 @@ SUBROUTINE plot_td_sim(self,dt,nsteps,nplot,sensors) t=0.d0 IF(ntimes_curr>0)THEN DO j=1,self%n_icoils - coil_vec(j)=linterp(coil_waveform(:,1),coil_waveform(:,j+1),ntimes_curr,t) + coil_vec(j)=linterp(coil_waveform(:,1),coil_waveform(:,j+1),ntimes_curr,t,1) END DO END IF ! @@ -600,7 +600,7 @@ SUBROUTINE plot_td_sim(self,dt,nsteps,nplot,sensors) IF(MOD(i,nplot)==0)THEN IF(ntimes_curr>0)THEN DO j=1,self%n_icoils - coil_vec(j)=linterp(coil_waveform(:,1),coil_waveform(:,j+1),ntimes_curr,t) + coil_vec(j)=linterp(coil_waveform(:,1),coil_waveform(:,j+1),ntimes_curr,t,1) END DO END IF ! diff --git a/src/docs/thincurr_example2.md b/src/docs/thincurr_example2.md index e5d4d04..f66dbaf 100644 --- a/src/docs/thincurr_example2.md +++ b/src/docs/thincurr_example2.md @@ -67,8 +67,7 @@ ToDo: Describe current potential "holes" **Coil current waveform file** (`curr.drive`) \verbatim -2 4 --1.0 1.E3 +2 3 0.0 1.E3 1.E-4 0.0 1.0 0.0 diff --git a/src/examples/ThinCurr/cyl/curr.drive b/src/examples/ThinCurr/cyl/curr.drive index ece83ac..1345d10 100644 --- a/src/examples/ThinCurr/cyl/curr.drive +++ b/src/examples/ThinCurr/cyl/curr.drive @@ -1,5 +1,4 @@ -2 4 --1.0 1.E3 +2 3 0.0 1.E3 1.E-4 0.0 1.0 0.0 diff --git a/src/tests/physics/test_thin_wall.py b/src/tests/physics/test_thin_wall.py index 7506944..6843563 100644 --- a/src/tests/physics/test_thin_wall.py +++ b/src/tests/physics/test_thin_wall.py @@ -222,11 +222,11 @@ def validate_td(sigs_final, tols=(1.E-8, 1.E-3)): # Test runners for time-dependent cases @pytest.mark.parametrize("direct_flag", ('F', 'T')) def test_td_plate(direct_flag): - sigs_final = (4.E-3, 8.442894E-4, 7.118076E-4) + sigs_final = (4.E-3, 8.459371E-4, 7.130923E-4) assert thin_wall_setup("tw_test-plate.h5",1,direct_flag, icoils=((0.5, 0.1),), floops=((0.5, -0.05), (0.5, -0.1)), - curr_waveform=((-1.0, -1.0), (0.0, 0.0), (1.0, 1.0))) + curr_waveform=((-1.0, 0.0), (0.0, 0.0), (1.0, 1.0))) assert validate_td(sigs_final) @pytest.mark.parametrize("direct_flag", ('F', 'T')) @@ -235,16 +235,16 @@ def test_td_plate_volt(direct_flag): assert thin_wall_setup("tw_test-plate.h5",1,direct_flag, vcoils=((0.5, 0.1),), floops=((0.5, -0.05), (0.5, -0.1)), - volt_waveform=((-1.0, 1.0), (0.0, 1.0), (1.0, 1.0))) + volt_waveform=((0.0, 1.0), (1.0, 1.0))) assert validate_td(sigs_final) @pytest.mark.parametrize("direct_flag", ('F', 'T')) def test_td_cyl(direct_flag): - sigs_final = (4.E-3, 7.178084E-4, 6.040163E-4) + sigs_final = (4.E-3, 7.254196E-4, 6.151460E-4) assert thin_wall_setup("tw_test-cyl.h5",1,direct_flag, icoils=((1.1, 0.25), (1.1, -0.25)), floops=((0.9, 0.5), (0.9, 0.0)), - curr_waveform=((-1.0, -1.0), (0.0, 0.0), (1.0, 1.0))) + curr_waveform=((-1.0, 0.0), (0.0, 0.0), (1.0, 1.0))) assert validate_td(sigs_final) @pytest.mark.parametrize("direct_flag", ('F', 'T')) @@ -253,17 +253,17 @@ def test_td_cyl_volt(direct_flag): assert thin_wall_setup("tw_test-cyl.h5",1,direct_flag, vcoils=((1.1, 0.25), (1.1, -0.25)), floops=((0.9, 0.5), (0.9, 0.0)), - volt_waveform=((-1.0, 1.0, 1.0), (0.0, 1.0, 1.0), (1.0, 1.0, 1.0))) + volt_waveform=((0.0, 1.0, 1.0), (1.0, 1.0, 1.0))) assert validate_td(sigs_final) @pytest.mark.coverage @pytest.mark.parametrize("direct_flag", ('F', 'T')) def test_td_torus(direct_flag): - sigs_final = (4.E-3, 4.772879E-4, 3.408103E-5) + sigs_final = (4.E-3, 4.935683E-4, 3.729159E-5) assert thin_wall_setup("tw_test-torus.h5",1,direct_flag, icoils=((1.5, 0.5), (1.5, -0.5)), floops=((1.4, 0.0), (0.6, 0.0)), - curr_waveform=((-1.0, -1.0), (0.0, 0.0), (1.0, 1.0))) + curr_waveform=((-1.0, 0.0), (0.0, 0.0), (1.0, 1.0))) assert validate_td(sigs_final) @pytest.mark.coverage @@ -273,18 +273,18 @@ def test_td_torus_volt(direct_flag): assert thin_wall_setup("tw_test-torus.h5",1,direct_flag, vcoils=((1.5, 0.5), (1.5, -0.5)), floops=((1.4, 0.0), (0.6, 0.0)), - volt_waveform=((-1.0, 1.0, 1.0), (0.0, 1.0, 1.0), (1.0, 1.0, 1.0))) + volt_waveform=((0.0, 1.0, 1.0), (1.0, 1.0, 1.0))) assert validate_td(sigs_final) @pytest.mark.coverage @pytest.mark.parametrize("direct_flag", ('F', 'T')) def test_td_passive(direct_flag): - sigs_final = (4.E-3, 7.685703E-4, 7.888816E-4) + sigs_final = (4.E-3, 7.706778E-4, 7.903190E-4) assert thin_wall_setup("tw_test-passive.h5",1,direct_flag,eta=1.E4, icoils=((0.5, 0.1),), vcoils=((0.5, 0.0),), floops=((0.5, -0.05), (0.5, -0.1)), - curr_waveform=((-1.0, -1.0), (0.0, 0.0), (1.0, 1.0))) + curr_waveform=((-1.0, 0.0), (0.0, 0.0), (1.0, 1.0))) assert validate_td(sigs_final) @pytest.mark.coverage @@ -294,7 +294,7 @@ def test_td_passive_volt(direct_flag): assert thin_wall_setup("tw_test-passive.h5",1,direct_flag,eta=1.E4, vcoils=((0.5, 0.0), (0.5, 0.1)), floops=((0.5, -0.05), (0.5, -0.1)), - volt_waveform=((-1.0, 0.0, 1.0), (0.0, 0.0, 1.0), (1.0, 0.0, 1.0))) + volt_waveform=((0.0, 0.0, 1.0), (1.0, 0.0, 1.0))) assert validate_td(sigs_final) #============================================================================