def main(): parser = ArgumentParser( description='Run WRF-Hydro test suite locally', epilog='Example usage: python -B run_tests.py ' '--config nwm_ana nwm_long_range ' '--compiler gfort ' '--candidate_dir ' '/glade/scratch/jmills/wrf_hydro_nwm_public_origin ' '--reference_dir ' '/glade/scratch/jmills/wrf_hydro_nwm_public_upstream ' '--domain_dir /glade/scratch/jmills/CONUS_V2 ' '--scheduler ' '--output_dir /glade/scratch/jmills/conus_v2_testing_gfort') parser.add_argument( "--config", required=True, nargs='+', help="<Required> The configuration(s) to test, " "must be one listed in trunk/NDHMS/hydro_namelist.json keys.") parser.add_argument('--compiler', required=True, help='<Required> compiler, options are intel or gfort') parser.add_argument('--output_dir', required=True, help='<Required> test output directory') parser.add_argument('--candidate_dir', required=True, help='<Required> candidate model directory') parser.add_argument('--reference_dir', required=True, help='<Required> reference model directory') parser.add_argument('--domain_dir', required=False, help='optional domain directory') parser.add_argument( "--domain_tag", required=False, help= "The release tag of the domain to retrieve, e.g. v5.0.1. or dev. If " "specified, a small test domain will be retrieved and placed in the " "specified output_dir and used for the testing domain") parser.add_argument( '--exe_cmd', default="'mpirun -np {0} ./wrf_hydro.exe'", required=False, help='The MPI-dependent model execution command. Default is best guess. ' 'The first/zeroth variable is set to the total number of cores (ncores). The ' 'wrf_hydro_py convention is that the exe is always named wrf_hydro.exe.' ) parser.add_argument('--ncores', default='2', required=False, help='Number of cores to use for testing') parser.add_argument( '--scheduler', required=False, action='store_true', help='Scheduler to use for testing, options are PBSCheyenne or do not ' 'specify for no scheduler') parser.add_argument( '--nnodes', default='6', required=False, help='Number of nodes to use for testing if running on scheduler') parser.add_argument('--account', default='NRAL0017', required=False, action='store', help='Account number to use if using a scheduler.') parser.add_argument('--walltime', default='02:00:00', required=False, action='store', help='Account number to use if using a scheduler.') parser.add_argument( '--queue', default='regular', required=False, action='store', help='Queue to use if running on NCAR Cheyenne, options are regular, ' 'premium, or shared') parser.add_argument('--print', required=False, action='store_true', help='Print log to stdout instead of html') parser.add_argument('--pdb', required=False, action='store_true', help='pdb (debug) in pytest') parser.add_argument('-x', required=False, action='store_true', help='Exit pdb on first failure.') parser.add_argument( '--use_existing_test_dir', default=False, required=False, action='store_true', help='Use existing compiles and runs, only perform output comparisons.' ) parser.add_argument('--xrcmp_n_cores', default=0, required=False, help='Use xrcmp if > 0, and how many cores if so?') args = parser.parse_args() # Make all directories pathlib objects output_dir = pathlib.Path(args.output_dir) candidate_dir = pathlib.Path(args.candidate_dir) reference_dir = pathlib.Path(args.reference_dir) domain_dir = args.domain_dir if domain_dir is not None: domain_dir = pathlib.Path(domain_dir) # Get other args config_list = args.config compiler = args.compiler domain_tag = args.domain_tag exe_cmd = args.exe_cmd ncores = int(args.ncores) nnodes = int(args.nnodes) scheduler = args.scheduler account = args.account walltime = args.walltime queue = args.queue print_log = args.print pdb = args.pdb pdb_x = args.x use_existing_test_dir = args.use_existing_test_dir xrcmp_n_cores = args.xrcmp_n_cores # Make output dir if does not exist if not use_existing_test_dir: if output_dir.is_dir(): raise (IsADirectoryError('Output directory ' + str(output_dir) + ' already exists')) else: output_dir.mkdir(parents=True) # Get the domain if asked for if domain_tag is not None: # Reset domain dir to be the downlaoded domain in the output dir domain_dir = output_dir.joinpath('example_case') if domain_tag == 'dev': file_id = '1xFYB--zm9f8bFHESzgP5X5i7sZryQzJe' download_file_from_google_drive( file_id, str(output_dir.joinpath('gdrive_testcase.tar.gz'))) # untar the test case untar_cmd = 'tar -xf *testcase*.tar.gz' subprocess.run(untar_cmd, shell=True, cwd=str(output_dir)) else: get_release_asset(download_dir=str(output_dir), repo_name='NCAR/wrf_hydro_nwm_public', tag=domain_tag, asset_name='testcase') # untar the test case untar_cmd = 'tar -xf *testcase*.tar.gz' subprocess.run(untar_cmd, shell=True, cwd=str(output_dir)) # Make copy paths candidate_copy = output_dir.joinpath(candidate_dir.name + '_can_pytest') reference_copy = output_dir.joinpath(reference_dir.name + '_ref_pytest') # copy directories to avoid polluting user source code directories if not candidate_copy.exists() or not use_existing_test_dir: shutil.copytree(str(candidate_dir), str(candidate_copy), symlinks=True) if not reference_copy.exists() or not use_existing_test_dir: shutil.copytree(str(reference_dir), str(reference_copy), symlinks=True) # run pytest for each supplied config has_failure = False print("\n\n---------------- Starting WRF-Hydro Testing ----------------") print("Testing the configs: " + ', '.join(config_list), flush=True) for config in config_list: extra_spaces = 29 total_len = len(config) + extra_spaces print('\n\n' + ('#' * total_len)) print('### TESTING: --- ' + config + ' --- ###') print(('#' * total_len) + '\n', flush=True) test_result = run_tests(config=config, compiler=compiler, domain_dir=str(domain_dir), candidate_dir=str(candidate_copy), reference_dir=str(reference_copy), output_dir=str(output_dir), scheduler=scheduler, exe_cmd=exe_cmd, ncores=ncores, nnodes=nnodes, account=account, walltime=walltime, queue=queue, print_log=print_log, pdb=pdb, pdb_x=pdb_x, use_existing_test_dir=use_existing_test_dir, xrcmp_n_cores=xrcmp_n_cores) if test_result.returncode != 0: has_failure = True # Exit with 1 if failure if has_failure: print('\n\n' '##################################') print('### --- TESTING FAILED --- ###') print('##################################\n\n', flush=True) exit(1) else: print('\n\n' '##################################') print('### --- TESTING PASSED --- ###') print('##################################\n\n', flush=True) exit(0)
def main(): parser = ArgumentParser( description='Run WRF-Hydro test suite locally', epilog='Example usage: python -B run_tests.py ' '--config nwm_ana nwm_long_range ' '--compiler gfort ' '--candidate_dir ' '/glade/scratch/jmills/wrf_hydro_nwm_public_origin ' '--reference_dir ' '/glade/scratch/jmills/wrf_hydro_nwm_public_upstream ' '--domain_dir /glade/scratch/jmills/CONUS_V2 ' '--scheduler ' '--output_dir /glade/scratch/jmills/conus_v2_testing_gfort') parser.add_argument( "--config", required=True, nargs='+', help="<Required> The configuration(s) to test, " "must be one listed in trunk/NDHMS/hydro_namelist.json keys.") parser.add_argument('--compiler', required=True, help='<Required> compiler, options are intel or gfort') parser.add_argument('--output_dir', required=True, help='<Required> test output directory') parser.add_argument('--candidate_dir', required=True, help='<Required> candidate model directory') parser.add_argument('--reference_dir', required=True, help='<Required> reference model directory') parser.add_argument('--domain_dir', required=False, help='optional domain directory') parser.add_argument( "--domain_tag", required=False, help= "The release tag of the domain to retrieve, e.g. v5.0.1. or dev. If " "specified, a small test domain will be retrieved and placed in the " "specified output_dir and used for the testing domain") parser.add_argument('--ncores', default='2', required=False, help='Number of cores to use for testing') parser.add_argument( '--scheduler', required=False, action='store_true', help='Scheduler to use for testing, options are PBSCheyenne or do not ' 'specify for no scheduler') parser.add_argument( '--nnodes', default='6', required=False, help='Number of nodes to use for testing if running on scheduler') parser.add_argument('--account', default='NRAL0017', required=False, action='store', help='Account number to use if using a scheduler.') parser.add_argument('--walltime', default='02:00:00', required=False, action='store', help='Account number to use if using a scheduler.') parser.add_argument( '--queue', default='regular', required=False, action='store', help='Queue to use if running on NCAR Cheyenne, options are regular, ' 'premium, or shared') parser.add_argument('--print', required=False, action='store_true', help='Print log to stdout instead of html') args = parser.parse_args() # Make all directories pathlib objects output_dir = pathlib.Path(args.output_dir) candidate_dir = pathlib.Path(args.candidate_dir) reference_dir = pathlib.Path(args.reference_dir) domain_dir = args.domain_dir if domain_dir is not None: domain_dir = pathlib.Path(domain_dir) # Get other args config_list = args.config compiler = args.compiler domain_tag = args.domain_tag ncores = int(args.ncores) nnodes = int(args.nnodes) scheduler = args.scheduler account = args.account walltime = args.walltime queue = args.queue print_log = args.print # Make output dir if does not exist if output_dir.is_dir(): raise (IsADirectoryError('Output directory ' + str(output_dir) + ' already exists')) else: output_dir.mkdir(parents=True) # Get the domain if asked for if domain_tag is not None: # Reset domain dir to be the downlaoded domain in the output dir domain_dir = output_dir.joinpath('example_case') if domain_tag == 'dev': file_id = '1EHgWeM8k2-Y3jNMLri6C0u_fIUQIonO_' download_file_from_google_drive( file_id, str(output_dir.joinpath('gdrive_testcase.tar.gz'))) # untar the test case untar_cmd = 'tar -xf *testcase*.tar.gz' subprocess.run(untar_cmd, shell=True, cwd=str(output_dir)) else: get_release_asset(download_dir=str(output_dir), repo_name='NCAR/wrf_hydro_nwm_public', tag=domain_tag, asset_name='testcase') # untar the test case untar_cmd = 'tar -xf *testcase*.tar.gz' subprocess.run(untar_cmd, shell=True, cwd=str(output_dir)) # Make copy paths candidate_copy = output_dir.joinpath(candidate_dir.name + '_can_pytest') reference_copy = output_dir.joinpath(reference_dir.name + '_ref_pytest') # Remove if exist and make if not if candidate_copy.is_dir(): shutil.rmtree(str(candidate_copy)) if reference_copy.is_dir(): shutil.rmtree(str(reference_copy)) # copy directories to avoid polluting user source code directories shutil.copytree(str(candidate_dir), str(candidate_copy), symlinks=True) shutil.copytree(str(reference_dir), str(reference_copy), symlinks=True) # run pytest for each supplied config has_failure = False print("\n\n---------------- Starting WRF-Hydro Testing ----------------") print("Testing the configs: " + ', '.join(config_list), flush=True) for config in config_list: extra_spaces = 29 total_len = len(config) + extra_spaces print('\n\n' + ('#' * total_len)) print('### TESTING: --- ' + config + ' --- ###') print(('#' * total_len) + '\n', flush=True) test_result = run_tests(config=config, compiler=compiler, domain_dir=str(domain_dir), candidate_dir=str(candidate_copy), reference_dir=str(reference_copy), output_dir=str(output_dir), scheduler=scheduler, ncores=ncores, nnodes=nnodes, account=account, walltime=walltime, queue=queue, print_log=print_log) if test_result.returncode != 0: has_failure = True # Exit with 1 if failure if has_failure: print('\n\n' '##################################') print('### --- TESTING FAILED --- ###') print('##################################\n\n', flush=True) exit(1) else: print('\n\n' '##################################') print('### --- TESTING PASSED --- ###') print('##################################\n\n', flush=True) exit(0)