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

Derived test module reloads and cleanup #14

Merged
merged 12 commits into from
Nov 25, 2024
9 changes: 5 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,19 @@ jobs:
python3 run_tests.py regression.suite \
--save_build \
--reuse_build \
--use_oversubscribe \
--log_file=ci_cpu_log.txt
- name: Upload CPU test log
- name: Upload logs
if: always()
uses: actions/upload-artifact@v3
with:
name: ci_cpu_log.txt
path: tst/ci_cpu_log.txt
name: logs
path: tst/testing/logs
retention-days: 3
- name: Upload figures
if: always()
uses: actions/upload-artifact@v3
with:
name: figs
path: tst/figs
path: tst/testing/figs
retention-days: 3
198 changes: 106 additions & 92 deletions tst/run_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
# the public, perform publicly and display publicly, and to permit others to do so.
# ========================================================================================

# This file was created in part by one of OpenAI's generative AI models

# Regression test script for Artemis.

# Usage: From this directory, call this script with python:
Expand All @@ -24,14 +22,16 @@
# - This file should not be modified when adding new scripts.
# - This file is largely borrowed from the open-source Athena++/AthenaK CI regression,
# adapted to work with LANL Darwin runners
# - This file was created in part by one of OpenAI's generative AI models

# Modules
import argparse
import os
from collections import OrderedDict
from importlib import reload
import logging
import logging.config
import shutil
from collections import OrderedDict
from importlib import reload
from pkgutil import iter_modules
from timeit import default_timer as timer

Expand All @@ -48,16 +48,10 @@
logger = logging.getLogger("artemis")


def read_cmakecache(filename):
with open(filename, "r") as f:
for line in f.readlines():
if "artemis_BINARY_DIR" in line:
return os.path.join(line.split("=")[-1].strip(), "src", "artemis")


def process_suite(filename):
tests = []
fname = os.path.join(artemis.get_source_directory(), "tst", "suites", filename)
aname = os.path.join(artemis.get_artemis_dir(), "tst")
fname = os.path.join(aname, "suites", filename)

with open(fname, "r") as f:
for line in f.readlines():
Expand All @@ -75,15 +69,7 @@ def process_suite(filename):
dir_test_names = [
name
for _, name, _ in iter_modules(
# path=["scripts/" + test_name], prefix=test_name + "."
path=[
os.path.join(
artemis.get_source_directory(),
"tst",
"scripts",
test_name,
)
],
path=[os.path.join(aname, "scripts", test_name)],
prefix=test_name + ".",
)
]
Expand All @@ -94,22 +80,16 @@ def process_suite(filename):
# Main function
def main(**kwargs):
# Make list of tests to run
scripts_path = os.path.join(artemis.get_artemis_dir(), "tst/scripts/")
tests = kwargs.pop("tests")
test_names = []
if len(tests) == 0: # run all tests
for _, directory, ispkg in iter_modules(path=["scripts"]):
if ispkg and (directory != "utils" and directory != "style"):
for _, directory, ispkg in iter_modules(path=[scripts_path]):
if ispkg and directory != "utils":
dir_test_names = [
name
for _, name, _ in iter_modules(
path=[
os.path.join(
artemis.get_source_directory(),
"tst",
"scripts",
directory,
)
],
path=[os.path.join(scripts_path, directory)],
prefix=directory + ".",
)
]
Expand All @@ -126,11 +106,7 @@ def main(**kwargs):
dir_test_names = [
name
for _, name, _ in iter_modules(
path=[
os.path.join(
artemis.get_source_directory(), "tst", "scripts", test
)
],
path=[os.path.join(scripts_path, test)],
prefix=test + ".",
)
]
Expand All @@ -141,7 +117,7 @@ def main(**kwargs):
logger.info("Running: " + ", ".join(test_names))

# Run tests
current_dir = os.getcwd()
build_dir = os.path.join(artemis.get_artemis_dir(), "tst")
test_times = []
test_results = []
test_errors = []
Expand All @@ -164,21 +140,28 @@ def main(**kwargs):
logger.warning("Unable to " 'import "{:}".'.format(missing_module))
deps_installed = False
if not deps_installed:
logger.warning("WARNING! Not all required Python modules " "are available")
logger.warning("WARNING! Not all required Python modules are available")

# Build Artemis
if not artemis.custom_exe and not kwargs.pop("reuse_build"):
if not artemis.get_supplied_exe() and not kwargs.pop("reuse_build"):
try:
os.system("rm -rf {0}/build".format(current_dir))
os.system("rm -rf {0}/build".format(build_dir))
# insert arguments for artemis.make()
artemis_cmake_args = kwargs.pop("cmake")
artemis_make_nproc = kwargs.pop("make_nproc")
module.artemis.make(artemis_cmake_args, artemis_make_nproc)
artemis.make(artemis_cmake_args, artemis_make_nproc)
except Exception:
logger.error("Exception occurred", exc_info=True)
test_errors.append("make()")
raise TestError("Unable to build Artemis")

