diff --git a/.gitattributes b/.gitattributes index dda8bcaaf..73e0aca5d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -27,3 +27,4 @@ test/ref_solns/interpInlet/restart_output.sol.h5 filter=lfs diff=lfs merge=lfs - test/meshes/spongeBox.msh filter=lfs diff=lfs merge=lfs -text test/ref_solns/sgsLoMach/restart_output.sol.h5 filter=lfs diff=lfs merge=lfs -text test/ref_solns/aveLoMach/restart_output.sol.h5 filter=lfs diff=lfs merge=lfs -text +test/ref_solns/flow1d_coupling.sol.h5 filter=lfs diff=lfs merge=lfs -text diff --git a/docker/test/Dockerfile b/docker/test/Dockerfile index d3869b629..3ec07450a 100644 --- a/docker/test/Dockerfile +++ b/docker/test/Dockerfile @@ -30,6 +30,7 @@ RUN yum -y install boost-gnu9-mpich-ohpc RUN yum -y install python38-pip RUN pip3 install matplotlib +RUN pip3 install h5py RUN yum -y install procps-ng RUN yum -y install diffutils diff --git a/src/flow1d-coupling.py b/src/flow1d-coupling.py deleted file mode 100644 index c4801c937..000000000 --- a/src/flow1d-coupling.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python3 -import sys -import os -import numpy as np -from mpi4py import MPI -# set path to C++ TPS library -path = os.path.abspath(os.path.dirname(sys.argv[0])) -sys.path.append(path + "/.libs") -import libtps - -comm = MPI.COMM_WORLD -# TPS solver -tps = libtps.Tps(comm) -tps.parseCommandLineArgs(sys.argv) -tps.parseInput() -tps.chooseDevices() - -# 1d flow - 2d EM interface -interface = libtps.Qms2Flow1d(tps) -# Initialize MFEM vectors with number of 1d points -n_points = 1000 -interface.initialize(n_points) - -# Access vectors through NumPy -cond_1d = np.array(interface.PlasmaConductivity1d(), copy = False) -joule_1d = np.array(interface.JouleHeating1d(), copy = False) -radius_1d = np.array(interface.TorchRadius1d(), copy = False) -z_1d = np.array(interface.Coordinates1d(), copy = False) - -# Example vector data -R_TORCH = 2.75e-2 -z_1d[0:n_points] = np.linspace(0, 0.315, n_points) -cond_1d[0:n_points] = np.ones(n_points) -radius_1d[0:n_points] = R_TORCH - -# Solve -interface.set_n_interp(100) -interface.solve() - -# Evaluate Joule heating -tot_1d = interface.total_joule_1d()/1e3 -tot_2d = interface.total_joule_2d()/1e3 -error = 100*np.abs(tot_1d - tot_2d)/np.abs(tot_2d) - -print(f'Total 2d Joule heating [kW]: {tot_2d:.2f}') -print(f'Total 1d Joule heating [kW]: {tot_1d:.2f}') -print(f'Error [%]: {error:.3e}') diff --git a/test/Makefile.am b/test/Makefile.am index 5137f5874..f6dee9e7c 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -34,6 +34,7 @@ EXTRA_DIST = tap-driver.sh test_tps_splitcomm.py soln_differ inputs meshes lte- ref_solns/aveLoMach/*.h5 \ ref_solns/reactBinDiff/*.h5 \ ref_solns/reactSingleRx/*.h5 \ + ref_solns/flow1d_coupling.sol.h5 \ vpath.sh die.sh count_gpus.sh sniff_mpirun.sh \ cyl3d.gpu.test cyl3d.mflow.gpu.test wedge.gpu.test \ averaging.gpu.test cyl3d.test cyl3d.gpu.python.test cyl3d.mflow.test cyl3d.dtconst.test \ @@ -49,7 +50,8 @@ EXTRA_DIST = tap-driver.sh test_tps_splitcomm.py soln_differ inputs meshes lte- sgsSmag.test sgsSigma.test heatEq.test sponge.test plate.test pipe.mix.test lte2noneq-restart.test \ coupled-3d.interface.test plasma.axisym.test plasma.axisym.lte1d.test \ lomach-flow.test lomach-lequere.test interpInlet.test sgsLoMach.test autoPeriodic.test aveLoMach.test \ - reactFlow-binDiff.test reactFlow-singleRx.test + reactFlow-binDiff.test reactFlow-singleRx.test \ + test_flow1d_coupling.py flow1d-coupling.python.test TESTS = vpath.sh XFAIL_TESTS = @@ -126,6 +128,11 @@ if PYTHON_ENABLED TESTS += cyl3d.python.test \ cyl3d.python.splitcomm.test \ coupled-3d.py-loop.test + +if GSLIB_ENABLED +TESTS += flow1d-coupling.python.test +endif + endif endif diff --git a/test/flow1d-coupling.python.test b/test/flow1d-coupling.python.test new file mode 100755 index 000000000..772251063 --- /dev/null +++ b/test/flow1d-coupling.python.test @@ -0,0 +1,41 @@ +#!./bats +# -*- mode: sh -*- + +TEST="flow1d-coupling" +RUNFILE="inputs/qms-axisym.flow1dcoupling.ini" +EXE="./test_flow1d_coupling.py" + +setup() { + SOLN_FILE=flow1d_coupling.sol.h5 + REF_FILE=ref_solns/flow1d_coupling.h5 +} + +@test "[$TEST] check for input file $RUNFILE" { + test -s $RUNFILE +} + +@test "[$TEST] run interface with input -> $RUNFILE" { + rm -f $SOLN_FILE + $EXE --runFile $RUNFILE + test -s $SOLN_FILE +} + +@test "[$TEST] verify interface output with input -> $RUNFILE" { + test -s $SOLN_FILE + test -s $REF_FILE + h5diff --report --relative=1e-12 $SOLN_FILE $REF_FILE /output/joule_heating +} + +@test "[$TEST] verify Joule heating is read only" { + # Should raise error + run WRITE_JOULE=True $EXE --runFile $RUNFILE + [ "$status" -ne 0 ] +} + +@test "[$TEST] verify total power input is 20 kW with input -> $RUNFILE { + TOTAL_POWER=True $EXE --runFile $RUNFILE +} + +@test "[$TEST] verify 1d and 2d total power inputs are equal with input -> $RUNFILE { + COMPARE_JOULE=True $EXE --runFile $RUNFILE +} \ No newline at end of file diff --git a/test/inputs/qms-axisym.flow1dcoupling.ini b/test/inputs/qms-axisym.flow1dcoupling.ini new file mode 100644 index 000000000..b653a627b --- /dev/null +++ b/test/inputs/qms-axisym.flow1dcoupling.ini @@ -0,0 +1,26 @@ +#--------------------- +# TPS runtime controls +#--------------------- + +# choice of solver +[solver] +type = em-axi # options are (flow, em, em-axi, or coupled) + +# controls for standalone EM-only solver +[em] +mesh = meshes/torch-em-coarse2.msh # mesh filename +order = 2 # FE order (polynomial degree) +max_iter = 60 # max number of iterations +rtol = 1.0e-13 # solver relative tolerance +atol = 1.0e-15 # solver absolute tolerance +top_only = false # run current through top branch only +bot_only = false # run current through bottom branch only +yinterp_min = -2.0 # minimum y interpolation value +yinterp_max = 2.0 # maximum y interpolation value +nBy = 129 # of interpolation points +By_file = ref_solns/By.h5 # file for By interpolant output + +# chosen for ~20 kW power input +current_amplitude = 4.575e7 # A/m^2 +current_frequency = 6e6 # 1/s +permeability = 1.25663706e-6 # m * kg / s^2 / A^2 \ No newline at end of file diff --git a/test/test_flow1d_coupling.py b/test/test_flow1d_coupling.py new file mode 100755 index 000000000..d0a593f55 --- /dev/null +++ b/test/test_flow1d_coupling.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +""" This is a driver to test the interface between a Python 1d flow solver + and the 2d axisymmetric EM solver. The 1d flow solver is emulated by defining + the plasma conductivity""" +import sys +import os +import numpy as np +import h5py +from mpi4py import MPI +# set path to C++ TPS library +path = os.path.abspath(os.path.dirname(sys.argv[0])) +sys.path.append(path + "/../src/.libs") +import libtps + +comm = MPI.COMM_WORLD +# TPS solver +tps = libtps.Tps(comm) +tps.parseCommandLineArgs(sys.argv) +tps.parseInput() +tps.chooseDevices() + +# 1d flow - 2d EM interface +interface = libtps.Qms2Flow1d(tps) + +# Initialize MFEM vectors with number of 1d points +N_POINTS = 1000 +interface.initialize(N_POINTS) + +# Access vectors through NumPy +cond_1d = np.array(interface.PlasmaConductivity1d(), copy = False) +joule_1d = np.array(interface.JouleHeating1d(), copy = False) +radius_1d = np.array(interface.TorchRadius1d(), copy = False) +z_1d = np.array(interface.Coordinates1d(), copy = False) + +# Check that Joule heating is read only +if 'WRITE_JOULE' in os.environ: + joule_1d[0] = 1 + +# Example vector data +R_TORCH = 2.75e-2 +L_TORCH = 0.315 +z_1d[0:N_POINTS] = np.linspace(0, L_TORCH, N_POINTS) +cond_1d[0:N_POINTS] = np.ones(N_POINTS) +radius_1d[0:N_POINTS] = R_TORCH + +# Solve +N_INTERP = 100 +interface.set_n_interp(N_INTERP) +interface.solve() + +if 'TOTAL_POWER' in os.environ: + power = interface.total_joule_2d()/1e3 + assert np.abs(power - 20)/20 < 0.05, 'Total power should be 20 kW' + +if 'COMPARE_JOULE' in os.environ: + power_1d = interface.total_joule_1d()/1e3 + power_2d = interface.total_joule_2d()/1e3 + error = np.abs(power_1d - power_2d)/np.abs(power_2d) + assert error < 1e-5, '1d and 2d Joule heating integrals should match' + +# Save output with hdf5 +with h5py.File("flow1d_coupling.sol.h5", "w") as f: + _ = f.create_dataset('input/axial_coordinates', data=z_1d) + _ = f.create_dataset('input/torch_radius', data=radius_1d) + _ = f.create_dataset('input/plasma_conductivity', data=cond_1d) + _ = f.create_dataset('output/joule_heating', data=joule_1d) diff --git a/test/vpath.sh b/test/vpath.sh index af1e5eead..c4557e6e2 100755 --- a/test/vpath.sh +++ b/test/vpath.sh @@ -32,6 +32,7 @@ fi # necessary binaries binaries="bats die.sh soln_differ count_gpus.sh sniff_mpirun.sh " binaries+="../src/tps.py ../src/tps-time-loop.py ../src/tps-bte_0d3v.py ../test/test_tps_splitcomm.py" +binaries+=" ../test/test_flow1d_coupling.py" for binary in $binaries; do if [ ! -x $binary ];then if [ -x $testDir/$binary ];then