From 2791487e21228ce2caf9a70e2709874320002b84 Mon Sep 17 00:00:00 2001 From: adrifoster Date: Thu, 20 Jun 2024 15:31:44 -0600 Subject: [PATCH] create classes for tests --- testing/build_fortran_tests.py | 2 +- .../allometry/allometry_plotting.py | 187 ------------------ .../allometry/allometry_test.py | 162 ++++++++++++++- .../math_utils/math_plotting.py | 52 ----- .../math_utils/math_utils_test.py | 62 ++++++ testing/functional_tests.cfg | 2 +- ...fates_tests.py => run_functional_tests.py} | 119 ++++++----- testing/testing_classes.py | 19 +- testing/utils.py | 42 +++- 9 files changed, 338 insertions(+), 309 deletions(-) delete mode 100644 testing/functional_testing/allometry/allometry_plotting.py delete mode 100644 testing/functional_testing/math_utils/math_plotting.py create mode 100644 testing/functional_testing/math_utils/math_utils_test.py rename testing/{run_fates_tests.py => run_functional_tests.py} (80%) diff --git a/testing/build_fortran_tests.py b/testing/build_fortran_tests.py index 35f5c51350..509755eb2d 100644 --- a/testing/build_fortran_tests.py +++ b/testing/build_fortran_tests.py @@ -92,7 +92,7 @@ def clean_cmake_files(): or file.startswith(".env_mach_specific") ): os.remove(file) - + def get_extra_cmake_args(build_dir:str, mpilib:str) -> str: """Makes a fake case to grab the required cmake arguments Args: diff --git a/testing/functional_testing/allometry/allometry_plotting.py b/testing/functional_testing/allometry/allometry_plotting.py deleted file mode 100644 index caa6cc1069..0000000000 --- a/testing/functional_testing/allometry/allometry_plotting.py +++ /dev/null @@ -1,187 +0,0 @@ -"""Utility functions for allometry functional unit tests -""" -import os -import math -import pandas as pd -import numpy as np -import xarray as xr -import matplotlib -import matplotlib.pyplot as plt -from utils import get_color_palette, round_up - -def blank_plot(x_max, x_min, y_max, y_min, draw_horizontal_lines=False): - """Generate a blank plot with set attributes - - Args: - x_max (float): maximum x value - x_min (float): minimum x value - y_max (float): maximum y value - y_min (float): minimum y value - draw_horizontal_lines (bool, optional): whether or not to draw horizontal - lines across plot. Defaults to False. - """ - - plt.figure(figsize=(7, 5)) - axis = plt.subplot(111) - axis.spines["top"].set_visible(False) - axis.spines["bottom"].set_visible(False) - axis.spines["right"].set_visible(False) - axis.spines["left"].set_visible(False) - - axis.get_xaxis().tick_bottom() - axis.get_yaxis().tick_left() - - plt.xlim(0.0, x_max) - plt.ylim(0.0, y_max) - - plt.yticks(fontsize=10) - plt.xticks(fontsize=10) - - if draw_horizontal_lines: - inc = (int(y_max) - y_min)/20 - for i in range(0, 20): - plt.plot(range(math.floor(x_min), math.ceil(x_max)), - [0.0 + i*inc] * len(range(math.floor(x_min), math.ceil(x_max))), - "--", lw=0.5, color="black", alpha=0.3) - - plt.tick_params(bottom=False, top=False, left=False, right=False) - - return plt - -def plot_allometry_var(data, varname, units, save_fig, plot_dir=None): - """Plot an allometry variable - - Args: - data (xarray DataArray): the data array of the variable to plot - var (str): variable name (for data structure) - varname (str): variable name for plot labels - units (str): variable units for plot labels - save_fig (bool): whether or not to write out plot - plot_dir (str): if saving figure, where to write to - """ - data_frame = pd.DataFrame({'dbh': np.tile(data.dbh, len(data.pft)), - 'pft': np.repeat(data.pft, len(data.dbh)), - data.name: data.values.flatten()}) - - max_dbh = data_frame['dbh'].max() - max_var = round_up(data_frame[data.name].max()) - - blank_plot(max_dbh, 0.0, max_var, 0.0, draw_horizontal_lines=True) - - pfts = np.unique(data_frame.pft.values) - colors = get_color_palette(len(pfts)) - for rank, pft in enumerate(pfts): - dat = data_frame[data_frame.pft == pft] - plt.plot(dat.dbh.values, dat[data.name].values, lw=2, color=colors[rank], - label=pft) - - plt.xlabel('DBH (cm)', fontsize=11) - plt.ylabel(f'{varname} ({units})', fontsize=11) - plt.title(f"Simulated {varname} for input parameter file", fontsize=11) - plt.legend(loc='upper left', title='PFT') - - if save_fig: - fig_name = os.path.join(plot_dir, f"allometry_plot_{data.name}.png") - plt.savefig(fig_name) - -def plot_total_biomass(data, save_fig, plot_dir): - """Plot two calculations of total biomass against each other - - Args: - data (xarray DataSet): the allometry dataset - """ - data_frame = pd.DataFrame({'dbh': np.tile(data.dbh, len(data.pft)), - 'pft': np.repeat(data.pft, len(data.dbh)), - 'total_biomass_parts': data.total_biomass_parts.values.flatten(), - 'total_biomass_tissues': data.total_biomass_tissues.values.flatten()}) - - max_biomass = np.maximum(data_frame['total_biomass_parts'].max(), - data_frame['total_biomass_tissues'].max()) - - blank_plot(max_biomass, 0.0, max_biomass, 0.0, draw_horizontal_lines=False) - - pfts = np.unique(data_frame.pft.values) - colors = get_color_palette(len(pfts)) - for rank, pft in enumerate(pfts): - data = data_frame[data_frame.pft == pft] - plt.scatter(data.total_biomass_parts.values, data.total_biomass_parts.values, - color=colors[rank], label=pft) - - plt.xlabel('Total biomass (kgC) from parts', fontsize=11) - plt.ylabel('Total biomass (kgC) from tissues', fontsize=11) - plt.title("Simulated total biomass for input parameter file", fontsize=11) - plt.legend(loc='upper left', title='PFT') - - if save_fig: - fig_name = os.path.join(plot_dir, "allometry_plot_total_biomass_compare.png") - plt.savefig(fig_name) - -def plot_allometry_dat(run_dir, out_file, save_figs, plot_dir): - """Plots all allometry plots - - Args: - run_dir (str): run directory - out_file (str): output file name - save_figs (bool): whether or not to save the figures - plot_dir (str): plot directory to save the figures to - """ - - # read in allometry data - allometry_dat = xr.open_dataset(os.path.join(run_dir, out_file)) - - plot_dict = { - 'height': { - 'varname': 'height', - 'units': 'm', - }, - 'bagw': { - 'varname': 'aboveground biomass', - 'units': 'kgC', - }, - 'blmax': { - 'varname': 'maximum leaf biomass', - 'units': 'kgC', - }, - 'crown_area': { - 'varname': 'crown area', - 'units': 'm$^2$', - }, - 'sapwood_area': { - 'varname': 'sapwood area', - 'units': 'm$^2$', - }, - 'bsap': { - 'varname': 'sapwood biomass', - 'units': 'kgC', - }, - 'bbgw': { - 'varname': 'belowground biomass', - 'units': 'kgC', - }, - 'fineroot_biomass': { - 'varname': 'fineroot biomass', - 'units': 'kgC', - }, - 'bstore': { - 'varname': 'storage biomass', - 'units': 'kgC', - }, - 'bdead': { - 'varname': 'deadwood biomass', - 'units': 'kgC', - }, - 'total_biomass_parts': { - 'varname': 'total biomass (calculated from parts)', - 'units': 'kgC', - }, - 'total_biomass_tissues': { - 'varname': 'total biomass (calculated from tissues)', - 'units': 'kgC', - }, - - } - for plot, attributes in plot_dict.items(): - plot_allometry_var(allometry_dat[plot], attributes['varname'], - attributes['units'], save_figs, plot_dir) - - plot_total_biomass(allometry_dat, save_figs, plot_dir) diff --git a/testing/functional_testing/allometry/allometry_test.py b/testing/functional_testing/allometry/allometry_test.py index fe800b91dd..231eb3e136 100644 --- a/testing/functional_testing/allometry/allometry_test.py +++ b/testing/functional_testing/allometry/allometry_test.py @@ -1,5 +1,159 @@ -from testing_classes import FunctionalTest +import os +import math +import xarray as xr +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +from utils import round_up, get_color_palette, blank_plot +from testing_classes import Heuristic -class AllometryTest(FunctionalTest): - def __init__() - \ No newline at end of file +class AllometryTest(Heuristic): + + name = 'allometry' + + def __init__(self, test_dict): + super().__init__(AllometryTest.name, test_dict['test_dir'], + test_dict['test_exe'], test_dict['out_file'], + test_dict['use_param_file'], + test_dict['other_args']) + + def plot_output(self, run_dir: str, save_figs: bool, plot_dir: str): + """Plots all allometry plots + + Args: + run_dir (str): run directory + out_file (str): output file name + save_figs (bool): whether or not to save the figures + plot_dir (str): plot directory to save the figures to + """ + + # read in allometry data + allometry_dat = xr.open_dataset(os.path.join(run_dir, self.out_file)) + + plot_dict = { + 'height': { + 'varname': 'height', + 'units': 'm', + }, + 'bagw': { + 'varname': 'aboveground biomass', + 'units': 'kgC', + }, + 'blmax': { + 'varname': 'maximum leaf biomass', + 'units': 'kgC', + }, + 'crown_area': { + 'varname': 'crown area', + 'units': 'm$^2$', + }, + 'sapwood_area': { + 'varname': 'sapwood area', + 'units': 'm$^2$', + }, + 'bsap': { + 'varname': 'sapwood biomass', + 'units': 'kgC', + }, + 'bbgw': { + 'varname': 'belowground biomass', + 'units': 'kgC', + }, + 'fineroot_biomass': { + 'varname': 'fineroot biomass', + 'units': 'kgC', + }, + 'bstore': { + 'varname': 'storage biomass', + 'units': 'kgC', + }, + 'bdead': { + 'varname': 'deadwood biomass', + 'units': 'kgC', + }, + 'total_biomass_parts': { + 'varname': 'total biomass (calculated from parts)', + 'units': 'kgC', + }, + 'total_biomass_tissues': { + 'varname': 'total biomass (calculated from tissues)', + 'units': 'kgC', + }, + + } + for plot, attributes in plot_dict.items(): + self.plot_allometry_var(allometry_dat[plot], attributes['varname'], + attributes['units'], save_figs, plot_dir) + + self.plot_total_biomass(allometry_dat, save_figs, plot_dir) + + @staticmethod + def plot_allometry_var(data: xr.Dataset, varname: str, units: str, + save_fig: bool, plot_dir :str=None): + """Plot an allometry variable + + Args: + data (xarray DataArray): the data array of the variable to plot + var (str): variable name (for data structure) + varname (str): variable name for plot labels + units (str): variable units for plot labels + save_fig (bool): whether or not to write out plot + plot_dir (str): if saving figure, where to write to + """ + data_frame = pd.DataFrame({'dbh': np.tile(data.dbh, len(data.pft)), + 'pft': np.repeat(data.pft, len(data.dbh)), + data.name: data.values.flatten()}) + + max_dbh = data_frame['dbh'].max() + max_var = round_up(data_frame[data.name].max()) + + blank_plot(max_dbh, 0.0, max_var, 0.0, draw_horizontal_lines=True) + + pfts = np.unique(data_frame.pft.values) + colors = get_color_palette(len(pfts)) + for rank, pft in enumerate(pfts): + dat = data_frame[data_frame.pft == pft] + plt.plot(dat.dbh.values, dat[data.name].values, lw=2, color=colors[rank], + label=pft) + + plt.xlabel('DBH (cm)', fontsize=11) + plt.ylabel(f'{varname} ({units})', fontsize=11) + plt.title(f"Simulated {varname} for input parameter file", fontsize=11) + plt.legend(loc='upper left', title='PFT') + + if save_fig: + fig_name = os.path.join(plot_dir, f"allometry_plot_{data.name}.png") + plt.savefig(fig_name) + + @staticmethod + def plot_total_biomass(data: xr.Dataset, save_fig: bool, plot_dir: str): + """Plot two calculations of total biomass against each other + + Args: + data (xarray DataSet): the allometry dataset + """ + data_frame = pd.DataFrame({'dbh': np.tile(data.dbh, len(data.pft)), + 'pft': np.repeat(data.pft, len(data.dbh)), + 'total_biomass_parts': data.total_biomass_parts.values.flatten(), + 'total_biomass_tissues': data.total_biomass_tissues.values.flatten()}) + + max_biomass = np.maximum(data_frame['total_biomass_parts'].max(), + data_frame['total_biomass_tissues'].max()) + + blank_plot(max_biomass, 0.0, max_biomass, 0.0, draw_horizontal_lines=False) + + pfts = np.unique(data_frame.pft.values) + colors = get_color_palette(len(pfts)) + for rank, pft in enumerate(pfts): + data = data_frame[data_frame.pft == pft] + plt.scatter(data.total_biomass_parts.values, data.total_biomass_parts.values, + color=colors[rank], label=pft) + + plt.xlabel('Total biomass (kgC) from parts', fontsize=11) + plt.ylabel('Total biomass (kgC) from tissues', fontsize=11) + plt.title("Simulated total biomass for input parameter file", fontsize=11) + plt.legend(loc='upper left', title='PFT') + + if save_fig: + fig_name = os.path.join(plot_dir, "allometry_plot_total_biomass_compare.png") + plt.savefig(fig_name) diff --git a/testing/functional_testing/math_utils/math_plotting.py b/testing/functional_testing/math_utils/math_plotting.py deleted file mode 100644 index 4e386dbe93..0000000000 --- a/testing/functional_testing/math_utils/math_plotting.py +++ /dev/null @@ -1,52 +0,0 @@ -"""Utility functions for allometry functional unit tests -""" -import os -import math -import xarray as xr -import numpy as np -import matplotlib.pyplot as plt - -from utils import get_color_palette - -def plot_quadratic_dat(run_dir, out_file, save_figs, plot_dir): - """Reads in and plots quadratic formula test output - - Args: - run_dir (str): run directory - out_file (str): output file - save_figs (bool): whether or not to save the figures - plot_dir (str): plot directory - """ - - # read in quadratic data - quadratic_dat = xr.open_dataset(os.path.join(run_dir, out_file)) - - # plot output - plot_quad_and_roots(quadratic_dat.a.values, quadratic_dat.b.values, - quadratic_dat.c.values, quadratic_dat.root1.values, - quadratic_dat.root2.values) - if save_figs: - fig_name = os.path.join(plot_dir, "quadratic_test.png") - plt.savefig(fig_name) - -def plot_quad_and_roots(a_coeff, b_coeff, c_coeff, root1, root2): - """Plots a set of quadratic formulas (ax**2 + bx + c) and their two roots - - Args: - a_coeff (float array): set of a coefficients - b_coeff (float array): set of b coefficients - c_coeff (float array): set of b coefficients - root1 (float array): set of first real roots - root2 (float array): set of second real roots - """ - num_equations = len(a_coeff) - - plt.figure(figsize=(7, 5)) - x_vals = np.linspace(-10.0, 10.0, num=20) - - colors = get_color_palette(num_equations) - for i in range(num_equations): - y_vals = a_coeff[i]*x_vals**2 + b_coeff[i]*x_vals + c_coeff[i] - plt.plot(x_vals, y_vals, lw=2, color=colors[i]) - plt.scatter(root1[i], root2[i], color=colors[i], s=50) - plt.axhline(y=0.0, color='k', linestyle='dotted') diff --git a/testing/functional_testing/math_utils/math_utils_test.py b/testing/functional_testing/math_utils/math_utils_test.py new file mode 100644 index 0000000000..094d222aeb --- /dev/null +++ b/testing/functional_testing/math_utils/math_utils_test.py @@ -0,0 +1,62 @@ +import os +import math +import xarray as xr +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +from utils import get_color_palette +from testing_classes import Heuristic + +class QuadraticTest(Heuristic): + + name = 'quadratic' + + def __init__(self, test_dict): + super().__init__(QuadraticTest.name, test_dict['test_dir'], + test_dict['test_exe'], test_dict['out_file'], + test_dict['use_param_file'], + test_dict['other_args']) + + def plot_output(self, run_dir: str, save_figs: bool, plot_dir: str): + """Reads in and plots quadratic formula test output + + Args: + run_dir (str): run directory + out_file (str): output file + save_figs (bool): whether or not to save the figures + plot_dir (str): plot directory + """ + + # read in quadratic data + quadratic_dat = xr.open_dataset(os.path.join(run_dir, self.out_file)) + + # plot output + self.plot_quad_and_roots(quadratic_dat.a.values, quadratic_dat.b.values, + quadratic_dat.c.values, quadratic_dat.root1.values, + quadratic_dat.root2.values) + if save_figs: + fig_name = os.path.join(plot_dir, "quadratic_test.png") + plt.savefig(fig_name) + + @staticmethod + def plot_quad_and_roots(a_coeff, b_coeff, c_coeff, root1, root2): + """Plots a set of quadratic formulas (ax**2 + bx + c) and their two roots + + Args: + a_coeff (float array): set of a coefficients + b_coeff (float array): set of b coefficients + c_coeff (float array): set of b coefficients + root1 (float array): set of first real roots + root2 (float array): set of second real roots + """ + num_equations = len(a_coeff) + + plt.figure(figsize=(7, 5)) + x_vals = np.linspace(-10.0, 10.0, num=20) + + colors = get_color_palette(num_equations) + for i in range(num_equations): + y_vals = a_coeff[i]*x_vals**2 + b_coeff[i]*x_vals + c_coeff[i] + plt.plot(x_vals, y_vals, lw=2, color=colors[i]) + plt.scatter(root1[i], root2[i], color=colors[i], s=50) + plt.axhline(y=0.0, color='k', linestyle='dotted') \ No newline at end of file diff --git a/testing/functional_tests.cfg b/testing/functional_tests.cfg index 3c16e8cb1c..f6c9c3f69c 100644 --- a/testing/functional_tests.cfg +++ b/testing/functional_tests.cfg @@ -10,4 +10,4 @@ test_dir = fates_math_ftest test_exe = FATES_math_exe out_file = quad_out.nc use_param_file = False -other_args = [] \ No newline at end of file +other_args = [] diff --git a/testing/run_fates_tests.py b/testing/run_functional_tests.py similarity index 80% rename from testing/run_fates_tests.py rename to testing/run_functional_tests.py index 9d7cc28801..833df5d487 100755 --- a/testing/run_fates_tests.py +++ b/testing/run_functional_tests.py @@ -19,8 +19,8 @@ You must also have a .cime folder in your home directory which specifies machine configurations for CIME. -This script builds and runs various FATES unit and functional tests, and plots any -relevant output from those tests. +This script builds and runs FATES functional tests, and plots any relevant output from +those tests. You can supply your own parameter file (either a .cdl or a .nc file), or if you do not specify anything, the script will use the default FATES parameter cdl file. @@ -29,11 +29,15 @@ import os import argparse import matplotlib.pyplot as plt + from build_fortran_tests import build_tests, build_exists from path_utils import add_cime_lib_to_path -from utils import copy_file, create_nc_from_cdl, config_to_dict, str_to_list -from functional_testing.allometry.allometry_plotting import plot_allometry_dat -from functional_testing.math_utils.math_plotting import plot_quadratic_dat +from utils import copy_file, create_nc_from_cdl, config_to_dict + +# add testing subclasses here +from testing_classes import Heuristic +from functional_testing.allometry.allometry_test import AllometryTest +from functional_testing.math_utils.math_utils_test import QuadraticTest add_cime_lib_to_path() @@ -49,7 +53,7 @@ def commandline_args(): """Parse and return command-line arguments""" description = """ - Driver for running FATES unit and functional tests + Driver for running FATES functional tests Typical usage: @@ -159,6 +163,13 @@ def check_arg_validity(args): """ # check to make sure parameter file exists and is one of the correct forms check_param_file(args.parameter_file) + + # make sure relevant output files exist: + if args.skip_run_executables: + # if you skip the run we assume you want to skip the build + print("--skip-run specified, assuming --skip-build") + args.skip_build = True + check_out_files(args.run_dir, args.test_dict) # make sure build directory exists if args.skip_build: @@ -167,10 +178,6 @@ def check_arg_validity(args): "Re-run script without --skip-build") check_build_dir(args.build_dir, args.test_dict) - # make sure relevant output files exist: - if args.skip_run_executables: - check_out_files(args.run_dir, args.test_dict) - def check_param_file(param_file): """Checks to see if param_file exists and is of the correct form (.nc or .cdl) @@ -201,7 +208,7 @@ def check_build_dir(build_dir, test_dict): if not build_exists(build_dir, attributes['test_dir'], attributes['test_exe']): raise argparse.ArgumentError(None, "Build directory or executable does not exist.\n" "Re-run script without --skip-build.") - + def check_out_files(run_dir, test_dict): """Checks to see that required output files are present in the run directory @@ -285,40 +292,40 @@ def run_tests(clean, verbose_make, build, run_executables, build_dir, run_dir, build_tests(build_dir, _CMAKE_BASE_DIR, make_j, clean=clean, verbose=verbose_make) - # run executables for each test in test list - if run_executables: - print("Running executables") - # we don't run executables for only pfunit tests - for attributes in dict(filter(lambda pair: pair[1]['test_exe'] is not None, - test_dict.items())).values(): - # prepend parameter file (if required) to argument list - args = attributes['other_args'] - print(args) - if attributes['use_param_file']: - args.insert(0, param_file) - # run - run_fortran_exectuables(build_dir_path, attributes['test_dir'], - attributes['test_exe'], run_dir_path, args) + # # run executables for each test in test list + # if run_executables: + # print("Running executables") + # # we don't run executables for only pfunit tests + # for attributes in dict(filter(lambda pair: pair[1]['test_exe'] is not None, + # test_dict.items())).values(): + # # prepend parameter file (if required) to argument list + # args = attributes['other_args'] + # print(args) + # if attributes['use_param_file']: + # args.insert(0, param_file) + # # run + # run_fortran_exectuables(build_dir_path, attributes['test_dir'], + # attributes['test_exe'], run_dir_path, args) - # run unit tests - for test, attributes in dict(filter(lambda pair: pair[1]['has_unit_test'], - test_dict.items())).items(): - print(f"Running unit tests for {test}.") + # # run unit tests + # for test, attributes in dict(filter(lambda pair: pair[1]['has_unit_test'], + # test_dict.items())).items(): + # print(f"Running unit tests for {test}.") - test_dir = os.path.join(build_dir_path, _TEST_SUB_DIR, attributes['test_dir']) - ctest_command = ["ctest", "--output-on-failure"] - output = run_cmd_no_fail(" ".join(ctest_command), from_dir=test_dir, - combine_output=True) - print(output) - - # plot output for relevant tests - for test, attributes in dict(filter(lambda pair: pair[1]['plotting_function'] is not None, - test_dict.items())).items(): - attributes['plotting_function'](run_dir_path, - attributes['out_file'], save_figs, - os.path.join(run_dir_path, 'plots', test)) - # show plots - plt.show() + # test_dir = os.path.join(build_dir_path, _TEST_SUB_DIR, attributes['test_dir']) + # ctest_command = ["ctest", "--output-on-failure"] + # output = run_cmd_no_fail(" ".join(ctest_command), from_dir=test_dir, + # combine_output=True) + # print(output) + + # # plot output for relevant tests + # for test, attributes in dict(filter(lambda pair: pair[1]['plotting_function'] is not None, + # test_dict.items())).items(): + # attributes['plotting_function'](run_dir_path, + # attributes['out_file'], save_figs, + # os.path.join(run_dir_path, 'plots', test)) + # # show plots + # plt.show() def make_plotdirs(run_dir, test_dict): """Create plotting directories if they don't already exist @@ -398,16 +405,24 @@ def main(): Reads in command-line arguments and then runs the tests. """ - functional_test_dict = config_to_dict(_DEFAULT_CONFIG_FILE) + full_test_dict = config_to_dict(_DEFAULT_CONFIG_FILE) + subclasses = Heuristic.__subclasses__() args = commandline_args() - test_dict = parse_test_list(functional_test_dict, args.test_list) + config_dict = parse_test_list(full_test_dict, args.test_list) - #build = not args.skip_build - #run = not args.skip_run_executables - - #run_tests(args.clean, args.verbose_make, build, run, args.build_dir, args.run_dir, - # args.make_j, args.parameter_file, args.save_figs, test_dict) - + ## for now just turn into a dictionary? + test_dict = {} + for name in config_dict.keys(): + my_class = list(filter(lambda subclass: subclass.name == name, subclasses))[0](config_dict[name]) + test_dict[name] = my_class.to_dict() + print(test_dict) + + build = not args.skip_build + run = not args.skip_run_executables + + # run_tests(args.clean, args.verbose_make, build, run, args.build_dir, args.run_dir, + # args.make_j, args.parameter_file, args.save_figs, test_dict) + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/testing/testing_classes.py b/testing/testing_classes.py index ea362e715b..35e2b93eb2 100644 --- a/testing/testing_classes.py +++ b/testing/testing_classes.py @@ -4,19 +4,16 @@ class Heuristic(ABC): """Class for running FATES heuristics""" - def __init__(self, name, test_dir, test_exe, out_file, use_param_file, other_args): - super().__init__(name, test_dir, test_exe, 'functional') + def __init__(self, name:str, test_dir:str, test_exe:str, out_file:str, + use_param_file:str, other_args:str): + self.name = name + self.test_dir = test_dir + self.text_exe = test_exe self.out_file = out_file self.use_param_file = str_to_bool(use_param_file) self.other_args = str_to_list(other_args) @abstractmethod - def plot_output(self, run_dir:str, out_file:str, save_figs:bool, plot_dir:str): - """plot output for this test - - Args: - run_dir (str): path to run directory - out_file (str): name of output file to read in - save_figs (bool): whether or not to save figs to png - plot_dir (str): where to save figs - """ \ No newline at end of file + def plot_output(self, run_dir:str, save_figs:bool, plot_dir:str): + pass + \ No newline at end of file diff --git a/testing/utils.py b/testing/utils.py index ea92313b0d..7ddf597fb4 100644 --- a/testing/utils.py +++ b/testing/utils.py @@ -4,6 +4,7 @@ import math import os import configparser +import matplotlib.pyplot as plt from path_utils import add_cime_lib_to_path add_cime_lib_to_path() @@ -136,7 +137,7 @@ def str_to_bool(val:str) -> bool: """ if val.lower() in ('y', 'yes', 't', 'true', 'on', '1'): return True - if val.lower in ('n', 'no', 'f', 'false', 'off', '0'): + if val.lower() in ('n', 'no', 'f', 'false', 'off', '0'): return False raise ValueError(f"invalid truth value {val}") @@ -154,3 +155,42 @@ def str_to_list(val:str) -> list: return [] res = val.strip('][').split(',') return [n.strip() for n in res] + +def blank_plot(x_max: float, x_min: float, y_max: float, y_min: float, + draw_horizontal_lines :bool=False): + """Generate a blank plot with set attributes + + Args: + x_max (float): maximum x value + x_min (float): minimum x value + y_max (float): maximum y value + y_min (float): minimum y value + draw_horizontal_lines (bool, optional): whether or not to draw horizontal + lines across plot. Defaults to False. + """ + + plt.figure(figsize=(7, 5)) + axis = plt.subplot(111) + axis.spines["top"].set_visible(False) + axis.spines["bottom"].set_visible(False) + axis.spines["right"].set_visible(False) + axis.spines["left"].set_visible(False) + + axis.get_xaxis().tick_bottom() + axis.get_yaxis().tick_left() + + plt.xlim(0.0, x_max) + plt.ylim(0.0, y_max) + + plt.yticks(fontsize=10) + plt.xticks(fontsize=10) + + if draw_horizontal_lines: + inc = (int(y_max) - y_min)/20 + for i in range(0, 20): + plt.plot(range(math.floor(x_min), math.ceil(x_max)), + [0.0 + i*inc] * len(range(math.floor(x_min), math.ceil(x_max))), + "--", lw=0.5, color="black", alpha=0.3) + + plt.tick_params(bottom=False, top=False, left=False, right=False) +