# Build working directory and copy librebound.so into working directory
os.makedirs(artemis.get_outputs_dir(), exist_ok=True)
reb_path = os.path.join(artemis.get_exe_dir(), "librebound.so")
if not os.path.exists(reb_path):
raise TestError(f'librebound.so not found at "{reb_path}"!')
shutil.copy(reb_path, artemis.get_outputs_dir())

# Run each test
for name in test_names:
t0 = timer()
Expand Down Expand Up @@ -215,8 +198,8 @@ def main(**kwargs):
# For CI, print after every individual test has finished
logger.info("{} test: run(), analyze() finished".format(name))
finally:
if not kwargs.pop("save_build") and not artemis.custom_exe:
os.system("rm -rf {0}/build".format(current_dir))
if not kwargs.pop("save_build") and not artemis.get_supplied_exe():
os.system("rm -rf {0}/build".format(build_dir))

# Report test results
logger.info("\nResults:")
Expand Down Expand Up @@ -269,7 +252,8 @@ def log_init(args):
# setup log_file
log_fn = kwargs.pop("log_file")
if log_fn:
f_handler = logging.FileHandler(os.path.join(artemis.artemis_log_dir, log_fn))
os.makedirs(artemis.get_log_dir(), exist_ok=True)
f_handler = logging.FileHandler(os.path.join(artemis.get_log_dir(), log_fn))
f_handler.setLevel(0) # log everything
f_format = logging.Formatter(
"%(asctime)s|%(levelname)s" ":%(name)s: %(message)s"
Expand All @@ -279,61 +263,85 @@ def log_init(args):
logger.debug("Starting Artemis regression tests")


def set_paths(args):
# Reads Artemis CMakeCache.txt to find executable
def read_cmakecache(filename):
with open(filename, "r") as f:
for line in f.readlines():
if "artemis_BINARY_DIR" in line:
return os.path.join(line.split("=")[-1].strip(), "src", "artemis")


# Sets global variables
def set_globals(args):
kwargs = vars(args)

# Set MPI oversubscribe
if kwargs.pop("use_oversubscribe"):
artemis.set_mpi_oversubscribe(True)

# Check for executable path and output directory args
out_dir = kwargs.pop("output_dir")
out_dir = os.path.join(out_dir, "testing") if out_dir is not None else None
exe_path = kwargs.pop("exe")
cwd = os.getcwd()

# Set the correct paths
artemis_exe_path = kwargs.pop("exe")
output_dir = kwargs.pop("output_dir")

# Check that path is valid
if output_dir is not None:
if not (os.path.exists(output_dir) and os.access(output_dir, os.W_OK)):
raise TestError(
f'Provided output directory "{output_dir}" not found or it is not writeable!'
)
# output_dir is a valid path or None

if artemis_exe_path is not None:
# Check that path is valid
if not (
os.path.exists(artemis_exe_path) and os.access(artemis_exe_path, os.X_OK)
):
raise TestError(
f'Provided executable "{artemis_exe_path}" not found or cannot be executed!'
)

# If no output_dir was passed, set it to the exe path
if output_dir is None:
output_dir = os.path.dirname(artemis_exe_path)
# Set the valid provided executable path
abs_exe_path = os.path.abspath(artemis_exe_path)
print(f"Found local executable {abs_exe_path}")
print(f"Outputting results to {output_dir}")
artemis.set_executable(abs_exe_path, output_dir)
if exe_path is not None:
adir = os.path.join(artemis.get_artemis_dir(), "tst")
out_dir = os.path.join(adir, "testing") if out_dir is None else out_dir
reb_path = os.path.join(os.path.dirname(exe_path), "librebound.so")

if not (os.path.exists(exe_path) and os.access(exe_path, os.X_OK)):
raise TestError(f'Provided exe "{exe_path}" not found or cannot be run!')

if not os.path.exists(reb_path):
raise TestError(f'librebound.so not found at "{reb_path}"!')

abs_out_dir = os.path.abspath(out_dir)
abs_exe_dir = os.path.abspath(os.path.dirname(exe_path))
artemis.set_paths(abs_exe_dir, abs_out_dir)
artemis.set_supplied_exe(True)
else:
# If no output_dir was passed, set it to the cwd
if output_dir is None:
output_dir = os.getcwd()
# If we are in a directory with an executable, default to using that
local_path = os.path.join(os.getcwd(), "artemis")
if os.path.exists(local_path) and os.access(local_path, os.X_OK):
print(f"Found local executable {local_path}")
print(f"Outputting results to {output_dir}")
artemis.set_executable(local_path, output_dir)
local_path = os.path.join(cwd, "artemis")
if os.path.isfile(local_path) and os.access(local_path, os.X_OK):
exe_path = local_path
out_dir = os.path.join(cwd, "testing") if out_dir is None else out_dir
reb_path = os.path.join(os.path.dirname(exe_path), "librebound.so")

if not os.path.exists(reb_path):
raise TestError(f'librebound.so not found at "{reb_path}"!')

abs_out_dir = os.path.abspath(out_dir)
abs_exe_dir = os.path.abspath(os.path.dirname(exe_path))
artemis.set_paths(abs_exe_dir, abs_out_dir)
artemis.set_supplied_exe(True)
else:
# Check if we are one level up from the executable
local_path = os.path.join(os.getcwd(), "CMakeCache.txt")
local_path = os.path.join(cwd, "CMakeCache.txt")
if os.path.exists(local_path) and os.access(local_path, os.R_OK):
# Pull out the executable path
exe_path = read_cmakecache(local_path)
if os.path.exists(exe_path) and os.access(exe_path, os.X_OK):
print(f"Found local executable {exe_path}")
print(f"Outputting results to {output_dir}")
artemis.set_executable(exe_path, output_dir)
else:
raise TestError(
f'Could not find executable in "{exe_path}" or cannot be executed!'
)
out_dir = os.path.join(cwd, "testing") if out_dir is None else out_dir
reb_path = os.path.join(os.path.dirname(exe_path), "librebound.so")

if not (os.path.exists(exe_path) and os.access(exe_path, os.X_OK)):
raise TestError(f'No exe in "{exe_path}" or cannot be run!')

if not os.path.exists(reb_path):
raise TestError(f'librebound.so not found at "{reb_path}"!')

abs_out_dir = os.path.abspath(out_dir)
abs_exe_dir = os.path.abspath(os.path.dirname(exe_path))
artemis.set_paths(abs_exe_dir, abs_out_dir)
artemis.set_supplied_exe(True)
else:
adir = os.path.join(artemis.get_artemis_dir(), "tst")
exe_path = os.path.join(adir, "build/src/artemis")
out_dir = os.path.join(adir, "testing") if out_dir is None else out_dir
abs_out_dir = os.path.abspath(out_dir)
abs_exe_dir = os.path.abspath(os.path.dirname(exe_path))
artemis.set_paths(abs_exe_dir, abs_out_dir)
artemis.set_supplied_exe(False)


# Execute main function
Expand All @@ -353,6 +361,12 @@ def set_paths(args):
"--make_nproc", type=int, default=8, help="set nproc N for make -jN"
)

