Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regression tests for solver examples #282

Draft
wants to merge 32 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
5b38532
Move solver_test_utils to test/common for reuse in regression tests.
oparry-ukaea Jan 21, 2025
0111a61
Rejig test fixture inheritance.
oparry-ukaea Jan 28, 2025
b42d6e5
Start regression data generator script. Reads a .fld file and writes …
oparry-ukaea Jan 28, 2025
7e3a4ed
Start regression test framework.
oparry-ukaea Jan 28, 2025
dc5f09b
Merge branch 'main' into tmp/eg_regression_tests
oparry-ukaea Jan 28, 2025
8337902
Move SOLWithParticlesSystem::solver_callback_handler so that it can b…
oparry-ukaea Jan 29, 2025
fdccec6
Add SolverRegTest::run_and_regress() to run a solver, read the final …
oparry-ukaea Feb 3, 2025
061720f
Add const qualifiers to test member funcs where possible.
oparry-ukaea Feb 3, 2025
68a2d37
Make test class member func args const ref where possible.
oparry-ukaea Feb 3, 2025
bbdc93d
More useful comments in solver test classes.
oparry-ukaea Feb 3, 2025
d72fa91
Finish regression data generator script.
oparry-ukaea Feb 4, 2025
2faaaaa
Remove some stdout output from solver tests.
oparry-ukaea Feb 3, 2025
62f93c6
Add SimpleSOLRegTest.2D
oparry-ukaea Feb 4, 2025
8476600
Data for SimpleSOLRegTest.1D
oparry-ukaea Feb 4, 2025
4da1ede
Fix SimpleSOL/2DWithParticles example.
oparry-ukaea Feb 3, 2025
5d3c565
Fix lambda return type in SolverRegTest::run_and_regress().
oparry-ukaea Feb 4, 2025
7f06fa0
Slightly relax regression test tolerance to accommodate small gcc/int…
oparry-ukaea Feb 4, 2025
ce0f438
Add SimpleSOLRegTest.2DWithParticles, disabled for now.
oparry-ukaea Feb 4, 2025
5a6c6db
Adjust regression data location.
oparry-ukaea Feb 4, 2025
924bf1a
Option to read and print regression data in gen_eg_regression_data.py
oparry-ukaea Feb 4, 2025
7191b5a
Fix logic in reporting regression data read errors.
oparry-ukaea Feb 4, 2025
a453519
Add Diffusion solver regression test.
oparry-ukaea Feb 4, 2025
724c512
Cope with egs that have more than one possible xml config in gen_eg_r…
oparry-ukaea Feb 4, 2025
f7eb2dc
Add 2D R&R regression test.
oparry-ukaea Feb 4, 2025
bea8ab9
More debugging info when regression tests fail.
oparry-ukaea Feb 4, 2025
6b1c783
Option to print some values from stored regression data.
oparry-ukaea Feb 4, 2025
99072c2
Add some xml meshes to version control to simplify example regression…
oparry-ukaea Feb 4, 2025
bf37d23
Remove the "poisson" example, which was associated with the now-defun…
oparry-ukaea Feb 4, 2025
ab64af5
Rename some example directories to avoid complications with dashes in…
oparry-ukaea Feb 4, 2025
ba5f130
Consistent naming of example directories.
oparry-ukaea Feb 5, 2025
0cdbc5e
Add HW tests; no data yet.
oparry-ukaea Feb 5, 2025
b8d3186
Skip incomplete tests (and add placeholder test data) .
oparry-ukaea Feb 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion examples/H3LAPD/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# Ignore xml files generated from .geos
cuboid*.xml
low_res.xml
full_res.xml
9,573 changes: 9,573 additions & 0 deletions examples/H3LAPD/2Din3DHW/cuboid.xml

Large diffs are not rendered by default.

File renamed without changes.
2,577 changes: 2,577 additions & 0 deletions examples/H3LAPD/2Din3DHW_fluid_only/cuboid.xml

Large diffs are not rendered by default.

2,577 changes: 2,577 additions & 0 deletions examples/H3LAPD/3DHW/cuboid.xml

Large diffs are not rendered by default.

File renamed without changes.
8 changes: 4 additions & 4 deletions examples/H3LAPD/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,22 @@ Then run the example with

This script expects to find mpirun on the path and executes with four MPI ranks by default. It looks for a solver executable in the most recently modified spack-build* directory, but this can be overridden using the '-b' option.

### 2Din3D-hw
### 2Din3DHW

Solves equations (1) and (2), as in the previous example, but also enables a system of neutral particles that are coupled to the fluid solver. Particles deposit density into the (plasma) fluid via ionization.

Generate the mesh with