parser.add_argument(
"--use_oversubscribe",
action="store_true",
help="use MPI oversubscribe",
)

parser.add_argument(
"--log_file", type=str, default=None, help="set filename of logfile"
)
Expand Down Expand Up @@ -383,7 +397,7 @@ def set_paths(args):
)

args = parser.parse_args()
set_paths(args)
set_globals(args)
log_init(args)

try:
Expand Down
13 changes: 6 additions & 7 deletions tst/scripts/advection/advection.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
import logging
import numpy as np
import os
import scripts.utils.artemis as artemis
import sys
import scripts.utils.artemis as artemis

sys.path.append(os.path.join(artemis.artemis_dir, "analysis"))
sys.path.append(os.path.join(artemis.get_artemis_dir(), "analysis"))
from ahistory import ahistory

logger = logging.getLogger("artemis" + __name__[7:]) # set logger name
Expand Down Expand Up @@ -72,17 +72,16 @@ def run(**kwargs):

# Analyze outputs
def analyze():
# NOTE(@pdmullen): In the below, we check the magnitude of the error,
# error convergence rates, and error identicality between L- and R-going
# advection.
# NOTE(@pdmullen): In the below, we check the magnitude of the error, error
# convergence rates, and error identicality between L- and R-going advection.
logger.debug("Analyzing test " + __name__)
err_path = os.path.join(artemis.get_run_directory(), _file_id + "-errs.dat")
err_path = os.path.join(artemis.get_data_dir(), _file_id + "-errs.dat")
data = np.loadtxt(
err_path,
dtype=np.float64,
ndmin=2,
)
hist_path = os.path.join(artemis.get_run_directory(), _file_id + ".out0.hst")
hist_path = os.path.join(artemis.get_data_dir(), _file_id + ".out0.hst")
history = ahistory(hist_path)
os.system(f"rm {err_path}")
os.system(f"rm {hist_path}")
Expand Down
5 changes: 2 additions & 3 deletions tst/scripts/advection/advection_mpi.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@
# softwares.

# Modules
import importlib
import logging
import numpy as np
import os
import scripts.utils.artemis as artemis
import scripts.advection.advection as advection

logger = logging.getLogger("artemis" + __name__[7:]) # set logger name

importlib.reload(advection)
advection._nranks = 4
advection._file_id = "advection_mpi"

Expand Down
Loading
Loading