./scripts/geo_to_xml.sh examples/H3LAPD/2Din3D-hw/cuboid_periodic_8x8x16.geo -x 1,2 -y 3,4 -z 5,6 -o cuboid.xml
./scripts/geo_to_xml.sh examples/H3LAPD/2Din3DHW/cuboid_periodic_8x8x16.geo -x 1,2 -y 3,4 -z 5,6 -o cuboid.xml

Then run the example with

./scripts/run_eg.sh H3LAPD 2Din3D-hw
./scripts/run_eg.sh H3LAPD 2Din3DHW

This script expects to find mpirun on the path and executes with four MPI ranks by default. It looks for a solver executable in the most recently modified spack-build* directory, but this can be overridden using the '-b' option.

## Diagnostics
For the '2Din3DHW' equation system (used in the `2Din3D-hw` and `2Din3D-hw_fluid-only` examples), the solver can be made to output the total fluid energy ($E$) and enstrophy ($W$), which are defined as:
For the '2Din3DHW' equation system (used in the `2Din3DHW` and `2Din3D-hw_fluid-only` examples), the solver can be made to output the total fluid energy ($E$) and enstrophy ($W$), which are defined as:

$$
\begin{align}
Expand Down
44 changes: 18 additions & 26 deletions examples/SimpleSOL/2DWithParticles/2DWithParticles_config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,23 @@
<P> pInf = 1.0 </P>
<P> rhoInf = 1.0 </P>
<P> uInf = 1.0 </P>

<P> num_particle_steps_per_fluid_step = 1 </P>
<!--
Note that, for the SOLWithParticles equation system, num_particles_total sets the
number of particles added to the simulation per timestep per MPI rank, rather than
the total number of particles that will be added.
-->
<P> num_particles_total = 100 </P>
<P> num_particles_per_cell = -1 </P>
<P> particle_output_freq = 4 </P>
<P> particle_thermal_velocity = 1.0 </P>
<P> particle_number_density = 3e18 </P>
<P> particle_source_region_count = 2 </P>
<P> particle_source_region_offset = 0.01 </P>
<P> particle_source_region_gaussian_width = 0.00001 </P>
<P> particle_source_lines_per_gaussian = 7 </P>
<P> unrotated_x_max = 110.0 </P>
<P> unrotated_y_max = 1.0 </P>
</PARAMETERS>
<SOLVERINFO>
<I PROPERTY="EQTYPE" VALUE="SOLWithParticles" />
Expand Down Expand Up @@ -99,7 +115,7 @@

<PARTICLES>
<INFO>
<I PROPERTY="PARTTYPE" VALUE="NeutralParticleSystem"/>
<I PROPERTY="PARTTYPE" VALUE="SOLParticleSystem"/>
</INFO>

<PARAMETERS>
Expand All @@ -121,29 +137,5 @@
<P> unrotated_x_max = 110.0 </P>
<P> unrotated_y_max = 1.0 </P>
</PARAMETERS>

<SPECIES>
<S ID="0" NAME="Neutral">
<P PROPERTY="Mass" VALUE="1.0"/>
<P PROPERTY="Charge" VALUE="0"/>
<P PROPERTY="Number" VALUE="100"/>
</S>
</SPECIES>

<BOUNDARYINTERACTION>
<REGION REF="0">
<C SPECIES="Neutral" VALUE="0.0" />
</REGION>
<REGION REF="1">
<C SPECIES="Neutral" VALUE="0.0" />
</REGION>
<REGION REF="2">
<P SPECIES="Neutral" VALUE="[3]" />
</REGION>
<REGION REF="3">
<P SPECIES="Neutral" VALUE="[2]" />
</REGION>
</BOUNDARYINTERACTION>

</PARTICLES>
</NEKTAR>
99 changes: 0 additions & 99 deletions examples/poisson/Poisson2D_4Elem.xml

This file was deleted.

6 changes: 0 additions & 6 deletions examples/poisson/README.md

This file was deleted.

159 changes: 159 additions & 0 deletions python/regression_data_generator/gen_eg_regression_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import argparse
import glob
import h5py
import math
import os.path
import sys

from NekPy.FieldUtils import Field, InputModule, ProcessModule

examples_root = os.path.normpath(sys.path[0] + "/../../examples")
eg_reg_tests_dir = os.path.normpath(sys.path[0] + "/../../test/regression/examples")


def determine_config_paths(solver_name, eg_name):
cmd_template_path = os.path.join(
examples_root, solver_name, eg_name, "run_cmd_template.txt"
)
config_fpaths = []
if os.path.exists(cmd_template_path):
with open(cmd_template_path, "r") as cmd_template:
line = cmd_template.readline()
config_fpaths = [
os.path.join(examples_root, solver_name, eg_name, elmt)
for elmt in line.split()
if elmt.endswith(".xml")
]

assert (
len(config_fpaths) > 0
), f"Unable to determine xml file paths from command template at {cmd_template}"
return config_fpaths


def get_nsteps(session, tol=1e-12):
"""
Extract number of steps from the nektar session.
"""
nsteps_str = "NumSteps"
if session.DefinesParameter(nsteps_str):
return int(session.GetParameter(nsteps_str))
else:
exit(f"Expected {nsteps_str} to be defined in the example's session file")


def read_nektar_fld_and_gen_pts(example_runs_root, solver_name, eg_name):
"""
1. Look for xml files and a .fld file in run_dir
2. Use nektar's FieldConvert modules to generate equispaced points from the .fld data
3. Write the points
"""
config_fpaths = determine_config_paths(solver_name, eg_name)
run_dir = os.path.join(example_runs_root, solver_name, eg_name)
fld_fpath = glob.glob(os.path.join(run_dir, "*.fld"))
assert len(fld_fpath) > 0, f"No .fld file in {run_dir}"
assert len(fld_fpath) == 1, f"Found multiple .fld files in {run_dir}"

# Init Field object
field = Field(config_fpaths)

# Read config
InputModule.Create("xml", field, *config_fpaths).Run()

# Read fld file
InputModule.Create("fld", field, fld_fpath[0]).Run()

# Compute equi-spaced points
ProcessModule.Create("equispacedoutput", field).Run()

# Return points in a dict to simplify output later
ndims = field.graph.GetSpaceDimension()
nsteps = get_nsteps(field.session)
return nsteps, {
fld_name: field.GetPts(ndims + fld_idx)
for fld_idx, fld_name in enumerate(field.session.GetVariables())
}


def user_confirms(msg: str) -> bool:
opts = ("y", "n")
opt_str = " or ".join(opts)
while (
answer := input(f"{msg} (Choose {opt_str})").strip().lower()[:1]
) not in opts:
print(f"Invalid input; choose {opt_str}")

return answer == opts[0]


def data_dir(solver_name: str):
return os.path.join(eg_reg_tests_dir, solver_name)


def reg_data_fname(eg_name: str):
return f"{eg_name}.regression_data.h5"


def gen_eg_regression_data(
solver_name: str, eg_name: str, attrs={}, example_runs_root=None
):
eg_reg_data_dir = data_dir(solver_name)
os.makedirs(eg_reg_data_dir, exist_ok=True)

if example_runs_root is None:
example_runs_root = os.path.normpath(sys.path[0] + "/../../runs")
nsteps, fld_data = read_nektar_fld_and_gen_pts(
example_runs_root, solver_name, eg_name
)
attrs["nsteps"] = nsteps

pth = os.path.join(eg_reg_data_dir, reg_data_fname(eg_name))
if os.path.exists(pth):
if not user_confirms(f"Overwrite file at {pth}?"):
print("Aborted.")
return
with h5py.File(pth, "w") as fh:
for fld_name, fld_vals in fld_data.items():
fh.create_dataset(fld_name, data=fld_vals)
fh.attrs.update(attrs)
print(f"Wrote regression data to {pth}")


def read_regression_data(solver_name: str, eg_name: str, nvals: int):
pth = os.path.join(data_dir(solver_name), reg_data_fname(eg_name))
if not os.path.exists(pth):
print(f"No regression data found at {pth}")
return

print(f"Reading regression data at {pth}")
with h5py.File(pth, "r") as data:
print(f" nsteps = {data.attrs['nsteps']}")
print(f" Field data:")
for fld_name, fld_data in data.items():
print(f" {fld_name}: {fld_data}")
print(f" {','.join([str(v) for v in [fld_data[:nvals]]])}")


if __name__ == "__main__":
parser = argparse.ArgumentParser(
description=f"Extract data from a Nektar++ .fld file (generated by running one of the solver examples) and write it to an HDF5 file in {eg_reg_tests_dir}/[solver_name]/[example_name]",
)
parser.add_argument("solver_name")
parser.add_argument("example_name")
parser.add_argument(
"-r",
"--read",
action="store_true",
help="read regression data and output some of its properties",
)
parser.add_argument(
"-n",
"--nvals",
type=int,
help="number of values to print from stored data; ignored unless -r/--read was supplied",
)
args = parser.parse_args(args=None if sys.argv[1:] else ["--help"])
if args.read:
read_regression_data(args.solver_name, args.example_name, args.nvals)
else:
gen_eg_regression_data(args.solver_name, args.example_name)
Loading