From 1141a18cd3a79e877723c667336e79f2c02693a7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 08:25:39 -0700 Subject: [PATCH] Update develop-ref after #2779 (#2804) * Store change of sonar.login to sonar.token. * Hotfix to develop, updating aclocal.m4 and config.h.in to what is created when running bootstrap inside the dtcenter/met-base:v3.1 Docker image. Updates autoconf 2.69 output to 2.71. * Adding -lnetcdf to configure_lib_args for NetCDF-CXX compilation * Added eckit and atlas loads and paths * Added atlas and eckit loads and paths * Changing -j to "-j 5" as the recommended value * Update ensemble-stat.rst Fixed incorrect path to obs error table * Updated values for GRIB2CLIB_NAME and BUFRLIB_NAME * Updated for proj, eckit, and atlas * Feature #2761 develop seneca (#2762) * Per #2761, define runtime python version for testing rather than using the default version which no longer exists in /usr/local * Per #2761, fix setting ci-skip-all * Per #2761, patching test_util.R to use the -C command line option for ncdiff. I did test running comp_dir.sh with this change and confirmed that it now runs to completion. * #2652 Added find_var_by_standard_name and separated common codes to find_xy_vars * #2757 Get the email list from the environment variable MET_CRON_EMAIL_LIST__MET (or MET_CRON_EMAIL_LIST_) * #2757 THe SonarQube token and URL are replaced by ujsing the environment variable SONAR_TOKEN_VALUE and SONAR_SERVER_URL * #2757 The SonarQube token and URL are replaced with the pre-defined strings, SONAR_TOKEN_VALUE and SONAR_SERVER_URL * Bugfix #2670 develop --enable-python (#2768) * #2755 Added a header count and checking header count instead of using header id (hid) * Update install_met_env.acorn_py3.10 * Update install_met_env.wcoss2_py3.10 * Feature #2776 cleanup (#2777) * Per #2776, delete stale prob_pair_info.h/.cc, wwmca_utils.h/.cc, and rmw_analysis_utils.h/.cc files that were not being compiled by their Makefiles. * Per #2776, update Makefile to compile the test_read_rmw utility. * Per #2776, add Makefile for the vx_python3_utils test utility directory. * add rpath for atlas and eckit lib dirs so dynamic libraries can be found when running on seneca * only set -L and -I arguments for atlas and eckit if the appropriate environment variable is set -- this matches how it is handled for other external library dependencies * Per #2776, just whitespace and capitalization --------- Co-authored-by: George McCabe <23407799+georgemccabe@users.noreply.github.com> * Bugfix #2782 develop MASSDEN (#2783) * Per #2782, port over fixes from the bugfix_2782_main_v11.1_MASSDEN branch over to the bugfix branch for develop. Will also add new GRIB2 filtering options in this branch. * Per #2782, add support for 4 new GRIB2 filtering options for GRIB2_aerosol_type, GRIB2_aerosol_interval_type, GRIB2_aerosol_size_lower, and GRIB2_aerosol_size_upper. These are useful in filtering the MASSDEN records in the RRFS smoke output files. * Per #2782, fix aerosol_size_lower() and aersol_size_upper() inline definitions. * Per #2782, add a unit test for GRIB2 table 4.48. * Per #2782, switch from strict equality to using the is_eq() function when checking the GRIB2_aerosol_size_lower and upper values since they're doubles and not ints. * Feature #2701 ismn (#2758) * Add support for ISMN soil moisture data * Per #2701, update ascii2nc.cc with the ismn file option. * Per #2701, update ascii2nc docs * Per #2701, store the depth for precip as 0. * Per #2701, parse the ISMN observation value from the correct column. * Per #2701, every time I run ascii2nc to I see a log message for 'DEBUG 1: Number of NDBC skipped files due to no lookup 0'. This is printed by the NdbcHandler destructor. Update the logic to only print a debug level 3 log message when the number of missing locations is greater than 0, 'DEBUG 3: Skipped 5 NDBC files whose locations are not defined in 'MET_BASE/table_files/ndbc_stations.xml'. Set the MET_NDBC_STATIONS environment variable to override this file.' * Per #2701, as instructed by @anewman89, store the average of the depth values rather than the maximum value. * Per #2701, add a test of processing the ISMN data through ascii2nc. * Per #2701, fix parsing of year and month to subtract 1900 and 1, respectively. * Per #2701, set use_var_id to true for ISMN inputs * Per #2701, even though we're mapping obs data to GRIB code names and units, we want to encode them with use_var_id = true and so we need to keep track of those var_id values. * Per #2701, fix typo in the name of the ascii2nc netcdf output file. --------- Co-authored-by: MET Tools Test Account * Feature 2753 comp script config (#2771) * set dynamic library file extension to .dylib if running on MacOS and .so otherwise * Added disabling of jasper documentation for compiliation on Hera * Updated * remove extra export of compiler env vars * include full path to log file so it is easier to file the log file to examine when a command fails * send cmake output to a log file * remove redundant semi-colon * use full path to log file so it is easier to examine on failure * use run_cmd to catch if rm command fails * Modifications for compilation on hera, gaea, and orion * Updating * fixed variable name * clean up if/else statements * set TIFF_LIBRARY_RELEASE argument to use full path to dynamic library file to prevent failure installing proj library * set LDFLAGS so that LDFLAGS value set in the user's environment will also be used * Updated based on gaea, orion, and hera installs * Updated * change extension of dynamic library files only if architecture is arm64 because older Macs still use .so * added netcdf library to args to prevent error installing NetCDF-CXX when PROJ has been installed in the same run of the script -- PATH is set in the COMPILE_PROJ if block that causes this flag from being added automatically * clean up how rpath and -L are added to LDFLAGS so that each entry is separate -- prevents errors installing on Mac arm64 because multiple rpath values aren't read using :. Also use MET_PROJLIB * Updated * removed -ltiff from MET libs * only add path to rpath and -L arguments if they are not already included in LDFLAGS * changed from using LIB_TIFF (full path to tiff lib file) to use TIFF_LIB_DIR (dir containing tiff lib file). Added TIFF_INCLUDE_DIR to proj compilation and -DJAS_ENABLE_DOC to jasper compliation taken from @jprestop branch * update comments * ensure all MET_* and MET_*LIB variables are added to the rpath for consistency * remove unnecessary if block and only export LDFLAGS at the end of setting locally * Updated * Added section for adding /lib64 and rearranged placement of ADDTL_DIR * Commenting out the running of the Jasper lib tests * Updating and/or removing files * Updating and/or removing files * Latest udpates which include the addition of the tiff library for proj * Remove commented out line. Co-authored-by: John Halley Gotway * Make indentation consistent. Co-authored-by: John Halley Gotway * Make indentation consistent. Co-authored-by: John Halley Gotway * Make indentation consistent. Co-authored-by: John Halley Gotway --------- Co-authored-by: George McCabe <23407799+georgemccabe@users.noreply.github.com> Co-authored-by: John Halley Gotway * #2697 Share the temporary file for blocking * #2697 Reduced the temporary files for pb2nc * Moved typedef unixtime from vx_cal.h to time_array.h * #2697 Changed static const to constexpr for SonarQube (code smell) * Removing ${MAKE_ARGS} in some locations Removing ${MAKE_ARGS} from "make install" and "make test" for MET. Removing "met" prefix from met.configure.log because we really need config.log for any detail. It is confusing to have met.configure.log when that does not contain useful information. * #2673 Removed unused variable grid_x and grid_y * #2673 Renamed the shadowed variable n to nd * #2673 Use nullptr instead of literal 0 * #2673 Moved down namespace declarations. Removed unused variables * #2673 Reduced the scope of variables * #2673 Use nullptr instead of literal 0 * #2673 Removed unused variables * #2673 Renamed ex to ex2 which becomes shadowed variable * #2673 Removed always true condition * #2673 Moved down namespace declarations. Use nullptr instead of literal 0 * #2673 Use nullptr instead of literal 0. Removed unused variables * #2673 Moved down namespace declarations. * #2673 Added namespace std to string * #2673 Removed unsued variables. Check nullptr of gt * #2673 Moved down namespace declarations. * #2673 Added namespace std to string * Feature #2547 Read WRF output files natively (#2790) * Per #2547, add the file_type = NETCDF_WRF configuration option. * Per #2547, rename vx_data2d_nc_interp as vx_data2d_nc_wrf and vx_data2d_nccf as vx_data2d_nc_cf. * Per #2547, rename nccf files as nc_cf for consistency throughout * Per #2547, rename Pinterp classes as Wrf * only set -L and -I arguments for atlas and eckit if the appropriate environment variable is set -- this matches how it is handled for other external library dependencies * add rpath for atlas and eckit lib dirs so dynamic libraries can be found when running on seneca * ignore directory automatically created by CLion * rename pinterp variables and classes to Wrf * more rename and fix typos * per #2547, add support for reading WRF files by adding support for different X/Y/Z dimensions, read X/Y/Z dimension names when reading a field instead of storing them per file, track if data is staggered in X/Y/Z dimension (still need to add DataPlane function to destagger data), handle pressure field that also includes time dimension * per #2547, add DataPlane function to handle staggering of grid * per #2547, change file type returned from pinterp to wrf * per #2547, call function to stagger DataPlane if necessary * per #2547, keep store if variable is on pressure levels and only read pressure field if it is * ignore cmake directory * per #2547, support U_PL, U_ZL, U, and U where is an integer in is_u_wind and the same for is_v_wind * change regex to match zero or more instances of a number after U or V instead of 1 or more to include the fields named U and V * error and exit if var with unsupported subgrid dimension is processed * clean up logging * only destagger Z dimension for bottom_top_stag because other _stag Z dimensions don't need to be destaggered, clean up logic to read X/Y/Z dimension info * Per #2547, instead of checking dimensions of all vars in file, check dimensions for the var that is being read. Only read pressure var if the z dimension matches the var to read. Set class variable DimNames instead of local gDimNames so dump function will work as expected * per #2547, support reading variable that corresponds to dimension with different name as dimension and includes a time dimension, e.g. P_PL(Time, num_press_levels_stag) * per #2547, added unit tests to process WRF out data with plot_data_plane and grid_stat * merged develop and resolved conflicts * rename lib from pinterp to wrf * rename lib * fixed incorrect merge changes * fixed broken unit test format * Per #2547, restore the mention of NAK.poly to Appendix B. * Change model to WRF Co-authored-by: John Halley Gotway * change obtype to WRF Co-authored-by: John Halley Gotway * plot higher vertical level to get non-zero output Co-authored-by: John Halley Gotway * indentation Co-authored-by: John Halley Gotway --------- Co-authored-by: John Halley Gotway * Hotfix to the develop branch after PR #2790 merged the feature_2547_wrf branch into develop. Rerunning bootstrap on the project machine seneca produces 2 small differences that should also be incorporated into develop. * Feature 2588 install rewrite (#2791) * updated first 3 sections * updated spacing * adding the Environment Variables to Run Script section * adding in the remainder of the google doc changes * trying to fix duplicate explicit target name issue * fixing required_external_libraries_to_build_met links * HDF4 testing double underscore * adding double underscores for HDF5 and HDF-EOS2 and changing library back * fixing HDF5 web link name * Added formatting, made minor edits, removed manual build instructions sections * Removed reference to sample-test-case which no longer exists * Resolving errors * Minor formatting change * Updated apptainer instructions, new version references * Formatting updates and removal of first person language * fixed code blocks * Resolving error * Removed first person references * Removed reference to met_directory_structure * updated bolding, removed end section * Changing X.Y.X references to X.Y.Z * updating thru Using the compile_MET_all.sh Script * fixing spacing for ATLAS web address * Fixed broken internal reference to installation due to typo * adding version numbers * fixing Recommended-Components MET * 3.4.2 section updates * fixing spacing * adding a period and more dropdown menus * fixing spacing * loose ends * removing space * removing bold * External Library handling in compile_MET_all.sh section updates * installation directory view * installation directory after * attempting to add figures via the web updating Executing the compile_MET_all.sh script section * fixing bolds, italics etc. * updating last half of doc * loose ends * loose ends * adding more dropdown menus * fixing dropdown menu and testing automatic version * testing automatic version * confirmed automatic version cannot be added to italics or code blocks * another round of updates * fixing indents * creating a table * fixing table * fixing table 2 * more loose end changes * more changes * hopefully final updates * trying a fancy new csv table * troubleshooting csv table * correcting table name * adding a grid table * fixing grid table * removing csv table * Per #2588, update to the environment variables table. * Delete docs/Users_Guide/environment_variables_table.csv * Per #2785, add new example installation files * updating small changes in the first half of the documentation * new dropdown menu and fixing some links * trying to fix line spacing in dropdown * take 2 fixing spacing * take 3 fix spacing * env var order * removing "s" * small changes * un capitalizing S * A few minor changes --------- Co-authored-by: j-opatz Co-authored-by: Julie Prestopnik Co-authored-by: j-opatz <59586397+j-opatz@users.noreply.github.com> Co-authored-by: John Halley Gotway * Minor hotfix to develop to fix a typo in the comments of the PB2NC config files. * Feature #2796 develop gha node20 (#2797) * Per #2796, update actions/checkout@v3 to actions/checkout@v4. * Per #2796, update to actions/upload-artifact@v4 and actions/setup-python@v5. * Per #2796, update to download-artifact@v4 and actions/github-script@v7 * Feature #2796 develop gha_node20, fix artifact names (#2799) * Per #2796, update actions/checkout@v3 to actions/checkout@v4. * Per #2796, update to actions/upload-artifact@v4 and actions/setup-python@v5. * Per #2796, update to download-artifact@v4 and actions/github-script@v7 * Per #2796, update testing workflow for update-artifact v4 version which requires unique artifact names. ci-run-unit * Per #2796, merge the logs into a single artifact. ci-run-unit * Per #2796, renaming rc_leads and rc as 1c and 2c, respectively for consistency purposes. Note that this commit should result in just a logs_compile artifact being created... and not doing the log merging step. * Per #2796, update the compilation workflow to make the log artifact names unique. * #2772 Change nan and inf to -9999 on reading ASCII input if failed to parse * Feature #2801 warnings (#2802) * Per #2801, add new time_offset_warning config option. * Per #2810, update MetConfig class to parse and check the time_offset_warning config option. * Per #2801, update Grid-Stat, MODE, PCP-Combine, Series-Analysis, Wavelet-Stat, and the WWMCA tool to call MetConfig::time_offset_warning() to decide whether to print a Warning or Debug(3) log message about initialization or valid time differences. * Per #2801, no change to content. Just removing unneeded whitespace. * Per #2801, remove extra empty line foromw m warning message. * Per #2801, remove extra empty line from MODE warning messages. --------- Co-authored-by: MET Tools Test Account * Feature #2745 mvmode enhancements (#2779) * Working mvmode with percentile thresholds and grid_type fixes, plus more unit tests * separated multivar and traditional mode frontend classes to simplify the mode executive class design * documentation changes to go with changes to percentiles and data_type * Cleanup and bugfix to doc * Bug fix to doc * Another bugfix attempt * synching up development.seneca with what is in the develop branch * added clone() method to all the vx_data2d var_info classes * Fix multivar_name/level definitions in UG * bug fix regarding accessing the config to create a verification grid * Update docs/Users_Guide/mode.rst * Update docs/Users_Guide/mode.rst * Per #2745, commit changes to Makefile.am/.in files after running bootstrap on this feature branch on seneca. * Per #2745, no real code changes. Just whitespace. --------- Co-authored-by: MET Tools Test Account Co-authored-by: Tracy Hertneky Co-authored-by: John Halley Gotway --------- Co-authored-by: MET Tools Test Account Co-authored-by: root Co-authored-by: Julie Prestopnik Co-authored-by: Christina Kalb Co-authored-by: John Halley Gotway Co-authored-by: Howard Soh Co-authored-by: Howard Soh Co-authored-by: George McCabe <23407799+georgemccabe@users.noreply.github.com> Co-authored-by: metplus-bot <97135045+metplus-bot@users.noreply.github.com> Co-authored-by: lisagoodrich <33230218+lisagoodrich@users.noreply.github.com> Co-authored-by: j-opatz Co-authored-by: j-opatz <59586397+j-opatz@users.noreply.github.com> Co-authored-by: davidalbo Co-authored-by: Tracy Hertneky --- .../build_docker_and_trigger_metplus.yml | 4 +- .github/workflows/compilation_options.yml | 36 +- .github/workflows/documentation.yml | 8 +- .github/workflows/testing.yml | 187 ++-- .github/workflows/update_truth.yml | 2 +- configure | 1 - data/config/ConfigConstants | 3 + .../dev_details/tmp_file_use.rst | 10 +- docs/Users_Guide/config_options.rst | 281 ++--- docs/Users_Guide/figure/installation_dir.png | Bin 0 -> 90216 bytes .../figure/installation_dir_after.png | Bin 0 -> 27860 bytes docs/Users_Guide/installation.rst | 924 ++++++++++------- docs/Users_Guide/mode.rst | 21 +- docs/Users_Guide/reformat_grid.rst | 2 +- docs/Users_Guide/reformat_point.rst | 2 +- docs/Users_Guide/tc-dland.rst | 2 +- ...nv.generic => install_met_env.generic_gnu} | 2 + .../install_met_env.generic_intel_non-oneapi | 86 ++ .../install_met_env.generic_intel_oneapi | 86 ++ internal/test_unit/config/MODEConfig_multivar | 274 +++++ .../test_unit/config/MODEConfig_multivar_3_2 | 275 +++++ .../config/MODEConfig_multivar_super | 271 +++++ internal/test_unit/config/PB2NCConfig | 2 +- internal/test_unit/config/PB2NCConfig_airnow | 2 +- internal/test_unit/config/PB2NCConfig_all | 2 +- internal/test_unit/config/PB2NCConfig_summary | 2 +- internal/test_unit/config/PB2NCConfig_vlevel | 2 +- .../test_unit/config/ref_config/PB2NCConfig | 2 +- internal/test_unit/xml/unit_mode_multivar.xml | 85 +- internal/test_util/libcode/Makefile.in | 2 +- scripts/config/PB2NCConfig_G212 | 2 +- scripts/python/pyembed/python_embedding.py | 38 +- src/basic/vx_cal/time_array.h | 2 + src/basic/vx_cal/vx_cal.h | 3 - src/basic/vx_config/config_constants.h | 8 +- src/basic/vx_config/config_file.cc | 68 +- src/basic/vx_config/config_file.h | 2 + src/libcode/vx_data2d/var_info.h | 5 +- src/libcode/vx_data2d_grib/var_info_grib.cc | 9 + src/libcode/vx_data2d_grib/var_info_grib.h | 1 + src/libcode/vx_data2d_grib2/var_info_grib2.cc | 9 + src/libcode/vx_data2d_grib2/var_info_grib2.h | 1 + src/libcode/vx_data2d_nc_cf/data2d_nc_cf.cc | 2 +- src/libcode/vx_data2d_nc_cf/nc_cf_file.cc | 41 +- src/libcode/vx_data2d_nc_cf/var_info_nc_cf.cc | 17 +- src/libcode/vx_data2d_nc_cf/var_info_nc_cf.h | 1 + .../vx_data2d_nc_met/var_info_nc_met.cc | 9 + .../vx_data2d_nc_met/var_info_nc_met.h | 1 + .../vx_data2d_nc_wrf/var_info_nc_wrf.cc | 9 + .../vx_data2d_nc_wrf/var_info_nc_wrf.h | 1 + .../vx_data2d_python/var_info_python.cc | 9 + .../vx_data2d_python/var_info_python.h | 1 + src/libcode/vx_data2d_ugrid/data2d_ugrid.cc | 11 +- src/libcode/vx_data2d_ugrid/ugrid_file.cc | 19 +- src/libcode/vx_data2d_ugrid/var_info_ugrid.cc | 18 +- src/libcode/vx_data2d_ugrid/var_info_ugrid.h | 1 + src/libcode/vx_grid/unstructured_grid.cc | 2 - src/libcode/vx_nc_obs/nc_obs_util.cc | 1 - src/libcode/vx_nc_obs/nc_point_obs_out.cc | 4 +- src/libcode/vx_nc_util/nc_utils.cc | 13 +- src/libcode/vx_nc_util/nc_utils.h | 4 +- src/libcode/vx_nc_util/nc_utils.hpp | 9 +- src/libcode/vx_nc_util/write_netcdf.cc | 51 +- src/libcode/vx_pb_util/copy_bytes.cc | 15 +- .../vx_pointdata_python/python_pointdata.cc | 31 +- src/libcode/vx_shapedata/Makefile.am | 1 + src/libcode/vx_shapedata/Makefile.in | 1 + src/libcode/vx_shapedata/engine.cc | 22 +- src/libcode/vx_shapedata/engine.h | 15 +- src/libcode/vx_shapedata/mode_conf_info.cc | 609 ++++++++--- src/libcode/vx_shapedata/mode_conf_info.h | 42 +- src/libcode/vx_shapedata/mode_field_info.cc | 32 +- src/libcode/vx_shapedata/mode_field_info.h | 8 +- src/libcode/vx_shapedata/mode_input_data.h | 42 + src/libcode/vx_summary/summary_calc.cc | 5 +- src/libcode/vx_summary/summary_calc_max.cc | 5 +- src/libcode/vx_summary/summary_calc_max.h | 2 +- src/libcode/vx_summary/summary_calc_mean.cc | 5 +- src/libcode/vx_summary/summary_calc_mean.h | 2 +- src/libcode/vx_summary/summary_calc_median.cc | 5 +- src/libcode/vx_summary/summary_calc_min.cc | 5 +- src/libcode/vx_summary/summary_calc_min.h | 2 +- .../vx_summary/summary_calc_percentile.cc | 5 +- .../vx_summary/summary_calc_percentile.h | 6 +- src/libcode/vx_summary/summary_calc_range.cc | 4 +- src/libcode/vx_summary/summary_calc_range.h | 2 +- src/libcode/vx_summary/summary_calc_stdev.cc | 4 +- src/libcode/vx_summary/summary_calc_stdev.h | 2 +- src/libcode/vx_summary/summary_calc_sum.cc | 4 +- src/libcode/vx_summary/summary_calc_sum.h | 2 +- src/libcode/vx_summary/summary_key.cc | 4 +- src/libcode/vx_summary/summary_key.h | 18 +- src/libcode/vx_summary/summary_obs.cc | 5 +- .../vx_summary/time_summary_interval.cc | 4 +- src/tools/core/grid_stat/grid_stat.cc | 26 +- src/tools/core/mode/Makefile.am | 1 + src/tools/core/mode/Makefile.in | 25 +- src/tools/core/mode/combine_boolplanes.h | 3 +- src/tools/core/mode/mode.cc | 9 +- src/tools/core/mode/mode_exec.cc | 780 +++++++------- src/tools/core/mode/mode_exec.h | 93 +- src/tools/core/mode/mode_frontend.cc | 508 +-------- src/tools/core/mode/mode_frontend.h | 43 +- src/tools/core/mode/mode_superobject.cc | 184 ++++ src/tools/core/mode/mode_superobject.h | 51 + src/tools/core/mode/multivar_data.cc | 22 +- src/tools/core/mode/multivar_data.h | 7 +- src/tools/core/mode/multivar_frontend.cc | 965 ++++++++---------- src/tools/core/mode/multivar_frontend.h | 106 ++ src/tools/core/pcp_combine/pcp_combine.cc | 35 +- src/tools/core/point_stat/point_stat.cc | 8 +- .../core/series_analysis/series_analysis.cc | 24 +- src/tools/core/wavelet_stat/wavelet_stat.cc | 29 +- src/tools/other/ascii2nc/aeronet_handler.cc | 6 +- src/tools/other/ascii2nc/aeronet_handler.h | 16 +- src/tools/other/ascii2nc/airnow_handler.cc | 33 +- src/tools/other/ascii2nc/airnow_handler.h | 50 +- src/tools/other/ascii2nc/airnow_locations.cc | 5 +- src/tools/other/ascii2nc/airnow_locations.h | 20 +- src/tools/other/ascii2nc/file_handler.cc | 8 +- src/tools/other/ascii2nc/file_handler.h | 22 +- src/tools/other/ascii2nc/python_handler.cc | 10 +- src/tools/other/madis2nc/madis2nc.cc | 5 +- src/tools/other/pb2nc/pb2nc.cc | 152 ++- src/tools/other/wwmca_tool/nc_output.cc | 52 +- 125 files changed, 4370 insertions(+), 2785 deletions(-) create mode 100644 docs/Users_Guide/figure/installation_dir.png create mode 100644 docs/Users_Guide/figure/installation_dir_after.png rename internal/scripts/installation/config/{install_met_env.generic => install_met_env.generic_gnu} (97%) create mode 100644 internal/scripts/installation/config/install_met_env.generic_intel_non-oneapi create mode 100644 internal/scripts/installation/config/install_met_env.generic_intel_oneapi create mode 100644 internal/test_unit/config/MODEConfig_multivar create mode 100644 internal/test_unit/config/MODEConfig_multivar_3_2 create mode 100644 internal/test_unit/config/MODEConfig_multivar_super create mode 100644 src/libcode/vx_shapedata/mode_input_data.h create mode 100644 src/tools/core/mode/mode_superobject.cc create mode 100644 src/tools/core/mode/mode_superobject.h create mode 100644 src/tools/core/mode/multivar_frontend.h diff --git a/.github/workflows/build_docker_and_trigger_metplus.yml b/.github/workflows/build_docker_and_trigger_metplus.yml index aec4f2ce30..23aafdfd76 100644 --- a/.github/workflows/build_docker_and_trigger_metplus.yml +++ b/.github/workflows/build_docker_and_trigger_metplus.yml @@ -18,7 +18,7 @@ jobs: name: Handle Docker Image runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Get branch name id: get_branch_name @@ -47,7 +47,7 @@ jobs: env: GITHUB_CONTEXT: ${{ toJson(github) }} run: echo "$GITHUB_CONTEXT" - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 with: github-token: ${{ secrets.METPLUS_BOT_TOKEN }} script: | diff --git a/.github/workflows/compilation_options.yml b/.github/workflows/compilation_options.yml index 48e7be9ca5..7a8dc73cf0 100644 --- a/.github/workflows/compilation_options.yml +++ b/.github/workflows/compilation_options.yml @@ -27,7 +27,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set job controls id: job_status run: .github/jobs/set_job_controls.sh @@ -46,18 +46,26 @@ jobs: needs: job_control strategy: matrix: - config_opts: - - '' - - '--enable-all' - - '--enable-grib2' - - '--enable-python' - - '--enable-ugrid' - - '--enable-lidar2nc' - - '--enable-mode_graphics' - - '--enable-modis' + include: + - jobid: 'job1' + config: '' + - jobid: 'job2' + config: '--enable-all' + - jobid: 'job3' + config: '--enable-grib2' + - jobid: 'job4' + config: '--enable-python' + - jobid: 'job5' + config: '--enable-ugrid' + - jobid: 'job6' + config: '--enable-lidar2nc' + - jobid: 'job7' + config: '--enable-mode_graphics' + - jobid: 'job8' + config: '--enable-modis' fail-fast: false steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Create directories to store output run: mkdir -p ${RUNNER_WORKSPACE}/logs @@ -68,7 +76,7 @@ jobs: SOURCE_BRANCH: ${{ needs.job_control.outputs.branch_name }} MET_BASE_REPO: ${{ needs.job_control.outputs.met_base_repo }} MET_BASE_TAG: ${{ needs.job_control.outputs.met_base_tag }} - MET_CONFIG_OPTS: ${{ matrix.config_opts }} + MET_CONFIG_OPTS: ${{ matrix.config }} - name: Copy all build log files into logs directory if: always() @@ -76,9 +84,9 @@ jobs: - name: Upload logs as artifact if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: logs + name: logs_${{ matrix.jobid }} path: ${{ runner.workspace }}/logs if-no-files-found: ignore diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index b039177ff7..9f061df17b 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -18,8 +18,8 @@ jobs: name: Build Documentation runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: '3.8' - name: Install dependencies @@ -29,12 +29,12 @@ jobs: python -m pip install -r docs/requirements.txt - name: Build docs run: ./.github/jobs/build_documentation.sh - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: always() with: name: documentation path: artifact/documentation - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: failure() with: name: documentation_warnings.log diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index bc43679221..b667511b92 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -50,7 +50,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set job controls id: job_status run: .github/jobs/set_job_controls.sh @@ -76,7 +76,7 @@ jobs: needs: job_control if: ${{ needs.job_control.outputs.run_compile == 'true' }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Create directories to store output run: mkdir -p ${RUNNER_WORKSPACE}/logs @@ -103,9 +103,9 @@ jobs: - name: Upload logs as artifact if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: logs + name: logs_compile path: ${{ runner.workspace }}/logs if-no-files-found: ignore @@ -132,12 +132,14 @@ jobs: if: ${{ needs.job_control.outputs.run_unit_tests == 'true' }} strategy: matrix: - tests: - - 'ascii2nc' - - 'pb2nc madis2nc pcp_combine' + include: + - jobid: 'job1' + tests: 'ascii2nc' + - jobid: 'job2' + tests: 'pb2nc madis2nc pcp_combine' fail-fast: false steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Free disk space run: .github/jobs/free_disk_space.sh @@ -150,16 +152,16 @@ jobs: INPUT_DATA_VERSION: ${{ needs.job_control.outputs.input_data_version }}-all - name: Upload output as artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: unit_1a + name: unit_1a_${{ matrix.jobid }} path: ${{ runner.workspace }}/output - name: Upload logs as artifact if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: logs + name: logs_unit_1a_${{ matrix.jobid }} path: ${{ runner.workspace }}/logs if-no-files-found: ignore @@ -170,12 +172,14 @@ jobs: if: ${{ needs.job_control.outputs.run_unit_tests == 'true' }} strategy: matrix: - tests: - - 'ascii2nc_indy pb2nc_indy tc_dland tc_pairs tc_stat plot_tc tc_rmw rmw_analysis tc_diag tc_gen' - - 'met_test_scripts mode_multivar mode_graphics mtd regrid airnow gsi_tools netcdf modis series_analysis gen_ens_prod wwmca_regrid gen_vx_mask grid_weight interp_shape grid_diag grib_tables lidar2nc shift_data_plane trmm2nc aeronet wwmca_plot ioda2nc gaussian' + include: + - jobid: 'job1' + tests: 'ascii2nc_indy pb2nc_indy tc_dland tc_pairs tc_stat plot_tc tc_rmw rmw_analysis tc_diag tc_gen' + - jobid: 'job2' + tests: 'met_test_scripts mode_multivar mode_graphics mtd regrid airnow gsi_tools netcdf modis series_analysis gen_ens_prod wwmca_regrid gen_vx_mask grid_weight interp_shape grid_diag grib_tables lidar2nc shift_data_plane trmm2nc aeronet wwmca_plot ioda2nc gaussian' fail-fast: false steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Free disk space run: .github/jobs/free_disk_space.sh @@ -188,33 +192,36 @@ jobs: INPUT_DATA_VERSION: ${{ needs.job_control.outputs.input_data_version }}-all - name: Upload output as artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: unit_1b + name: unit_1b_${{ matrix.jobid }} path: ${{ runner.workspace }}/output - name: Upload logs as artifact if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: logs + name: logs_unit_1b_${{ matrix.jobid }} path: ${{ runner.workspace }}/logs if-no-files-found: ignore - unit_rc_leads: - name: Unit RC leads + unit_1c: + name: Unit 1c runs-on: ubuntu-latest needs: [job_control, update_input_data, compile] if: ${{ needs.job_control.outputs.run_unit_tests == 'true' }} strategy: matrix: - tests: - - 'ref_config_lead_00 ref_config_lead_12' - - 'ref_config_lead_24 ref_config_lead_48' - - 'ref_config_lead_36' + include: + - jobid: 'job1' + tests: 'ref_config_lead_00 ref_config_lead_12' + - jobid: 'job2' + tests: 'ref_config_lead_24 ref_config_lead_48' + - jobid: 'job3' + tests: 'ref_config_lead_36' fail-fast: false steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Free disk space run: .github/jobs/free_disk_space.sh @@ -227,40 +234,42 @@ jobs: INPUT_DATA_VERSION: ${{ needs.job_control.outputs.input_data_version }}-all - name: Upload output as artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: unit_rc_leads + name: unit_1c_${{ matrix.jobid }} path: ${{ runner.workspace }}/output - name: Upload logs as artifact if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: logs + name: logs_rc_leads_${{ matrix.jobid }} path: ${{ runner.workspace }}/logs if-no-files-found: ignore - unit_rc: - name: Unit RC + unit_2c: + name: Unit 2c runs-on: ubuntu-latest - needs: [job_control, unit_rc_leads] + needs: [job_control, unit_1c] if: ${{ needs.job_control.outputs.run_unit_tests == 'true' }} strategy: matrix: - tests: - - 'ref_config' + include: + - jobid: 'job1' + tests: 'ref_config' fail-fast: false steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Free disk space run: .github/jobs/free_disk_space.sh - name: Download ref_config_leads output from artifact - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: - name: unit_rc_leads path: ${{ runner.workspace }}/output + pattern: unit_1c_job* + merge-multiple: true - name: Run Unit Tests in Docker run: .github/jobs/run_unit_docker.sh @@ -270,16 +279,16 @@ jobs: INPUT_DATA_VERSION: 'none' - name: Upload output as artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: unit_rc + name: unit_2c_${{ matrix.jobid }} path: ${{ runner.workspace }}/output - name: Upload logs as artifact if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: logs + name: logs_unit_2c_${{ matrix.jobid }} path: ${{ runner.workspace }}/logs if-no-files-found: ignore @@ -290,24 +299,30 @@ jobs: if: ${{ needs.job_control.outputs.run_unit_tests == 'true' }} strategy: matrix: - tests: - - 'point_stat stat_analysis_ps' - - 'grid_stat stat_analysis_gs' - - 'wavelet_stat stat_analysis_ws' - - 'ensemble_stat stat_analysis_es' - - 'ugrid' + include: + - jobid: 'job1' + tests: 'point_stat stat_analysis_ps' + - jobid: 'job2' + tests: 'grid_stat stat_analysis_gs' + - jobid: 'job3' + tests: 'wavelet_stat stat_analysis_ws' + - jobid: 'job4' + tests: 'ensemble_stat stat_analysis_es' + - jobid: 'job5' + tests: 'ugrid' fail-fast: false steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Free disk space run: .github/jobs/free_disk_space.sh - name: Download 1a output from artifact - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: - name: unit_1a path: ${{ runner.workspace }}/output + pattern: unit_1a_job* + merge-multiple: true - name: Run Unit Tests in Docker run: .github/jobs/run_unit_docker.sh @@ -317,16 +332,16 @@ jobs: INPUT_DATA_VERSION: ${{ needs.job_control.outputs.input_data_version }}-all - name: Upload output as artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: unit_2a + name: unit_2a_${{ matrix.jobid }} path: ${{ runner.workspace }}/output - name: Upload logs as artifact if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: logs + name: logs_unit_2a_${{ matrix.jobid }} path: ${{ runner.workspace }}/logs if-no-files-found: ignore @@ -337,23 +352,28 @@ jobs: if: ${{ needs.job_control.outputs.run_unit_tests == 'true' }} strategy: matrix: - tests: - - 'climatology_1.0deg' - - 'climatology_1.5deg' - - 'climatology_2.5deg' - - 'python point2grid plot_data_plane mode mode_analysis perc_thresh hira plot_point_obs quality_filter obs_summary duplicate_flag' + include: + - jobid: 'job1' + tests: 'climatology_1.0deg' + - jobid: 'job2' + tests: 'climatology_1.5deg' + - jobid: 'job3' + tests: 'climatology_2.5deg' + - jobid: 'job4' + tests: 'python point2grid plot_data_plane mode mode_analysis perc_thresh hira plot_point_obs quality_filter obs_summary duplicate_flag' fail-fast: false steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Free disk space run: .github/jobs/free_disk_space.sh - name: Download 1a output from artifact - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: - name: unit_1a path: ${{ runner.workspace }}/output + pattern: unit_1a_job* + merge-multiple: true - name: Run Unit Tests in Docker run: .github/jobs/run_unit_docker.sh @@ -363,34 +383,34 @@ jobs: INPUT_DATA_VERSION: ${{ needs.job_control.outputs.input_data_version }}-all - name: Upload output as artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: unit_2b + name: unit_2b_${{ matrix.jobid }} path: ${{ runner.workspace }}/output - name: Upload logs as artifact if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: logs + name: logs_unit_2b_${{ matrix.jobid }} path: ${{ runner.workspace }}/logs if-no-files-found: ignore run_diffs: name: Check for Differences runs-on: ubuntu-latest - needs: [job_control, unit_1b, unit_2a, unit_2b, unit_rc] + needs: [job_control, unit_1b, unit_2a, unit_2b, unit_2c] if: ${{ needs.job_control.outputs.run_diff == 'true' }} steps: - name: Download data from previous jobs - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 - name: Copy test output into single directory run: | mkdir ${RUNNER_WORKSPACE}/output cp -r unit_*/* ${RUNNER_WORKSPACE}/output/ - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Run Diff Tests in Docker run: .github/jobs/run_diff_docker.sh @@ -401,7 +421,7 @@ jobs: - name: Upload diff files as artifact if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: diff path: ${{ runner.workspace }}/diff @@ -409,25 +429,38 @@ jobs: - name: Upload logs as artifact if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: logs + name: logs_diff path: ${{ runner.workspace }}/logs if-no-files-found: ignore + merge_logs: + name: Merge Log Artifacts + runs-on: ubuntu-latest + needs: [run_diffs] + if: ${{ always() }} + steps: + - name: Upload merged logs as artifact + uses: actions/upload-artifact/merge@v4 + with: + name: logs + pattern: logs_* + delete-merged: true + update_truth: name: Update Truth Data runs-on: ubuntu-latest - needs: [job_control, unit_1b, unit_2a, unit_2b, unit_rc] + needs: [job_control, unit_1b, unit_2a, unit_2b, unit_2c] if: ${{ needs.job_control.outputs.run_update_truth == 'true' }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Free disk space run: .github/jobs/free_disk_space.sh - name: Download data from previous jobs - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 - name: Copy test output into single directory run: | diff --git a/.github/workflows/update_truth.yml b/.github/workflows/update_truth.yml index cf9734e449..4b8c310dd7 100644 --- a/.github/workflows/update_truth.yml +++ b/.github/workflows/update_truth.yml @@ -26,7 +26,7 @@ jobs: fi echo ERROR: Branch is $branch_name - must be develop or match main_vX.Y exit 1 - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: Checkout repository with: fetch-depth: 0 diff --git a/configure b/configure index 888d197d7d..40672ec913 100755 --- a/configure +++ b/configure @@ -10139,7 +10139,6 @@ ac_config_files="$ac_config_files Makefile scripts/Rscripts/Makefile scripts/Rsc if test -n "$MET_DEVELOPMENT"; then ac_config_files="$ac_config_files src/tools/dev_utils/Makefile src/tools/dev_utils/shapefiles/Makefile internal/test_util/Makefile internal/test_util/basic/Makefile internal/test_util/basic/vx_config/Makefile internal/test_util/basic/vx_log/Makefile internal/test_util/basic/vx_util/Makefile internal/test_util/libcode/Makefile internal/test_util/libcode/vx_data2d/Makefile internal/test_util/libcode/vx_data2d_factory/Makefile internal/test_util/libcode/vx_data2d_grib/Makefile internal/test_util/libcode/vx_data2d_nc_met/Makefile internal/test_util/libcode/vx_data2d_nc_cf/Makefile internal/test_util/libcode/vx_geodesy/Makefile internal/test_util/libcode/vx_grid/Makefile internal/test_util/libcode/vx_plot_util/Makefile internal/test_util/libcode/vx_ps/Makefile internal/test_util/libcode/vx_tc_util/Makefile internal/test_util/libcode/vx_nc_util/Makefile internal/test_util/libcode/vx_python3_utils/Makefile internal/test_util/libcode/vx_physics/Makefile internal/test_util/libcode/vx_series_data/Makefile internal/test_util/libcode/vx_solar/Makefile internal/test_util/tools/Makefile internal/test_util/tools/other/Makefile internal/test_util/tools/other/mode_time_domain/Makefile" - fi cat >confcache <<\_ACEOF diff --git a/data/config/ConfigConstants b/data/config/ConfigConstants index 16954f5467..23210b6c34 100644 --- a/data/config/ConfigConstants +++ b/data/config/ConfigConstants @@ -7,6 +7,9 @@ // Warning message results in tool exiting. exit_on_warning = FALSE; +// Allowable offset in seconds to silence time difference warning messages +time_offset_warning = 0; + // NetCDF Compression per variable (0 to 9, 0: disable compression) // This will be overridden by the environment variable MET_NC_COMPRESS and the // command line argument. diff --git a/docs/Contributors_Guide/dev_details/tmp_file_use.rst b/docs/Contributors_Guide/dev_details/tmp_file_use.rst index cb5703c53f..3e2525abdb 100644 --- a/docs/Contributors_Guide/dev_details/tmp_file_use.rst +++ b/docs/Contributors_Guide/dev_details/tmp_file_use.rst @@ -39,23 +39,17 @@ those observations, and writes the result to a NetCDF output file. PB2NC creates the following temporary files when running: -* :code:`tmp_pb2nc_blk_{PID}`, :code:`tmp_pb2nc_meta_blk_{PID}`, - :code:`tmp_pb2nc_tbl_blk_{PID}` +* :code:`tmp_pb2nc_blk_{PID}` PB2NC assumes that each input binary file requires Fortran blocking prior to being read by the BUFRLIB library. It applies Fortran blocking, writes the result to this temporary file, and uses BUFRLIB to read its contents. -* :code:`tmp_pb2nc_bufr_{PID}_tbl`: PB2NC extracts Bufr table data +* :code:`tmp_pb2nc_blk_{PID}_tbl`: PB2NC extracts Bufr table data that is embedded in input files and writes it to this temporary file for later use. -.. note:: - The first 3 files listed above are identical. They are all - Fortran-blocked versions of the same input file. Recommend - modifying the logic to only apply Fortran blocking once. - .. _tmp_files_point2grid: Point2Grid Tool diff --git a/docs/Users_Guide/config_options.rst b/docs/Users_Guide/config_options.rst index f2dc2803ba..7a11bcfe3a 100644 --- a/docs/Users_Guide/config_options.rst +++ b/docs/Users_Guide/config_options.rst @@ -505,9 +505,25 @@ If set to true and a MET tool encounters a warning, it will immediately exit with bad status after writing the warning message. .. code-block:: none - + exit_on_warning = FALSE; +time_offset_warning +------------------- + +The "time_offset_warning" entry in ConfigConstants defines an allowable +offset in seconds to silence time differences warning messages. Several +MET tools check the timestamps of the datasets being compared and print a +warning message if they differ. Increasing this option from its default +value of 0 seconds enables datasets that are close in time to be compared +without triggering a warning. If the absolute value of a non-zero time +difference is less than or equal to this setting, a debug log message is +written instead of a warning. + +.. code-block:: none + + time_offset_warning = 0; + nc_compression -------------- @@ -527,7 +543,7 @@ WARNING: Selecting a high compression level may slow down the reading and writing of NetCDF files within MET significantly. .. code-block:: none - + nc_compression = 0; output_precision @@ -539,7 +555,7 @@ files. Setting this option in the config file of one of the tools will override the default value set in ConfigConstants. .. code-block:: none - + output_precision = 5; .. _config_tmp_dir: @@ -554,7 +570,7 @@ Some tools override the temporary directory by the command line argument "-tmp_dir ". .. code-block:: none - + tmp_dir = "/tmp"; A description of the use of temporary files in MET can be found in @@ -573,7 +589,7 @@ logic should be applied. If not defined, the default values listed below are used. .. code-block:: none - + mesage_type_group_map = [ { key = "SURFACE"; val = "ADPSFC,SFCSHP,MSONET"; }, { key = "ANYAIR"; val = "AIRCAR,AIRCFT"; }, @@ -592,7 +608,7 @@ is also supported in PBN2NC as a way of renaming input PREPBUFR message types. .. code-block:: none - + message_type_map = [ { key = "FM-12 SYNOP"; val = "ADPSFC"; }, { key = "FM-13 SHIP"; val = "SFCSHP"; }, @@ -615,7 +631,7 @@ whitespace) to distinguish between their output. e.g. model = "GFS"; .. code-block:: none - + model = "FCST"; desc @@ -631,7 +647,7 @@ between their output. e.g. desc = "QC_9"; .. code-block:: none - + desc = "NA"; obtype @@ -646,7 +662,7 @@ observation message type value is written to the OBTYPE column. Otherwise, the configuration file obtype value is written. .. code-block:: none - + obtype = "ANALYS"; .. _regrid: @@ -748,7 +764,7 @@ using the following entries: Settings defined at higher levels of config file context are not applied. .. code-block:: none - + regrid = { to_grid = NONE; method = NEAREST; @@ -787,7 +803,7 @@ to be verified. This dictionary may include the following entries: Grid-Stat or Point-Stat tools as follows: .. code-block:: none - + fcst = { field = [ { name = "APCP_24_A24_ENS_FREQ_gt0.0"; level = "(*,*)"; @@ -871,7 +887,7 @@ to be verified. This dictionary may include the following entries: Examples of user-defined data censoring operations include: .. code-block:: none - + censor_thresh = [ >12000 ]; censor_val = [ 12000 ]; @@ -945,7 +961,7 @@ to be verified. This dictionary may include the following entries: For example: .. code-block:: none - + fcst = { file_type = GRIB1; GRIB version 1 file_type = GRIB2; GRIB version 2 @@ -1165,7 +1181,7 @@ File-format specific settings for the "field" entry: For example: .. code-block:: none - + field = [ { name = "read_ascii_numpy.py data/python/fcst.txt FCST"; } ]; @@ -1187,16 +1203,15 @@ File-format specific settings for the "field" entry: For example: .. code-block:: none - - file_type = PYTHON_NUMPY; + + file_type = PYTHON_NUMPY; field = [ { name = "read_ascii_numpy.py MET_PYTHON_INPUT_ARG FCST"; } ]; - - + .. code-block:: none - + fcst = { censor_thresh = []; censor_val = []; @@ -1230,13 +1245,13 @@ in a non-trivial way. The length of the "obs.field" array must match the length of the "fcst.field" array. For example: .. code-block:: none - + obs = fcst; or .. code-block:: none - + fcst = { censor_thresh = []; censor_val = []; @@ -1290,7 +1305,7 @@ or than one "message_type" entry is desired within the config file. For example: .. code-block:: none - + fcst = { censor_thresh = []; censor_val = []; @@ -1340,7 +1355,7 @@ or are included or excluded for each verification task. .. code-block:: none - + obs = fcst; climo_mean @@ -1383,7 +1398,7 @@ of several entires defining the climatology file names and fields to be used. entries "match_month", "match_day", and "time_step". .. code-block:: none - + climo_mean = { file_name = [ "/path/to/climatological/mean/files" ]; @@ -1425,7 +1440,7 @@ rather than means. In the example below, this dictionary is set by copying over the "climo_mean" setting and then updating the "file_name" entry. .. code-block:: none - + climo_stdev = climo_mean; climo_stdev = { file_name = [ "/path/to/climatological/standard/deviation/files" ]; @@ -1463,7 +1478,7 @@ climatological bins. The array must begin with 0.0 and end with 1.0. For example: .. code-block:: none - + cdf_bins = [ 0.0, 0.10, 0.25, 0.75, 0.90, 1.0 ]; When "cdf_bins" is set to an integer, it defines the number of bins to be @@ -1472,7 +1487,7 @@ centered on 0.5. An odd number of bins can be centered or uncentered while an even number of bins can only be uncentered. For example: .. code-block:: none - + 4 uncentered bins (cdf_bins = 4; center_bins = FALSE;) yields: 0.0, 0.25, 0.50, 0.75, 1.0 5 uncentered bins (cdf_bins = 5; center_bins = FALSE;) yields: @@ -1490,7 +1505,7 @@ Setting the number of bins to 1 effectively disables this logic by grouping all pairs into a single climatological bin. .. code-block:: none - + climo_cdf = { cdf_bins = 11; or an array of floats center_bins = TRUE; or FALSE @@ -1551,7 +1566,7 @@ in the Wavelet-Stat and MODE tools: * "BOTH" to mask both fields with missing data from the other .. code-block:: none - + mask_missing_flag = BOTH; @@ -1566,7 +1581,7 @@ In PB2NC, the reference time is the PREPBUFR files center time. In Point-Stat and Ensemble-Stat, the reference time is the forecast valid time. .. code-block:: none - + obs_window = { beg = -5400; end = 5400; @@ -1680,7 +1695,7 @@ domain. If the nearest grid point is inside the mask, that point observation is included in the mask. .. code-block:: none - + mask = { grid = [ "FULL" ]; poly = [ "MET_BASE/poly/LMV.poly", @@ -1708,7 +1723,7 @@ value. Therefore, an alpha value of 0.05 corresponds to a 95% confidence interval. .. code-block:: none - + ci_alpha = [ 0.05, 0.10 ]; .. _config_boot: @@ -1767,7 +1782,7 @@ should be used for computing bootstrap confidence intervals: computing platform. .. code-block:: none - + boot = { interval = PCTILE; rep_prop = 1.0; @@ -1836,7 +1851,7 @@ This dictionary may include the following entries: * DW_MEAN for the distance-weighted average value where weight = distance^-2 - + * LS_FIT for a least-squares fit * BILIN for bilinear interpolation (width = 2) @@ -1872,12 +1887,12 @@ This dictionary may include the following entries: permutations of their values are applied. .. code-block:: none - + interp = { field = BOTH; vld_thresh = 1.0; shape = SQUARE; - + type = [ { method = [ NEAREST ]; @@ -1905,7 +1920,7 @@ threshold which defines land (threshold is true) and water (threshold is false). The "land_mask.flag" entry may be set separately in each "obs.field" entry. .. code-block:: none - + land_mask = { flag = FALSE; file_name = []; @@ -1934,7 +1949,7 @@ the verification domain. The "topo_mask.flag" entry may be set separately in each "obs.field" entry. .. code-block:: none - + topo_mask = { flag = FALSE; file_name = []; @@ -1987,7 +2002,7 @@ This dictionary may include the following entries: the obs.cat_thresh thresholds will be used instead. .. code-block:: none - + hira = { flag = FALSE; width = [ 2, 3, 4, 5 ]; @@ -2012,7 +2027,7 @@ output line type from the MET tools. Each line type may be set to one of: "_type.txt" file, a more readable ASCII file sorted by line type. .. code-block:: none - + output_flag = { fho = NONE; Forecast, Hit, Observation Rates ctc = NONE; Contingency Table Counts @@ -2064,7 +2079,7 @@ the output of all the different fields). A value of false means no netcdf output will be generated. .. code-block:: none - + nc_pairs_flag = { latlon = TRUE; raw = TRUE; @@ -2096,7 +2111,7 @@ For example: | .. code-block:: none - + nc_pairs_var_name = ""; nc_pairs_var_suffix @@ -2119,7 +2134,7 @@ NOTE: This option was previously named "nc_pairs_var_str", which is now deprecated. .. code-block:: none - + nc_pairs_var_suffix = ""; ps_plot_flag @@ -2130,7 +2145,7 @@ indicating whether a PostScript plot should be generated summarizing the verification. .. code-block:: none - + ps_plot_flag = TRUE; grid_weight_flag @@ -2154,7 +2169,7 @@ The weights are ultimately computed as the weight at each grid point divided by the sum of the weights for the current masking region. .. code-block:: none - + grid_weight_flag = NONE; hss_ec_value @@ -2172,7 +2187,7 @@ If set, it must greater than or equal to 0.0 and less than 1.0. A value of 0.0 produces an HSS_EC statistic equal to the Accuracy statistic. .. code-block:: none - + hss_ec_value = NA; rank_corr_flag @@ -2184,7 +2199,7 @@ be computed. Computing them over large datasets is computationally intensive and slows down the runtime significantly. .. code-block:: none - + rank_corr_flag = FALSE; duplicate_flag @@ -2206,7 +2221,7 @@ about where duplicates were detected and which observations were used in those cases. .. code-block:: none - + duplicate_flag = NONE; obs_summary @@ -2241,7 +2256,7 @@ about where duplicates were detected and which observations were used in those cases. .. code-block:: none - + obs_summary = NONE; @@ -2251,13 +2266,13 @@ obs_perc_value Percentile value to use when obs_summary = PERC .. code-block:: none - + obs_perc_value = 50; obs_quality_inc --------------- - + The "obs_quality_inc" entry specifies the quality flag values that are to be retained and used for verification. An empty list signifies that all point observations should be used, regardless of their quality flag value. @@ -2267,13 +2282,13 @@ an array of strings, even if the values themselves are numeric. Note "obs_quality_inc" replaces the older option "obs_quality". .. code-block:: none - + obs_quality_inc = [ "1", "2", "3", "9" ]; obs_quality_exc --------------- - + The "obs_quality_exc" entry specifies the quality flag values that are to be ignored and not used for verification. An empty list signifies that all point observations should be used, regardless of their quality flag value. @@ -2282,7 +2297,7 @@ observations. The quality flag values to ignore should be specified as an array of strings, even if the values themselves are numeric. .. code-block:: none - + obs_quality_exc = [ "1", "2", "3", "9" ]; @@ -2295,7 +2310,7 @@ should be set to the installed *share/met* directory so the MET tools can locate the static data files they need at run time. .. code-block:: none - + met_data_dir = "MET_BASE"; many_plots @@ -2318,7 +2333,7 @@ forecast field: for this plot. .. code-block:: none - + fcst_raw_plot = { color_table = "MET_BASE/colortables/met_default.ctable"; plot_min = 0.0; @@ -2340,7 +2355,7 @@ to modify the output file name and avoid naming conflicts for multiple runs of the same tool. .. code-block:: none - + output_prefix = ""; version @@ -2351,7 +2366,7 @@ The configuration file version number should match the version number of the MET code being run. This value should generally not be modified. .. code-block:: none - + version = "VN.N"; time_summary @@ -2369,7 +2384,7 @@ ending time offsets in seconds. For example: .. code-block:: none - + beg = "00"; end = "235959"; step = 300; @@ -2387,7 +2402,7 @@ uncentered time intervals. The following example requests observations for one hour prior: .. code-block:: none - + width = { beg = -3600; end = 0; } The summaries will only be calculated for the specified GRIB codes @@ -2421,7 +2436,7 @@ They are inclusive (union). All variables are included if both options are empty. Note: grib_code 11 is equivalent to obs_var "TMP". .. code-block:: none - + time_summary = { flag = FALSE; beg = "000000"; @@ -2469,7 +2484,7 @@ entries. This dictionary may include the following entries: ensemble field. .. code-block:: none - + ens = { censor_thresh = []; censor_val = []; @@ -2500,7 +2515,7 @@ combination of the categorical threshold (cat_thresh) and neighborhood width specified. .. code-block:: none - + nbrhd_prob = { width = [ 5 ]; shape = CIRCLE; @@ -2520,7 +2535,7 @@ combination of the categorical threshold (cat_thresh), neighborhood width (nbrhd_prob.width), and smoothing method (nmep_smooth.type) specified. .. code-block:: none - + nmep_smooth = { vld_thresh = 0.0; shape = CIRCLE; @@ -2571,7 +2586,7 @@ which ensemble products should be generated: rank NetCDF output file. .. code-block:: none - + ensemble_flag = { latlon = TRUE; mean = TRUE; @@ -2615,7 +2630,7 @@ computation of the ranked probability score. If left empty, but climatology data is provided, the climo_cdf thresholds will be used instead. .. code-block:: none - + fcst = { message_type = [ "ADPUPA" ]; ens_ssvar_bin_size = 1; @@ -2645,7 +2660,7 @@ unique. e.g. nc_var_str = "MIN"; .. code-block:: none - + nc_var_str = ""; obs_thresh @@ -2659,7 +2674,7 @@ Verification output will be computed separately for each threshold specified. This option may be set separately for each obs.field entry. .. code-block:: none - + obs_thresh = [ NA ]; skip_const @@ -2673,7 +2688,7 @@ false, constant points are included and the observation rank is chosen at random. .. code-block:: none - + skip_const = FALSE; obs_error @@ -2723,7 +2738,7 @@ type, instrument type, station ID, range of heights, range of pressure levels, and range of values. .. code-block:: none - + obs_error = { flag = FALSE; TRUE or FALSE dist_type = NONE; Distribution type @@ -2741,7 +2756,7 @@ See: `Random Number Generator Performance =5.0, >=10.0, >=15.0 ]; * The "vld_thresh" entry is described above. @@ -3156,7 +3171,7 @@ The object definition settings for MODE are contained within the "fcst" and Multiple merge thresholds may be specified as an array. For example: .. code-block:: none - + merge_thresh = [ >=1.0, >=2.0, >=3.0 ]; * The "merge_flag" entry specifies the merging methods to be applied: @@ -3171,7 +3186,7 @@ The object definition settings for MODE are contained within the "fcst" and * "BOTH" for both the double-threshold and engine merging methods .. code-block:: none - + fcst = { field = { name = "APCP"; @@ -3199,7 +3214,7 @@ setting the appropriately will help ensure that appropriate default values are used for these variables. .. code-block:: none - + grid_res = 4; match_flag @@ -3218,7 +3233,7 @@ The "match_flag" entry specifies the matching method to be applied: * "NO_MERGE" for matching with no additional merging in either field .. code-block:: none - + match_flag = MERGE_BOTH; max_centroid_dist @@ -3230,7 +3245,7 @@ Setting this to a reasonable value speeds up the runtime enabling MODE to skip unreasonable object comparisons. .. code-block:: none - + max_centroid_dist = 800.0/grid_res; weight @@ -3243,7 +3258,7 @@ total interest value is computed, the weighted sum is normalized by the sum of the weights listed. .. code-block:: none - + weight = { centroid_dist = 2.0; boundary_dist = 4.0; @@ -3258,7 +3273,7 @@ sum of the weights listed. interest_function ^^^^^^^^^^^^^^^^^ - + The set of interest function variables listed define which values are of interest for each pairwise attribute measured. The interest functions may be defined as a piecewise linear function or as an algebraic expression. A @@ -3267,7 +3282,7 @@ graph. An algebraic function may be defined in terms of several built-in mathematical functions. .. code-block:: none - + interest_function = { centroid_dist = ( @@ -3321,7 +3336,7 @@ threshold is applied to the total interest values computed for each pair of objects and is used in determining matches. .. code-block:: none - + total_interest_thresh = 0.7; print_interest_thresh @@ -3336,7 +3351,7 @@ be written as long as the distance between the object centroids is less than the max_centroid_dist variable. .. code-block:: none - + print_interest_thresh = 0.0; plot_valid_flag @@ -3348,7 +3363,7 @@ indicates the entire domain should be plotted; FALSE indicates only the region containing valid data after masking should be plotted. .. code-block:: none - + plot_valid_flag = FALSE; plot_gcarc_flag @@ -3359,7 +3374,7 @@ polylines should be plotted using great circle arcs as opposed to straight lines in the grid. .. code-block:: none - + plot_gcarc_flag = FALSE; ct_stats_flag @@ -3369,7 +3384,7 @@ The ct_stats_flag can be set to TRUE or FALSE to produce additional output, in the form of contingency table counts and statistics. .. code-block:: none - + ct_stats_flag = TRUE; shift_right @@ -3384,7 +3399,7 @@ This option provides a very specialized case of automated regridding. The much more flexible "regrid" option may be used instead. .. code-block:: none - + shift_right = 0; PB2NCConfig_default @@ -3467,7 +3482,7 @@ For example: `Current Table A Entries in PREPBUFR mnemonic table `_ .. code-block:: none - + message_type = []; station_id @@ -3481,7 +3496,7 @@ stations should be retained. For example: station_id = [ "KDEN" ]; .. code-block:: none - + station_id = []; elevation_range @@ -3492,7 +3507,7 @@ entries specifying the range of observing locations elevations to be retained. .. code-block:: none - + elevation_range = { beg = -1000; end = 100000; @@ -3516,7 +3531,7 @@ For example: | .. code-block:: none - + pb_report_type = []; in_report_type @@ -3536,7 +3551,7 @@ For example: | .. code-block:: none - + in_report_type = []; instrument_type @@ -3546,7 +3561,7 @@ The "instrument_type" entry is an array of instrument types to be retained. An empty list indicates that all should be retained. .. code-block:: none - + instrument_type = []; level_range @@ -3556,7 +3571,7 @@ The "level_range" entry is a dictionary which contains "beg" and "end" entries specifying the range of vertical levels (1 to 255) to be retained. .. code-block:: none - + level_range = { beg = 1; end = 255; @@ -3596,7 +3611,7 @@ An empty list indicates that all should be retained. See: `Current Table A Entries in PREPBUFR mnemonic table `_ .. code-block:: none - + level_category = []; obs_bufr_var @@ -3617,7 +3632,7 @@ command line option to see the list of available observation variables. | .. code-block:: none - + obs_bufr_var = [ "QOB", "TOB", "ZOB", "UOB", "VOB" ]; obs_bufr_map @@ -3629,7 +3644,7 @@ Users may choose to rename BUFR variables to match the naming convention of the forecast the observation is used to verify. .. code-block:: none - + obs_bufr_map = []; obs_prepbufr_map @@ -3642,7 +3657,7 @@ backward-compatibility for earlier versions of MET which wrote GRIB abbreviations to the output. .. code-block:: none - + obs_prepbufr_map = [ { key = "POB"; val = "PRES"; }, { key = "QOB"; val = "SPFH"; }, @@ -3669,7 +3684,7 @@ GREATER THAN this threshold will be discarded. See `Code table for observation quality markers `_ .. code-block:: none - + quality_mark_thresh = 2; event_stack_flag @@ -3680,7 +3695,7 @@ specify whether observations should be drawn from the top of the event stack (most quality controlled) or the bottom of the event stack (most raw). .. code-block:: none - + event_stack_flag = TOP; SeriesAnalysisConfig_default @@ -3697,7 +3712,7 @@ If set less than or equal to 0, it is automatically reset to the number of grid points, and they are all processed concurrently. .. code-block:: none - + block_size = 1024; vld_thresh @@ -3710,7 +3725,7 @@ setting requires all data in the series to be valid. .. code-block:: none - + vld_thresh = 1.0; output_stats @@ -3722,7 +3737,7 @@ it is recommended to process a few output types at a time, especially if the grid is large. .. code-block:: none - + output_stats = { fho = []; ctc = []; @@ -3816,7 +3831,7 @@ Where "job_name" is set to one of the following: Optional Args: .. code-block:: none - + -by column_name to specify case information -out_alpha to override default alpha value of 0.05 -derive to derive statistics on the fly @@ -3830,7 +3845,7 @@ Where "job_name" is set to one of the following: types may be aggregated: .. code-block:: none - + -line_type FHO, CTC, MCTC, SL1L2, SAL1L2, VL1L2, VAL1L2, PCT, NBRCNT, NBRCTC, GRAD, @@ -3848,7 +3863,7 @@ Where "job_name" is set to one of the following: combinations of "-line_type" and "-out_line_type" are listed below. .. code-block:: none - + -line_type FHO, CTC, -out_line_type CTS, ECLV -line_type MCTC -out_line_type MCTS -line_type SL1L2, SAL1L2, -out_line_type CNT @@ -3870,7 +3885,7 @@ Where "job_name" is set to one of the following: Additional Required Args for -line_type MPR: .. code-block:: none - + -out_thresh or -out_fcst_thresh and -out_obs_thresh When -out_line_type FHO, CTC, CTS, MCTC, MCTS, PCT, PSTD, PJC, PRC @@ -3878,7 +3893,7 @@ Where "job_name" is set to one of the following: Additional Optional Args for -line_type MPR: .. code-block:: none - + -mask_grid, -mask_poly, -mask_sid -out_thresh or -out_fcst_thresh and -out_obs_thresh -out_cnt_logic @@ -3890,14 +3905,14 @@ Where "job_name" is set to one of the following: Additional Optional Arg for: .. code-block:: none - + -line_type ORANK -out_line_type PHIST, SSVAR ... -out_bin_size Additional Optional Args for: .. code-block:: none - + -out_line_type ECLV ... -out_eclv_points @@ -3941,7 +3956,7 @@ Where "job_name" is set to one of the following: For lead times of 12, 24, 36, and 48 hours, it contains RMSE for: .. code-block:: none - + - Wind Speed at the surface(b), 850(a), 400(a), 250(a) mb - Dew point Temperature at the surface(b), 850(b), 700(b), 400(b) mB - Temperature at the surface(b), 400(a) mB @@ -3966,7 +3981,7 @@ Where "job_name" is set to one of the following: Required Args: .. code-block:: none - + -ramp_thresh (-ramp_thresh_fcst or -ramp_thresh_obs) For DYDT, threshold for the amount of change required to define an event. @@ -3977,7 +3992,7 @@ Where "job_name" is set to one of the following: Optional Args: .. code-block:: none - + -ramp_type str Overrides the default ramp definition algorithm to be used. May be set to DYDT (default) or SWING for the swinging door @@ -4010,7 +4025,7 @@ Where "job_name" is set to one of the following: union: .. code-block:: none - + "-model name" "-fcst_lead HHMMSS" "-obs_lead HHMMSS" @@ -4053,7 +4068,7 @@ Where "job_name" is set to one of the following: performed on their intersection: .. code-block:: none - + "-column_min col_name value" e.g. -column_min BASER 0.02 "-column_max col_name value" "-column_eq col_name value" @@ -4078,7 +4093,7 @@ Where "job_name" is set to one of the following: "-mask_sid file|list" see description of "sid" entry above .. code-block:: none - + "-out_line_type name" "-out_thresh value" sets both -out_fcst_thresh and -out_obs_thresh "-out_fcst_thresh value" multiple for multi-category contingency tables @@ -4103,7 +4118,7 @@ Where "job_name" is set to one of the following: entry .. code-block:: none - + "-out_alpha value" .. code-block:: none @@ -4123,7 +4138,7 @@ Where "job_name" is set to one of the following: For aggregate and aggregate_stat job types: .. code-block:: none - + "-out_stat path" to write a .stat output file for the job including the .stat header columns. Multiple values for each header column are written as @@ -4138,7 +4153,7 @@ Where "job_name" is set to one of the following: of the output .stat output file: .. code-block:: none - + -job aggregate_stat -line_type MPR -out_line_type CNT \ -by OBS_SID -set_hdr VX_MASK OBS_SID -stat_out out.stat When using mulitple "-by" options, use "CASE" to reference the full string: @@ -4146,7 +4161,7 @@ Where "job_name" is set to one of the following: .. code-block:: none - + jobs = [ "-job filter -line_type SL1L2 -vx_mask DTC165 \ -dump_row job_filter_SL1L2.stat", @@ -4192,7 +4207,7 @@ statistic name. Statistics using the default arithemtic mean method do not need to be listed. .. code-block:: none - + wmo_sqrt_stats = []; wmo_fisher_stats = []; @@ -4202,7 +4217,7 @@ table counts or partial sums. The VIF is used to adjust the normal confidence intervals computed for the aggregated statistics. .. code-block:: none - + vif_flag = FALSE; WaveletStatConfig_default @@ -4221,7 +4236,7 @@ Wavelet-Stat into dyadic (2^n x 2^n) tiles: * "PAD" to pad the input data out to the nearest integer power of 2. .. code-block:: none - + grid_decomp_flag = AUTO; tile @@ -4238,7 +4253,7 @@ in Wavelet-Stat when the "grid_decomp_flag" is set to "TILE": coordinates of the tile. .. code-block:: none - + tile = { width = 0; location = [ @@ -4279,7 +4294,7 @@ wavelet decomposition should be performed: 204, 206, 208, 301, 303, 305, 307, 309) .. code-block:: none - + wavelet = { type = HAAR; member = 2; @@ -4313,7 +4328,7 @@ Supply the NetCDF output information. For example: level = "SFC"; .. code-block:: none - + variable_name = ""; units = ""; long_name = ""; @@ -4336,7 +4351,7 @@ The swap_endian option indicates whether the endian-ness of the data should be swapped after reading. .. code-block:: none - + swap_endian = TRUE; write_pixel_age @@ -4347,5 +4362,5 @@ command line to the output file. This option writes the pixel age data, in minutes, to the output file instead. .. code-block:: none - + write_pixel_age = FALSE; diff --git a/docs/Users_Guide/figure/installation_dir.png b/docs/Users_Guide/figure/installation_dir.png new file mode 100644 index 0000000000000000000000000000000000000000..40f1b48231ef9e749bf5217b31a8640c783768be GIT binary patch literal 90216 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 YEV2Dr%FZI;iTmJKa^@>J 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 literal 0 HcmV?d00001 diff --git a/docs/Users_Guide/installation.rst b/docs/Users_Guide/installation.rst index 668da2f71d..4540d341b4 100644 --- a/docs/Users_Guide/installation.rst +++ b/docs/Users_Guide/installation.rst @@ -1,385 +1,583 @@ .. _installation: -************************************* -Software Installation/Getting Started -************************************* +************ +Installation +************ Introduction ============ -This section describes how to install the MET package. MET has been developed and tested on Linux operating systems. Support for additional platforms and compilers may be added in future releases. The MET package requires many external libraries to be available on the user's computer prior to installation. Required and recommended libraries, how to install MET, the MET directory structure, and sample cases are described in the following sections. - -Supported Architectures -======================= - -The MET package was developed on Debian Linux using the GNU compilers and the Portland Group (PGI) compilers. The MET package has also been built on several other Linux distributions using the GNU, PGI, and Intel compilers. Past versions of MET have also been ported to IBM machines using the IBM compilers, but we are currently unable to support this option as the development team lacks access to an IBM machine for testing. Other machines may be added to this list in future releases as they are tested. In particular, the goal is to support those architectures supported by the WRF model itself. - -The MET tools run on a single processor. Therefore, none of the utilities necessary for running WRF on multiple processors are necessary for running MET. Individual calls to the MET tools have relatively low computing and memory requirements. However users will likely be making many calls to the tools and passing those individual calls to several processors will accomplish the verification task more efficiently. - -Programming Languages -===================== - -The MET package, including MET-TC, is written primarily in C/C++ in order to be compatible with an extensive verification code base in C/C++ already in existence. In addition, the object-based MODE and MODE-TD verification tools rely heavily on the object-oriented aspects of C++. Knowledge of C/C++ is not necessary to use the MET package. The MET package has been designed to be highly configurable through the use of ASCII configuration files, enabling a great deal of flexibility without the need for source code modifications. - -With the release of MET-11.1.0, C++11 is now the minimum required version of the C++ programming language standard. - -NCEP's BUFRLIB is written entirely in Fortran. The portion of MET that handles the interface to the BUFRLIB for reading PrepBUFR point observation files is also written in Fortran. - -The MET package is intended to be a tool for the modeling community to use and adapt. As users make upgrades and improvements to the tools, they are encouraged to offer those upgrades to the broader community by offering feedback to the developers. - -Required Compilers and Scripting Languages -========================================== - -The MET package was developed and tested using the GNU g++/gfortran compilers and the Intel icc/ifort compilers. As additional compilers are successfully tested, they will be added to the list of supported platforms/compilers. - -The GNU make utility is used in building all executables and is therefore required. - -The MET package consists of a group of command line utilities that are compiled separately. The user may choose to run any subset of these utilities to employ the type of verification methods desired. New tools developed and added to the toolkit will be included as command line utilities. - -In order to control the desired flow through MET, users are encouraged to run the tools via a script or consider using the `METplus package `_. Some sample scripts are provided in the distribution; these examples are written in the Bourne shell. However, users are free to adapt these sample scripts to any scripting language desired. +This section is meant to provide guidance on installing MET. It assumes a +beginner user to MET is compiling MET from scratch and will step through +the installation process, including listing dependent libraries and how +to install them. Installation will require an understanding of the +environment and hardware that MET is being installed on as it has options +that are dependent on compiler usage, modulefiles, etc. + +There are multiple supported methods for installing MET: using a provided +compilation script, Docker instances, and Apptainer instances. +All of these methods will be described below. The recommended method is +using the provided +:ref:`compile_script_install`. + +Some organizations have access to precompiled versions of MET on +shared systems, making the need for self-installation unnecessary. +Users should check the +`METplus Downloads page `_ +for the latest coordinated release’s Existing Builds page for +existing MET installations before continuing. + +.. _required_external_libraries_to_build_MET: + +Required External Libraries +=========================== + +MET is dependent on several external libraries to function properly. +The required libraries are listed below: + +* `BUFRLIB `_ + for reading PrepBufr Observation files +* `NetCDF4 `_ + in C and CXX, for intermediate and output file formats +* `HDF5 `__ + is required to support NetCDF 4. HDF5 should be built with + `zlib `_ +* `GSL `_ + GNU Scientific Library Developer's Version for computing + confidence intervals (use **GSL-1.11** for **PGI** compilers) +* `Proj `_ Library used to instantiate grids within MET + +The following libraries are conditionally required, depending on the intended +verification use and compiler language: + +* `GRIB2C `_ + Library (with dependencies Z, PNG, JASPER), if compiling GRIB2 support +* `Python `_ Libraries, + if compiling support for Python embedding +* `ecKit `_ + Library, if compiling support for unstructured grids +* `ATLAS `_ + Library, if compiling support for unstructured grids +* `HDF4 `__ + library if compiling the MODIS-Regrid or lidar2nc tool +* `HDF-EOS2 `__ + library if compiling the MODIS-Regrid or lidar2nc tool +* `Cairo `_ + library if compiling the MODE-Graphics tool +* `FreeType `_ + library if compiling the MODE-Graphics tool +* `f2c `_ + library for interfacing between Fortran and C + (**Not required for most compilers**) + +Users can take advantage of the compilation script to download and install all of the +libraries automatically, both required and conditionally required +:ref:`compile_script_install`. + +.. _suggested_external_utiliites: + +Suggested External Utilities +============================ + +The following utilities have been used with success by other METplus users in their verification processes. +They are not required for MET to function, but depending on the user’s intended verification needs, they may be of use: + +* `copygb utility `_ + for re-gridding GRIB version 1 data +* `wgrib2 utility `_ + for re-gridding GRIB version 2 data +* `Integrated Data Viewer (IDV) `_ + for displaying gridded data, including GRIB and NetCDF +* `ncview utility `_ + for viewing gridded NetCDF data (e.g. the output of pcp_combine) + +.. _compile_script_install: + +Using the compile_MET_all.sh script +=================================== + +The **compile_MET_all.sh** script is designed to install MET and, +if desired, all +of the external library dependencies required for running the system on various +platforms. There are some required environment variables that need to be set +before running the script and some optional environment variables, both of +which are described below. The script relies on a **tar_files.tgz** file, which +contains all of the required and optional library packages for MET but not +MET itself. A separate command will be used to pull down the latest version of +MET in **tar_files.tgz** +format from GitHub, which the script will then install. + +To begin, create and change to a directory where the latest version of MET will be +installed. Assuming that the following guidance uses “/d1” as the parent directory, +a suggested format is a path to a “met” directory, followed by the version number +subdirectory (e.g. */d1/met/12.0.0*). +Next, download the +`compile_MET_all.sh `_ +script and +`tar_files.tgz `_ +file and place both of these files in the +new directory. These files are available either +through using the hyperlinks provided or by entering the following commands in +the terminal while in the directory MET will be installed in: + +.. code-block:: ini + + wget https://raw.githubusercontent.com/dtcenter/MET/main_v12.0/internal/scripts/installation/compile_MET_all.sh + wget https://dtcenter.ucar.edu/dfiles/code/METplus/MET/installation/tar_files.tgz + +The tar files will need to be extracted in the MET installation directory: + +.. code-block:: ini + + tar -zxf tar_files.tgz + +To make the compilation script into an executable, change the permissions to the following: + +.. code-block:: ini + + chmod 775 compile_MET_all.sh + +Now change directories to the one that was created from expanding the tar files: + +.. code-block:: ini + + cd tar_files + +The next step will be to identify and download the latest MET release as a +tar file (e.g. **v12.0.0.tar.gz**) and place it in +the *tar_files* directory. The file is available from the +MET line under the “RECOMMENDED - COMPONENTS” section on the +`METplus website `_ or +by using a wget command while in the *tar_files* directory: + +.. code-block:: ini + + wget https://github.com/dtcenter/MET/archive/refs/tags/v12.0.0.tar.gz .. _Install_Required-libraries-and: -Required Libraries and Optional Utilities -========================================= - -Three external libraries are required for compiling/building MET and should be downloaded and installed before attempting to install MET. Additional external libraries required for building advanced features in MET are discussed in :numref:`Installation-of-required` : - -1. NCEP's BUFRLIB is used by MET to decode point-based observation datasets in PrepBUFR format. BUFRLIB is distributed and supported by NCEP and is freely available for download from `NCEP's BUFRLIB website `_. BUFRLIB requires C and Fortran-90 compilers that should be from the same family of compilers used when building MET. - -2. Several tools within MET use Unidata's NetCDF libraries for writing output NetCDF files. NetCDF libraries are distributed and supported by Unidata and are freely available for download from `Unidata's NetCDF website `_. The same family of compilers used to build NetCDF should be used when building MET. MET is now compatible with the enhanced data model provided in NetCDF version 4. The support for NetCDF version 4 requires NetCDF-C, NetCDF-CXX, and HDF5, which is freely available for download on the `HDF5 webpage `_. - -3. The GNU Scientific Library (GSL) is used by MET when computing confidence intervals. GSL is distributed and supported by the GNU Software Foundation and is freely available for download from the `GNU website `_. - -4. The Zlib is used by MET for compression when writing postscript image files from tools (e.g. MODE, Wavelet-Stat, Plot-Data-Plane, and Plot-Point-Obs). Zlib is distributed, supported and is freely available for download from the `Zlib website `_. - -Two additional utilities are strongly recommended for use with MET: - -1. The Unified Post-Processor is recommended for post-processing the raw WRF model output prior to verifying the model forecasts with MET. The Unified Post-Processor is freely available for `download `_. MET can read data on a standard, de-staggered grid and on pressure or regular levels in the vertical. The Unified Post-Processor outputs model data in this format from both WRF cores, the NMM and the ARW. However, the Unified Post-Processor is not strictly required as long as the user can produce gridded model output on a standard de-staggered grid on pressure or regular levels in the vertical. Two-dimensional fields (e.g., precipitation amount) are also accepted for some modules. - -2. The copygb utility is recommended for re-gridding model and observation datasets in GRIB version 1 format to a common verification grid. The copygb utility is distributed as part of the Unified Post-Processor and is available from other sources as well. While earlier versions of MET required that all gridded data be placed on a common grid, MET version 5.1 added support for automated re-gridding on the fly. After version 5.1, users have the option of running copygb to regrid their GRIB1 data ahead of time or leveraging the automated regridding capability within MET. - -.. _Installation-of-required: - -Installation of Required Libraries -================================== - -As described in :numref:`Install_Required-libraries-and`, some external libraries are required for building the MET: - -1. -NCEP's BUFRLIB is used by the MET to decode point-based observation datasets in PrepBUFR format. Once you have downloaded and unpacked the BUFRLIB tarball, refer to the README_BUFRLIB file. When compiling the library using the GNU C and Fortran compilers, users are strongly encouraged to use the -DUNDERSCORE and -fno-second-underscore options. Compiling the BUFRLIB version 11.3.0 (recommended version) using the GNU compilers consists of the following three steps: - -.. code-block:: none - - gcc -c -DUNDERSCORE `./getdefflags_C.sh` *.c >> make.log - gfortran -c -fno-second-underscore -fallow-argument-mismatch `./getdefflags_F.sh` modv*.F moda*.F \ - `ls -1 *.F *.f | grep -v "mod[av]_"` >> make.log - ar crv libbufr.a *.o - -Compiling the BUFRLIB using the PGI C and Fortran-90 compilers consists of the following three steps: - -.. code-block:: none - - pgcc -c -DUNDERSCORE `./getdefflags_C.sh` *.c >> make.log - pgf90 -c -Mnosecond_underscore `./getdefflags_F.sh` modv*.F moda*.F \ - `ls -1 *.F *.f | grep -v "mod[av]_"` >> make.log - ar crv libbufr.a *.o - -Compiling the BUFRLIB using the Intel icc and ifort compilers consists of the following three steps: - -.. code-block:: none +Environment Variables to Run Script +----------------------------------- + +Before running the compilation script, there are five environment variables +that are required: +**TEST_BASE**, **COMPILER**, **MET_SUBDIR**, **MET_TARBALL**, and **USE_MODULES**. +If compiling support for Python embedding, the script will need the following +additional environment variables: **MET_PYTHON**, **MET_PYTHON_CC**, and +**MET_PYTHON_LD**. All of these environment variables are discussed +in further detail in the Environment Variable Descriptions section below. +An easy way to set these environment variables is in an environment +configuration file (for example, **install_met_env.**). An +example environment configuration file to start from (**install_met_env.generic_gnu**), +as well as environment configuration files used on HPCs at NCAR and NOAA, +can be found in the `MET GitHub repository `_ in the +`scripts/installation/config `_ +directory. + +Environment Variable Descriptions +--------------------------------- + +.. dropdown:: REQUIRED + + **TEST_BASE** – Format is */d1/met/12.0.0*. This is the MET + installation directory that was created + the beginning of, :numref:`compile_script_install` and contains the + **compile_MET_all.sh** script, **tar_files.tgz**, + and the *tar_files* directory from the untar command. + + **COMPILER** – Format is *compiler_version* (e.g. gnu_8.3.0). For the GNU family of compilers, + use “gnu”; for the Intel family of compilers, use “intel”, "intel-classic", + “intel-oneapi”, “ics”, “ips”, or “PrgEnv-intel”, + depending on the system. If using an Intel compiler, users that have also + set the **USE_MODULES** environment variable to TRUE should review the additional + information below for proper configuration file setup. In the past, support was + provided for the PGI family of compilers through “pgi”. However, this compiler + option is no longer actively tested. + + **MET_SUBDIR** – Format is */d1/met/12.0.0*. This is the location where the top-level MET + subdirectory will + be installed and is often set equivalent to **TEST_BASE** (e.g. ${TEST_BASE}). + + **MET_TARBALL** – Format is *v12.0.0tar.gz*. This is the name of the downloaded MET tarball. + + **USE_MODULES** – Format is *TRUE* or *FALSE*. Set to FALSE if using a machine that does not use + modulefiles; set to TRUE if using a machine that does use modulefiles. For more information on + modulefiles, visit the `Wikipedia page `_. + If the **USE_MODULES** setting is set to true and the compiler is an Intel compiler, please + review the additional information below for proper configuration file setup. + + **PYTHON_MODULE** - Format is *PythonModuleName_version* (e.g. python_3.10.4). This environment variable + is only required if **USE_MODULES** = TRUE. To set properly, list the Python module to load + followed by an underscore and version number. For example, setting + **PYTHON_MODULE** =python_3.10.4 + will cause the script to run "module load python/3.10.4". + +.. dropdown:: ADDITIONAL SETTINGS FOR INTEL COMPILER USERS WITH THE USE_MODULES SETTING + + It is necessary for the user to specify (in the install_met_env. config file) the + following environment variables if using the Intel compilers: + + | For non-oneAPI Intel compilers: + | export FC=ifort + | export F77=ifort + | export F90=ifort + | export CC=icc + | export CXX=icpc + + + | For oneAPI Intel compilers: + | export FC=ifx + | export F77=ifx + | export F90=ifx + | export CC=icx + | export CXX=icpx + + This is due to the machines allowing users to load a module but not setting these environment + variables as expected, leading to failed installations. For user convenience, additional + generic configuration files have been created that include these settings. Users with a + classic Intel compiler are encouraged to use the install_met_env.generic_intel_classic + configuration file, and users with a oneAPI Intel compiler should use the + install_met_env.generic_intel_oneapi configuration file. + + +.. dropdown:: REQUIRED, IF COMPILING PYTHON EMBEDDING + + **MET_PYTHON** – Format is */usr/local/python3*. + This is the location + containing the bin, include, lib, and share directories for Python. + + **MET_PYTHON_CC** - Format is -I followed by the directory containing + the Python include files (ex. -I/usr/local/python3/include/python3.10). + This information may be obtained by + running :code:`python3-config --cflags`; + however, this command can, on certain systems, + provide too much information. + + **MET_PYTHON_LD** - Format is -L followed by the directory containing + the Python library + files then a space, then -l followed by the necessary Python + libraries to link to + (ex. -L/usr/local/python3/lib/\\ -lpython3.10\\ + -lpthread\\ -ldl\\ -lutil\\ -lm). + The backslashes are necessary in the example shown because of + the spaces, which will be + recognized as the end of the value unless preceded by the “\\” + character. Alternatively, + a user can provide the value in quotations + (e.g. export MET_PYTHON_LD="-L/usr/local/python3/lib/ + -lpython3.10 -lpthread -ldl -lutil -lm"). + This information may be obtained by running + :code:`python3-config --ldflags --embed`; however, + this command can, on certain systems, provide too much information. + +.. dropdown:: OPTIONAL + + **export MAKE_ARGS="-j #"** – If there is a need to install external + libraries, or to attempt + to speed up the MET compilation process, this environmental + setting can be added to the + environment configuration file. Replace the # with the number + of cores to use + (as an integer) or simply specify "export MAKE_ARGS=-j" + with no integer argument to + start as many processes in parallel as possible. Note that Docker + has trouble compiling + without a specified value of cores to use. The automated MET + testing scripts in the + Docker environment have been successful with a value of + 5 (ex. export MAKE_ARGS=”-j 5”). + + +External Library Handling in compile_MET_all.sh +----------------------------------------------- + +.. dropdown:: IF THE USER WANTS TO HAVE THE COMPILATION SCRIPT DOWNLOAD THE LIBRARY DEPENDENCIES + + The **compile_MET_all.sh** script will compile and install MET and its + :ref:`required_external_libraries_to_build_MET`, if needed. + Note that if these libraries are already installed somewhere on the system, + MET will call and use the libraries that were installed by the script. + +.. dropdown:: IF THE USER ALREADY HAS THE LIBRARY DEPENDENCIES INSTALLED + + If the required external library dependencies have already been installed and don’t + need to be reinstalled, or if compiling MET on a machine that uses modulefiles and + the user would like to make use of the existing dependent libraries on that machine, + there are more environment variables that need to be set to let MET know where those + library and header files are. The following environment variables need to be added + to the environment configuration file: + + +-------------------+--------------------------------+------------------------------+ + | **Feature** | **Configuration Option** | **Environment Variables** | + +===================+================================+==============================+ + | *Always* | | MET_BUFRLIB, | + | | | | + | *Required* | | BUFRLIB_NAME, | + | | | | + | | | MET_PROJ, | + | | | | + | | | MET_HDF5, | + | | | | + | | | MET_NETCDF, | + | | | | + | | | MET_GSL | + +-------------------+--------------------------------+------------------------------+ + | *Optional* | :code:`--enable-all` or | MET_GRIB2CLIB, | + | | | | + | GRIB2 | :code:`--enable-grib2` | MET_GRIB2CINC, | + | | | | + | Support | | GRIB2CLIB_NAME, | + | | | | + | | | LIB_JASPER, | + | | | | + | | | LIB_PNG, | + | | | | + | | | LIB_Z | + +-------------------+--------------------------------+------------------------------+ + | *Optional* | :code:`--enable-all` or | MET_PYTHON_BIN_EXE, | + | | | | + | Python | :code:`--enable-python` | MET_PYTHON_CC, | + | | | | + | Support | | MET_PYTHON_LD | + +-------------------+--------------------------------+------------------------------+ + | *Optional* | :code:`--enable-all` or | MET_ATLAS, | + | | | | + | Unstructured Grid | :code:`--enable-ugrid` | MET_ECKIT | + | | | | + | Support | | | + +-------------------+--------------------------------+------------------------------+ + | *Optional* | :code:`--enable-all` or | MET_HDF | + | | | | + | LIDAR2NC | :code:`--enable-lidar2nc` | | + | | | | + | Support | | | + +-------------------+--------------------------------+------------------------------+ + | *Optional* | :code:`--enable-all` or | MET_HDF, | + | | | | + | MODIS | :code:`--enable-modis` | MET_HDFEOS | + | | | | + | Support | | | + +-------------------+--------------------------------+------------------------------+ + | *Optional* | :code:`--enable-all` or | MET_CAIRO, | + | | | | + | MODE Graphics | :code:`--enable-mode_graphics` | MET_FREETYPE | + | | | | + | Support | | | + +-------------------+--------------------------------+------------------------------+ + + Generally speaking, for each library there is a set of three + environment variables that can + describe the locations: + **$MET_**, **$MET_INC** and **$MET_LIB**. + + The $MET_ environment variable can be used if the external library is + installed such that there is a main directory which has a subdirectory called + *lib* containing the library files and another subdirectory called *include* + containing the include files. + + Alternatively, the $MET_INC and $MET_LIB environment variables are used if the + library and include files for an external library are installed in separate locations. + In this case, both environment variables must be specified and the associated + $MET_ variable will be ignored. + +.. dropdown:: FINAL NOTE ON EXTERNAL LIBRARIES + + For users wishing to run the Plot-MODE-Field tool, the Ghostscript + `font data `_ must be + downloaded and the **MET_FONT_DIR** environment variable in the + **install_met_env.** file should point to the directory containing those fonts. + +Executing the compile_MET_all.sh script +--------------------------------------- + +With the proper files downloaded and the environment configuration file set to the +particular system’s needs, MET is ready for installation. The screenshot below shows the +contents of the installation directory followed by the tar_files subdirectory at +this step on the machine ‘hera’. + +.. image:: figure/installation_dir.png + +Simply enter the following into the terminal to execute the script: + +.. code-block:: ini + + ./compile_MET_all.sh install_met_env. + +The screenshot below shows the contents of the installation directory after installation: + +.. image:: figure/installation_dir_after.png + +To confirm that MET was installed successfully, run the following command from the installation directory to check for errors in the test file: + +.. code-block:: ini - icc -c -DUNDERSCORE `./getdefflags_C.sh` *.c >> make.log - ifort -c `./getdefflags_F.sh` modv*.F moda*.F \ - `ls -1 *.F *.f | grep -v "mod[av]_"` >> make.log - ar crv libbufr.a *.o - -In the directions above, the static library file that is created will be named libbufr.a. MET will check for the library file named libbufr.a, however in some cases (e.g. where the BUFRLIB is already available on a system) the library file may be named differently (e.g. libbufr_v11.3.0_4_64.a). If the library is named anything other than libbufr.a, users will need to tell MET what library to link with by passing the BUFRLIB_NAME option to MET when running configure (e.g. BUFRLIB_NAME=-lbufr_v11.3.0_4_64). - -2. Unidata's NetCDF libraries are used by several tools within MET for writing output NetCDF files. Both `NetCDF-C and NetCDF-CXX `_ are required. The same family of compilers used to build NetCDF should be used when building MET. Users may also find some utilities built for NetCDF such as ncdump and ncview useful for viewing the contents of NetCDF files. Support for NetCDF version 4 requires `HDF5 `_. - -3. The GNU Scientific Library (GSL) is used by MET for random sampling and normal and binomial distribution computations when estimating confidence intervals. Precompiled binary packages are available for most GNU/Linux distributions and may be installed with root access. When installing GSL from a precompiled package on Debian Linux, the developer's version of GSL must be used; otherwise, use the GSL version available from the `GNU GSL website `_. MET requires access to the GSL source headers and library archive file at build time. - -4. For users wishing to compile MET with GRIB2 file support, `NCEP's GRIB2 Library `_ in C (g2clib) must be installed, along with jasperlib, libpng, and zlib. **Please note that compiling the GRIB2C library with the -D__64BIT__ option requires that MET also be configured with CFLAGS="-D__64BIT__". Compiling MET and the GRIB2C library inconsistently may result in a segmentation fault or an "out of memory" error when reading GRIB2 files.** MET looks for the GRIB2C library to be named libgrib2c.a, which may be set in the GRIB2C makefile as LIB=libgrib2c.a. However in some cases, the library file may be named differently (e.g. libg2c_v1.6.0.a). If the library is named anything other than libgrib2c.a, users will need to tell MET what library to link with by passing the GRIB2CLIB_NAME option to MET when running configure (e.g. GRIB2CLIB_NAME=-lg2c_v1.6.0). - -5. Users wishing to compile MODIS-regrid and/or lidar2nc will need to install both the `HDF4 `_ and `HDF-EOS2 `_ libraries available from the HDF group websites linked here. - -6. The MODE-Graphics utility requires `Cairo `_ and `FreeType `_. Thus, users who wish to compile this utility must install both libraries. In addition, users will need to download the `Ghostscript font data `_ required at runtime. - -.. _Installation-of-optional: - -Installation of Optional Utilities -================================== - -As described in the introduction to this section, two additional utilities are strongly recommended for use with MET. - -1. The `Unified Post-Processor `_ is recommended for post-processing the raw WRF model output prior to verifying the data with MET. The Unified Post-Processor may be used on WRF output from both the ARW and NMM cores. - -2. The copygb utility is recommended for re-gridding model and observation datasets in GRIB format to a common verification grid. The copygb utility is distributed as part of the Unified Post-Processor and is available from other sources as well. Please refer to the "Unified Post-processor" utility mentioned above for information on availability and installation. - -.. _met_directory_structure: - -MET Directory Structure -======================= - -The top-level MET directory consists of Makefiles, configuration files, and several subdirectories. The top-level Makefile and configuration files control how the entire toolkit is built. Instructions for using these files to build MET can be found in :numref:`Install_Building-the-MET`. - -When MET has been successfully built and installed, the installation directory contains two subdirectories. The *bin/* directory contains executables for each module of MET as well as several plotting utilities. The *share/met/* directory contains many subdirectories with data required at runtime and a subdirectory of sample R scripts utilities. The *colortables/*, *map/*, and *ps/* subdirectories contain data used in creating PostScript plots for several MET tools. The *poly/* subdirectory contains predefined lat/lon polyline regions for use in selecting regions over which to verify. The polylines defined correspond to verification regions used by NCEP as described in :numref:`Appendix B, Section %s `. The *config/* directory contains default configuration files for the MET tools. The *python/* subdirectory contains Python scripts. The *python/examples* subdirectory contains sample scripts used in Python embedding (:numref:`Appendix F, Section %s `). The *python/pyembed/* subdirectory contains code used in Python embedding (:numref:`Appendix F, Section %s `). The *table_files/* and *tc_data/* subdirectories contain GRIB table definitions and tropical cyclone data, respectively. The *Rscripts/* subdirectory contains a handful of plotting graphic utilities for MET-TC. These are the same Rscripts that reside under the top-level MET *scripts/Rscripts* directory, other than it is the installed location. - -The *data/* directory contains several configuration and static data files used by MET. The *sample_fcst/* and *sample_obs/* subdirectories contain sample data used by the test scripts provided in the *scripts/* directory. - -The *docs/* directory contains the Sphinx documentation for MET. - -The *out/* directory will be populated with sample output from the test cases described in the next section. - -The *src/* directory contains the source code for each of the tools in MET. - -The *scripts/* directory contains test scripts that are run by make test after MET has been successfully built, and a directory of sample configuration files used in those tests located in the *scripts/config/* subdirectory. The output from the test scripts in this directory will be written to the *out/* directory. Users are encouraged to copy sample configuration files to another location and modify them for their own use. - -The *share/met/Rscripts* directory contains a handful of sample R scripts, including plot_tcmpr.R, which provides graphic utilities for MET-TC. For more information on the graphics capabilities, see :numref:`TC-Stat-tool-example` of this User's Guide. - -.. _Install_Building-the-MET: - -Building the MET Package -======================== - -Building the MET package consists of three main steps: (1) install the required libraries, (2) configure the environment variables, and (3) configure and execute the build. Users can follow the instructions below or use a sample installation script. Users can find the script and its instructions under on the `Downloads `_ page of the MET website. - -Get the MET Source Code ------------------------ - -The MET source code is available for download from the public `MET GitHub repository `_. - -- Open a web browser and go to the `latest stable MET release `_. - -- Click on the `Source code` link (either the *zip* or *tar.gz*) under Assets and when prompted, save it to your machine. - -- (Optional) Verify the checksum of the source code download - - - Download the checksum file that corresponds to the source code download link that was used (checksum_zip.txt for the *zip* file and checksum_tar.txt for the *tar.gz* file). Put the checksum file into the same directory as the source code file. - - Run the *sha256sum* command with the --check argument to verify that the source code download file was not corrupted. - -Zip File:: - - sha256sum --check checksum_zip.txt - -Tar File:: - - sha256sum --check checksum_tar.txt - -.. note:: - If the source code is downloaded using **wget**, then the filenames will not - match the filenames listed in the checksum files. If the source code is - downloaded using **curl**, the *-LJO* flags should be added to the command to - preserve the expected filenames found in the checksum files. - -- Uncompress the source code (on Linux/Unix\ *: gunzip* for zip file or *tar xvfz* for the tar.gz file) - -Install the Required Libraries ------------------------------- - -• Please refer to :numref:`Installation-of-required` and :numref:`Installation-of-optional` on how to install the required and optional libraries. - -• If installing the required and optional libraries in a non-standard location, the user may need to tell MET where to find them. This can be done by setting or adding to the LD_LIBRARY PATH to include the path to the library files. - -Set Environment Variables + grep -i error MET12.0.0/met.make_test.log + +If no errors are returned, the installation was successful. +Due to the highly variable nature of hardware systems, users may encounter issues during +the installation process that result in MET not being installed. If this occurs please +first recheck that the location of all the necessary data files and scripts is correct. +Next, recheck the environment variables in the environment configuration file and +ensure there are no spelling errors or improperly set variables. +After these checks are complete, run the script again. + +If there are still errors, users still have options to obtain a successful +MET installation. Check the `FAQ section of the User’s Guide on topics relevant to installation `_. +Next, review previously asked questions on the installation topic in +`GitHub Discussions `_. +Users are welcome to post any questions they might have that have not been asked. +Finally, consider one of the remaining installation methods for MET, +as these may prove more successful. + +Using Docker for Running MET +============================ + +Docker is a system that seeks to eliminate some of the complexities associated with +downloading various software and any library dependencies it might have by allowing +users to run inside a preset container. Instead of using a hard copy of an application, +Docker allows users to pull images of the application and run those within the +Docker environment. This is beneficial to both developers (who no longer have to +design with every possible system environment in mind) and users (who can skip tracking +down system environment settings and meet with success faster) alike. + +MET has numerous version images for Docker users and continues to be released as +images at the same interval as system releases. While the advantages of Docker can +make it an appealing installation route for first time users, it does require +privileged user access that will result in an unsuccessful installation if not +available. Please ensure the user has high system access +(e.g. admin access) before attempting this method. + +Installing Docker +----------------- + +To begin, download and install the correct version of Docker for the +intended system. +`The Docker installation webpage `_ +should detect what +system is accessing the webpage and auto select the appropriate +version. If a different version is required, select the correct +version from the dropdown option. Follow Docker’s instructions +for a successful installation. + + +Loading the Latest Docker Image of MET +-------------------------------------- + +Once the installation of Docker has been confirmed to be successful, +all that’s needed to run MET is to download the latest image of MET +in Docker. To accomplish that, use the pull command, with the latest +MET version number, for example: + +.. code-block:: ini + + docker pull dtcenter/met:12.0.0 + +Omitting the +version number will result in an error due to Docker’s behavior +of attempting to retrieve an image with the “latest” tag, which +MET no longer uses. + + +Running the Docker version of MET +--------------------------------- + +All that is left to do is launch a shell in the Docker container. +This is accomplished with the command: + +.. code-block:: ini + + docker run -it --rm dtcenter/met /bin/bash + +Note that the "--rm" command was added to automatically remove the +container created +from the image once exiting Docker. Simply remove this command if the +container should persist after exiting. If there is an error +during this run command, try adding the latest MET version number +the same way the latest image of MET was pulled: + +.. code-block:: ini + + docker run -it --rm dtcenter/met:12.0.0 /bin/bash + +If the usage MET via Docker images was successful, it is highly +recommended to move on +to using the METplus wrappers of the tools, which have their own +Docker image. +Instructions for obtaining that image are in the +`METplus Wrappers User's Guide `_. + +Using Apptainer for Running MET +=============================== + +Similar to Docker, Apptainer (formerly Singularity) removes some of the +complexities associated with downloading various library dependencies and +runs inside a preset container. Apptainer is incredibly flexible and was +designed to function on High Performance Computing (HPC) systems. It can +utilize Container Library and Docker images, meaning users can benefit +from the Docker images that already exist for MET. + +Perhaps the biggest benefit of using Apptainer (aside from its agnostic +platform availability) is its nonrequirement of root permissions. This can +be one of the only ways users operating on large-scale, shared computing +resources can access MET. That, plus the relatively simple installation of +Apptainer and retrieval of Docker images, should help any users experiencing +difficulties with MET installation using previous methods achieve success. + +Installing Apptainer +-------------------- + +To begin, download and install the correct version of Apptainer for the +intended system. The method of installing from code is outlined in +`Apptainer’s INSTALL.md file `_ +on their GitHub page. If users require an alternate method to +install Apptainer, the +`Admin guide `_ +will provide further details. + +Loading the Latest MET Image +---------------------------- + +Similar to Docker, Apptainer will build the container based off of the +MET image in a single command. To accomplish this, Apptainer’s +“Swiss army knife” :code:`build` +command is used. Use the the latest MET version number in +conjunction with :code:`build` +to make the container: + +.. code-block:: ini + + singularity build met-12.0.0.sif docker://dtcenter/met:12.0.0 + + +Running the MET Container ------------------------- -The MET build uses environment variables to specify the locations of the needed external libraries. For each library, there is a set of three environment variables to describe the locations: $MET_, $MET_INC and $MET_LIB. - -The $MET_ environment variable can be used if the external library is installed such that there is a main directory which has a subdirectory called "lib" containing the library files and another subdirectory called "include" containing the include files. For example, if the NetCDF library files are installed in */opt/netcdf/lib* and the include files are in */opt/netcdf/include*, you can just define the $MET_NETCDF environment variable to be "*/opt/netcdf*". - -The $MET_INC and $MET_LIB environment variables are used if the library and include files for an external library are installed in separate locations. In this case, both environment variables must be specified and the associated $MET_ variable will be ignored. For example, if the NetCDF include files are installed in */opt/include/netcdf* and the library files are in */opt/lib/netcdf*, then you would set $MET_NETCDFINC to "*/opt/include/netcdf*" and $MET_NETCDFLIB to "*/opt/lib/netcdf*". - -The following environment variables should also be set: - -* Set $MET_PROJ to point to the main Proj directory, or set $MET_PROJINC to point to the directory with the Proj include files and set $MET_PROJLIB to point to the directory with the Proj library files. - -* Set $MET_NETCDF to point to the main NetCDF directory, or set $MET_NETCDFINC to point to the directory with the NetCDF include files and set $MET_NETCDFLIB to point to the directory with the NetCDF library files. Note that the files for both NetCDF-C and NetCDF-CXX must be installed in the same include and library directories. - -* Set $MET_HDF5 to point to the main HDF5 directory. - -* Set $MET_BUFR to point to the main BUFR directory, or set $MET_BUFRLIB to point to the directory with the BUFR library files. Because we don't use any BUFR library include files, you don't need to specify $MET_BUFRINC. +The container is now ready for usage! Simply use the :code:`exec` +command to invoke the MET container, along with the appropriate +MET command line usage: -* Set $MET_GSL to point to the main GSL directory, or set $MET_GSLINC to point to the directory with the GSL include files and set $MET_GSLLIB to point to the directory with the GSL library files. +.. code-block:: ini -* If compiling support for GRIB2, set $MET_GRIB2CINC and $MET_GRIB2CLIB to point to the main GRIB2C directory which contains both the include and library files. These are used instead of $MET_GRIB2C since the main GRIB2C directory does not contain include and lib subdirectories. + singularity exec met-12.0.0.sif plot_data_plane /home/data/fcst_006.grb2 image_output.ps ‘name=”TMP”; level=”Z0”;’ -* If compiling support for PYTHON, set $MET_PYTHON_BIN_EXE to specify the desired Python executable to be used. Also set $MET_PYTHON_CC, and $MET_PYTHON_LD to specify the compiler (-I) and linker (-L) flags required for Python. Set $MET_PYTHON_CC for the directory containing the "Python.h" header file. Set $MET_PYTHON_LD for the directory containing the Python library file and indicate the name of that file. For example: - - .. code-block:: none - - MET_PYTHON_BIN_EXE='/usr/bin/python3.6' - MET_PYTHON_CC='-I/usr/include/python3.6' - MET_PYTHON_LD='-L/usr/lib/python3.6/config-x86_64-linux-gnu -lpython3.6m' - - Note that this version of Python must include support for a minimum set of required packages. For more information about Python support in MET, including the list of required packages, please refer to :numref:`Appendix F, Section %s `. - -* If compiling support for unstructured grids, specify the location of the Atlas and ecKit libraries. Set $MET_ATLAS to point to the main Atlas directory, or set $MET_ATLASINC to point to the directory with the Atlas include files and set $MET_ATLASLIB to point to the directory with the Atlas library files. Set $MET_ECKIT to point to the main ecKit directory, or set $MET_ECKITINC to point to the directory with the ecKit include files and set $MET_ECKITLIB to point to the directory with the ecKit library files. - -* If compiling MODIS-Regrid and/or lidar2nc, set $MET_HDF to point to the main HDF4 directory, or set $MET_HDFINC to point to the directory with the HDF4 include files and set $MET_HDFLIB to point to the directory with the HDF4 library files. Also, set $MET_HDFEOS to point to the main HDF EOS directory, or set $MET_HDFEOSINC to point to the directory with the HDF EOS include files and set $MET_HDFEOSLIB to point to the directory with the HDF EOS library files. - -* If compiling MODE Graphics, set $MET_CAIRO to point to the main Cairo directory, or set$MET_CAIROINC to point to the directory with the Cairo include files and set $MET_CAIROLIB to point to the directory with the Cairo library files. Also, set $MET_FREETYPE to point to the main FreeType directory, or set $MET_FREETYPEINC to point to the directory with the FreeType include files and set $MET_FREETYPELIB to point to the directory with the FreeType library files. - -* When running MODE Graphics, set $MET_FONT_DIR to the directory containing font data required at runtime. A link to the tarball containing this font data can be found on the MET website. - -For ease of use, you should define these in your .cshrc, .bashrc, or equivalent file. - -Configure and Execute the Build +Stopping the Apptainer Instance ------------------------------- -Example: To configure MET to install all of the available tools and options in the "bin" subdirectory of your current directory, you would use the following commands: - -.. code-block:: none - - 1. Type './configure --prefix=`pwd` --enable-all' - Note that --enable-all is equivalent to specifying: - --enable-grib2 --enable-python --enable-ugrid --enable-modis --enable-mode_graphics --enable-lidar2nc - 2. Type 'make install >& make_install.log &' - 3. Type 'tail -f make_install.log' to view the execution of the make. - 4. When make is finished, type 'CTRL-C' to quit the tail. - -If all tools are enabled and the build is successful, the "*/bin*" directory (where ** is the prefix you specified on your configure command line) will contain the following executables: - -.. code-block:: none - - - ascii2nc - - ensemble_stat - - gen_ens_prod - - gen_vx_mask - - grid_stat - - gis_dump_dbf - - gis_dump_shp - - gis_dump_shx - - grid_diag - - gsid2mpr - - gsidens2orank - - lidar2nc - - madis2nc - - mode - - mode_analysis - - modis_regrid - - mtd - - pb2nc - - pcp_combine - - plot_data_plane - - plot_mode_field - - plot_point_obs - - point2grid - - point_stat - - rmw_analysis - - regrid_data_plane - - series_analysis - - shift_data_plane - - stat_analysis - - tc_diag - - tc_dland - - tc_gen - - tc_pairs - - tc_rmw - - tc_stat - - wavelet_stat - - wwmca_plot - - wwmca_regrid - -NOTE: Several compilation warnings may occur which are expected. If any errors occur, please refer to :numref:`Appendix A, Section %s ` on troubleshooting for common problems. - -**-help** and **-version** command line options are available for all of the MET tools. Typing the name of the tool with no command line options also produces the usage statement. - -The configure script has command line options to specify where to install MET and which MET utilities to install. Include any of the following options that apply to your system: - -.. code-block:: none - - --prefix=PREFIX - -By default, MET will install all the files in "*/usr/local/bin*". You can specify an installation prefix other than "*/usr/local*" using "--prefix", for instance "--prefix=$HOME" or "--prefix=`pwd`". - -.. code-block:: none - - --enable-all - -Enable the configuration options --enable-grib2, --enable-python, --enable-ugrid, --enable-lidar2nc, --enable-mode_graphics, and --enable-modis, described below. - -.. code-block:: none - - --enable-grib2 - -Enable compilation of GRIB2 support. Requires $MET_GRIB2C. - -.. code-block:: none - - --enable-python - -Enable compilation of Python embedding support. Requires $MET_PYTHON_CC and $MET_PYTHON_LD. - -.. code-block:: none - - --enable-ugrid - -Enable compilation of unstructured grid support. Requires $MET_ATLAS and $MET_ECKIT. - -.. code-block:: none - - --enable-lidar2nc - -Enable compilation of the LIDAR2NC tool. Requires $MET_HDF. - -.. code-block:: none - - --enable-modis - -Enable compilation of the Modis-Regrid tool. Requires $MET_HDF, $MET_HDFEOSINC, and $MET_HDFEOSLIB. - -.. code-block:: none - - --enable-mode_graphics - -Enable compilation of the MODE-Graphics tool. Requires $MET_CAIRO and $MET_FREETYPE. - -.. code-block:: none - - --disable-block4 - -Disable use of BLOCK4 in the compilation. Use this if you have trouble using PrepBUFR files. - -.. code-block:: none - - --disable-openmp - -Disable compilation of OpenMP directives within the code which allows some code -regions to benefit from thread-parallel execution. Runtime environment variable -:code:`OMP_NUM_THREADS` controls the number of threads. - -Run the configure script with the **-help** argument to see the full list of configuration options. - -Make Targets ------------- - -The autoconf utility provides some standard make targets for the users. In MET, the following standard targets have been implemented and tested: - -1. **all** - compile all of the components in the package, but don't install them. - -2. **install** - install the components (where is described below). Will also compile if "make all" hasn't been done yet. - -3. **clean** - remove all of the temporary files created during the compilation. - -4. **uninstall** - remove the installed files. For us, these are the executables and the files in $MET_BASE. - -MET also has the following non-standard targets: - -5. **test** - runs the *scripts/test_all.sh* script. You must run "make install" before using this target. - -.. _Sample Test cases: - -Sample Test Cases -================= +Once work is complete within the instance, the :code:`stop` +command can be used to end the instance. This command will need to +be used otherwise the instance will continue to run in the background: -Once the MET package has been built successfully, the user is encouraged to run the sample test scripts provided. They are run using make test in the top-level directory. Execute the following commands: +.. code-block:: ini -1. Type 'make test >& make_test.log &' to run all of the test scripts in the directory. These test scripts use test data supplied with the tarball. For instructions on running your own data, please refer to the MET User's Guide. + singularity instance stop /path/to/container/met-12.0.0.sif met-12.0.0 -2. Type 'tail -f make_test.log' to view the execution of the test script. +Now that MET is successfully installed, it is highly recommended to +next install the METplus wrappers to take full advantage of +`Python integration `_. +Users can also proceed to the +`Tutorial `_ +and run through the examples that only utilize the MET processes +(METplus wrapper applications and commands will not work unless +METplus wrappers are also installed). -3. When the test script is finished, type 'CTRL-C' to quit the tail. Look in "out" to find the output files for these tests. Each tool has a separate, appropriately named subdirectory for its output files. -4. In particular, check that the PB2NC tool ran without error. If there was an error, run "make clean" then rerun your configure command adding **--disable-block4** to your configure command line and rebuild MET. diff --git a/docs/Users_Guide/mode.rst b/docs/Users_Guide/mode.rst index 5a6503f0f5..491b452002 100644 --- a/docs/Users_Guide/mode.rst +++ b/docs/Users_Guide/mode.rst @@ -118,6 +118,23 @@ The **multivar_intensity_compare_fcst** and **multivar_intensity_compare_obs** c When regridding to the FCST or OBS field (e.g. to_grid = FCST), the first field of the field array is used from the forecast and observation field dictionaries, respectively. All regridding is then done to that grid. Other regrid options described in :ref:`regrid` can also be used as normal. +"file_type" can be set independently for each input in multivariate mode. If not set for an input, MET uses file names and file content to determine the type. + +When setting a threshold to a percentile, some choices require both an observation input and a forecast input. When this is the case, it's assumed the indices match, so for example if forecast input 1 has such a percentile setting, then observation input 1 will be used to compute the percentile. Percentiles in which this will happen are: + +* SFP in an observation input. + * The matching forecast input will be used to determine the threshold. e.g. ">SFP33.3" in the 2nd observation input means greater than 33.3-rd percentile of the 2nd forecast input will be used as the threshold for that observation input. + +* SOP in a forecast input. + * The matching observation input will be used to determine the threshold. e.g. ">SOP33.3" in the 2nd forecast input means greater than 33.3-rd percentile of the 2nd observation input will be used as the threshold for that forecast input. + +* "==FBIAS" in an observation input. + * e.g. "==FBIAS1" in an observation input to automatically de-bias the data, using a simple threshold in the matching forecast input. For example, when observation input 3 has "==FBIAS1", and forecast input 3 has ">5.0", MET applies the >5.0 threshold to the forecast and then chooses an observation threshold which results in a frequency bias of 1. The frequency bias can be any float value > 0.0. + +* "==FBIAS" in a forecast input. + * e.g. "==FBIAS1" in a forecast input to automatically de-bias the data, using a simple threshold in the matching observation input. For example, when forecast input 2 has "==FBIAS1", and observation input 2 has ">5.0", MET applies the >5.0 threshold to the observation and then chooses a forecast threshold which results in a frequency bias of 1. The frequency bias can be any float value > 0.0. + + Practical Information ===================== @@ -260,7 +277,7 @@ _____________________ multivar_name = "Super"; -The **multivar_name** entry appears only in the **MODEMultivarConfig_default** file. This option is used only when the multivar option is enabled, and only when all **multivar_intensity_flag** values are FALSE. It can be thought of as an identifier for the multivariate super object. It shows up in output files names and content. It can be set separately for forecasts and observations or as a common value for both. +The **multivar_name** entry appears only in the **MODEMultivarConfig_default** file. This option is used only when the multivar option is enabled, and only when **multivar_intensity_compare_fcst** and **multivar_intensity_compare_obs** are empty. It can be thought of as an identifier for the multivariate super object. It shows up in output files names and content. It can be set separately for forecasts and observations or as a common value for both. _____________________ @@ -268,7 +285,7 @@ _____________________ multivar_level = "LO"; -The **multivar_level** entry appears only in the **MODEMultivarConfig_default** file. This option is used only when the multivar option is enabled, and only when all **multivar_intensity_flag** values are FALSE. It is the identifier for the multivariate super object as regards level. It shows up in output files names and content. If not set the default value is "NA". It can be set separately for forecasts and observations, or as a common value for both. +The **multivar_level** entry appears only in the **MODEMultivarConfig_default** file. This option is used only when the multivar option is enabled, and only when **multivar_intensity_compare_fcst** and **multivar_intensity_compare_obs** are empty. It is the identifier for the multivariate super object as regards level. It shows up in output files names and content. If not set the default value is "NA". It can be set separately for forecasts and observations, or as a common value for both. _____________________ diff --git a/docs/Users_Guide/reformat_grid.rst b/docs/Users_Guide/reformat_grid.rst index 3ef6897c54..afee78a7c6 100644 --- a/docs/Users_Guide/reformat_grid.rst +++ b/docs/Users_Guide/reformat_grid.rst @@ -215,7 +215,7 @@ Each NetCDF file generated by the Pcp-Combine tool contains the dimensions and v Regrid-Data-Plane Tool ====================== -This section contains a description of running the Regrid-Data-Plane tool. This tool may be run to read data from any gridded file MET supports, interpolate to a user-specified grid, and writes the field(s) out in NetCDF format. The user may specify the method of interpolation used for regridding as well as which fields to regrid. This tool is particularly useful when dealing with GRIB2 and NetCDF input files that need to be regridded. For GRIB1 files, it has also been tested for compatibility with the copygb regridding utility mentioned in :numref:`Installation-of-optional`. +This section contains a description of running the Regrid-Data-Plane tool. This tool may be run to read data from any gridded file MET supports, interpolate to a user-specified grid, and writes the field(s) out in NetCDF format. The user may specify the method of interpolation used for regridding as well as which fields to regrid. This tool is particularly useful when dealing with GRIB2 and NetCDF input files that need to be regridded. For GRIB1 files, it has also been tested for compatibility with the copygb regridding utility mentioned in :numref:`suggested_external_utiliites`. regrid_data_plane Usage ----------------------- diff --git a/docs/Users_Guide/reformat_point.rst b/docs/Users_Guide/reformat_point.rst index 31d752a6f3..32695f36b0 100644 --- a/docs/Users_Guide/reformat_point.rst +++ b/docs/Users_Guide/reformat_point.rst @@ -94,7 +94,7 @@ In this example, the PB2NC tool will process the input **sample_pb.blk** file ap pb2nc Configuration File ------------------------ -The default configuration file for the PB2NC tool named **PB2NCConfig_default** can be found in the installed *share/met/config* directory. The version used for the example run in :numref:`Sample test cases` is available in *scripts/config*. It is recommended that users make a copy of configuration files prior to modifying their contents. +The default configuration file for the PB2NC tool named **PB2NCConfig_default** can be found in the installed *share/met/config* directory. The version used for the installation test cases is available in *scripts/config*. It is recommended that users make a copy of configuration files prior to modifying their contents. Note that environment variables may be used when editing configuration files, as described in the :numref:`config_env_vars`. diff --git a/docs/Users_Guide/tc-dland.rst b/docs/Users_Guide/tc-dland.rst index aa631ed7de..1138c465ad 100644 --- a/docs/Users_Guide/tc-dland.rst +++ b/docs/Users_Guide/tc-dland.rst @@ -16,7 +16,7 @@ Input/Output Format The input for the TC-dland tool is a file containing the longitude (degrees east) and latitude (degrees north) of all the coastlines and islands considered to be a significant landmass. The default input is to use all three land data files (**aland.dat, shland.dat, wland.dat**) found in the installed *share/met/tc_data/* directory. The use of all three files produces a global land data file. The **aland.dat** file contains the longitude and latitude distinctions used by NHC for the Atlantic and eastern North Pacific basins, the **shland.dat** contains longitude and latitude distinctions for the Southern Hemisphere (south Pacific and South Indian Ocean), and the **wland.dat** contains the remainder of the Northern Hemisphere (western North Pacific and North Indian Ocean). Users may supply their own input file in order to refine the definition of coastlines and a significant landmass. -The output file from TC-dland is a NetCDF format file containing a gridded field representing the distance to the nearest coastline or island, as specified in the input file. This file is used in the TC-Pairs tool to compute the distance from land for each track point in the adeck and bdeck. As noted in :numref:`met_directory_structure`, precomputed distance to land (NetCDF output from TC-dland) files are available in the release. In the installed *share/met/tc_data* directory: +The output file from TC-dland is a NetCDF format file containing a gridded field representing the distance to the nearest coastline or island, as specified in the input file. This file is used in the TC-Pairs tool to compute the distance from land for each track point in the adeck and bdeck. The precomputed distance to land (NetCDF output from TC-dland) files are available in the release. In the installed *share/met/tc_data* directory: **dland_nw_hem_tenth_degree.nc:** TC-dland output from **aland.dat** using a 1/10th degree grid diff --git a/internal/scripts/installation/config/install_met_env.generic b/internal/scripts/installation/config/install_met_env.generic_gnu similarity index 97% rename from internal/scripts/installation/config/install_met_env.generic rename to internal/scripts/installation/config/install_met_env.generic_gnu index b001f069c0..6fd870d742 100644 --- a/internal/scripts/installation/config/install_met_env.generic +++ b/internal/scripts/installation/config/install_met_env.generic_gnu @@ -47,6 +47,8 @@ export MAKE_ARGS="-j 5" # need to update the paths to the appropriate location. #export EXTERNAL_LIBS=${TEST_BASE}/external_libs #export MET_PROJ=${EXTERNAL_LIBS} +#export TIFF_INCLUDE_DIR=${EXTERNAL_LIBS}/include +#export TIFF_LIB_DIR=${EXTERNAL_LIBS}/lib #export SQLITE_INCLUDE_DIR=${EXTERNAL_LIBS}/include #export SQLITE_LIB_DIR=${EXTERNAL_LIBS}/lib #export MET_GSL=${EXTERNAL_LIBS} diff --git a/internal/scripts/installation/config/install_met_env.generic_intel_non-oneapi b/internal/scripts/installation/config/install_met_env.generic_intel_non-oneapi new file mode 100644 index 0000000000..ddffe2f043 --- /dev/null +++ b/internal/scripts/installation/config/install_met_env.generic_intel_non-oneapi @@ -0,0 +1,86 @@ +# Find the directory this script is called from +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +# Required +# Directory that is the root of the compile +export TEST_BASE=${DIR} + +# Required +# Format is compiler_version (e.g. gnu_8.3.0) +# Compiler options = gnu, intel, ics, ips, PrgEnv-intel, or pgi +# Version is used for gnu in compilation of BUFRLIB and HDF5 +export COMPILER=intel_2022.1.2 + +# Set the values for the compilers +export FC=ifort +export F77=ifort +export F90=ifort +export CC=icc +export CXX=icpc + +# Required +# Root directory for creating/untaring met source code - usually same as TEST_BASE +export MET_SUBDIR=${TEST_BASE} + +# Required +# The name of the met tarbal usually downloaded with version from dtcenter.org and includes a version +# example - v11.1.0.tar.gz +export MET_TARBALL=v12.0.0.tar.gz + +# Required +# Specify if machine useds modules for loading software +export USE_MODULES=FALSE + +# Root directory of your python install, containing the bin, include, lib, and share directories +export MET_PYTHON=`python3-config --prefix` + +# Python ldflags created using python3-config +export MET_PYTHON_LD=`python3-config --ldflags --embed` + +# Python cflags created using python3-config +export MET_PYTHON_CC=`python3-config --cflags` + +# Use MAKE_ARGS to sped up the compilation of the external libaries and/or MET +# MAKE_ARGS can be set "-j #" where # is replaced with the number of +# cores to use (as an integer) or to simply "-j" to use all available cores. +# Recommend setting to "-j 5" as some users have experienced problems with +# higher values or no # specified. +export MAKE_ARGS="-j 5" + +# If users have already installed these libraries and would like to make use of +# them, uncomment out the export statements. If those pre-existing libraries are +# in the external_libs directory, no further edits are needed; however, users +# that have the pre-existing libraries not in the external_libs directory will +# need to update the paths to the appropriate location. +#export EXTERNAL_LIBS=${TEST_BASE}/external_libs +#export MET_PROJ=${EXTERNAL_LIBS} +#export TIFF_INCLUDE_DIR=${EXTERNAL_LIBS}/include +#export TIFF_LIB_DIR=${EXTERNAL_LIBS}/lib +#export SQLITE_INCLUDE_DIR=${EXTERNAL_LIBS}/include +#export SQLITE_LIB_DIR=${EXTERNAL_LIBS}/lib +#export MET_GSL=${EXTERNAL_LIBS} +#export MET_BUFRLIB=${EXTERNAL_LIBS}/lib +#export BUFRLIB_NAME=-lbufr_4 +#export LIB_JASPER=${EXTERNAL_LIBS}/lib +#export LIB_LIBPNG=${EXTERNAL_LIBS}/lib +#export LIB_Z=${EXTERNAL_LIBS}/lib +#export MET_GRIB2CLIB=${EXTERNAL_LIBS}/lib +#export MET_GRIB2CINC=${EXTERNAL_LIBS}/include +#export GRIB2CLIB_NAME=-lg2c +#export MET_HDF5=${EXTERNAL_LIBS} +#export MET_NETCDF=${EXTERNAL_LIBS} +#export MET_ECKIT==${EXTERNAL_LIBS} +#export MET_ATLAS==${EXTERNAL_LIBS} + +# The optional libraries ecKit and atlas offer support for unstructured +# grids. The optional libraries HDF4, HDFEOS, FREETYPE, and CAIRO are +# used for the following, not widely used tools, MODIS-Regrid, +# lidar2nc, and MODE Graphics. To enable building of these libraries, +# set the compile flags for the library (e.g. COMPILE_ECKIT, COMPILE_ATLAS, +# COMPILE_HDF, COMPILE_HDFEOS) to any value in the environment config +# file. If these libraries have already been installed and don't need +# to be reinstalled, please supply values for the following environment +# variables in the input environment configuration file +# (install_met_env.): MET_ECKIT, MET_ATLAS, MET_HDF, +# MET_HDFEOS, MET_FREETYPEINC, MET_FREETYPELIB, MET_CAIROINC, +# MET_CAIROLIB. diff --git a/internal/scripts/installation/config/install_met_env.generic_intel_oneapi b/internal/scripts/installation/config/install_met_env.generic_intel_oneapi new file mode 100644 index 0000000000..bba43e5bde --- /dev/null +++ b/internal/scripts/installation/config/install_met_env.generic_intel_oneapi @@ -0,0 +1,86 @@ +# Find the directory this script is called from +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +# Required +# Directory that is the root of the compile +export TEST_BASE=${DIR} + +# Required +# Format is compiler_version (e.g. gnu_8.3.0) +# Compiler options = gnu, intel, ics, ips, PrgEnv-intel, or pgi +# Version is used for gnu in compilation of BUFRLIB and HDF5 +export COMPILER=intel-oneapi_2023.0.0 + +# Set the values for the compilers +export FC=ifx +export F77=ifx +export F90=ifx +export CC=icx +export CXX=icpx + +# Required +# Root directory for creating/untaring met source code - usually same as TEST_BASE +export MET_SUBDIR=${TEST_BASE} + +# Required +# The name of the met tarbal usually downloaded with version from dtcenter.org and includes a version +# example - v12.0.0.tar.gz +export MET_TARBALL=v12.0.0.tar.gz + +# Required +# Specify if machine useds modules for loading software +export USE_MODULES=FALSE + +# Root directory of your python install, containing the bin, include, lib, and share directories +export MET_PYTHON=`python3-config --prefix` + +# Python ldflags created using python3-config +export MET_PYTHON_LD=`python3-config --ldflags --embed` + +# Python cflags created using python3-config +export MET_PYTHON_CC=`python3-config --cflags` + +# Use MAKE_ARGS to sped up the compilation of the external libaries and/or MET +# MAKE_ARGS can be set "-j #" where # is replaced with the number of +# cores to use (as an integer) or to simply "-j" to use all available cores. +# Recommend setting to "-j 5" as some users have experienced problems with +# higher values or no # specified. +export MAKE_ARGS="-j 5" + +# If users have already installed these libraries and would like to make use of +# them, uncomment out the export statements. If those pre-existing libraries are +# in the external_libs directory, no further edits are needed; however, users +# that have the pre-existing libraries not in the external_libs directory will +# need to update the paths to the appropriate location. +#export EXTERNAL_LIBS=${TEST_BASE}/external_libs +#export MET_PROJ=${EXTERNAL_LIBS} +#export TIFF_INCLUDE_DIR=${EXTERNAL_LIBS}/include +#export TIFF_LIB_DIR=${EXTERNAL_LIBS}/lib +#export SQLITE_INCLUDE_DIR=${EXTERNAL_LIBS}/include +#export SQLITE_LIB_DIR=${EXTERNAL_LIBS}/lib +#export MET_GSL=${EXTERNAL_LIBS} +#export MET_BUFRLIB=${EXTERNAL_LIBS}/lib +#export BUFRLIB_NAME=-lbufr_4 +#export LIB_JASPER=${EXTERNAL_LIBS}/lib +#export LIB_LIBPNG=${EXTERNAL_LIBS}/lib +#export LIB_Z=${EXTERNAL_LIBS}/lib +#export MET_GRIB2CLIB=${EXTERNAL_LIBS}/lib +#export MET_GRIB2CINC=${EXTERNAL_LIBS}/include +#export GRIB2CLIB_NAME=-lg2c +#export MET_HDF5=${EXTERNAL_LIBS} +#export MET_NETCDF=${EXTERNAL_LIBS} +#export MET_ECKIT==${EXTERNAL_LIBS} +#export MET_ATLAS==${EXTERNAL_LIBS} + +# The optional libraries ecKit and atlas offer support for unstructured +# grids. The optional libraries HDF4, HDFEOS, FREETYPE, and CAIRO are +# used for the following, not widely used tools, MODIS-Regrid, +# lidar2nc, and MODE Graphics. To enable building of these libraries, +# set the compile flags for the library (e.g. COMPILE_ECKIT, COMPILE_ATLAS, +# COMPILE_HDF, COMPILE_HDFEOS) to any value in the environment config +# file. If these libraries have already been installed and don't need +# to be reinstalled, please supply values for the following environment +# variables in the input environment configuration file +# (install_met_env.): MET_ECKIT, MET_ATLAS, MET_HDF, +# MET_HDFEOS, MET_FREETYPEINC, MET_FREETYPELIB, MET_CAIROINC, +# MET_CAIROLIB. diff --git a/internal/test_unit/config/MODEConfig_multivar b/internal/test_unit/config/MODEConfig_multivar new file mode 100644 index 0000000000..d3fba7556d --- /dev/null +++ b/internal/test_unit/config/MODEConfig_multivar @@ -0,0 +1,274 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// MODE configuration file. +// +// For additional information, see the MET_BASE/config/README file. +// +//////////////////////////////////////////////////////////////////////////////// + +// +// Output model name to be written +// +// model = +model = "HRRR"; + +// +// Output description to be written +// +// desc = + +// +// Output observation type to be written +// +// obtype = +obtype = "ANALYSIS"; + +//////////////////////////////////////////////////////////////////////////////// + +// +// Verification grid +// +regrid = {to_grid = FCST;method = NEAREST;width = 1;vld_thresh = 0.5;} + +//////////////////////////////////////////////////////////////////////////////// + +// +`// Approximate grid resolution (km) +// +// grid_res = +// + +//////////////////////////////////////////////////////////////////////////////// + +// +// Run all permutations of radius and threshold +// +// quilt = +// ${METPLUS_QUILT} + +// +// MODE Multivar boolean combination logic +// +//multivar_logic = +//${METPLUS_MULTIVAR_LOGIC} +multivar_logic = "#1 && #2 && #3"; + +// keep this around to compare to older versions, the new version doesn't read this +multivar_intensity_flag = [FALSE, TRUE, TRUE]; + +multivar_intensity_compare_fcst = [ 2, 3 ]; +multivar_intensity_compare_obs = [ 2, 3 ]; + +// +// Forecast and observation fields to be verified +// +fcst = { + //${METPLUS_FCST_FIELD} + field = [{ name="CSNOW"; level="L0"; conv_radius = 0; conv_thresh = ==1; file_type = GRIB2; merge_flag = NONE; }, + { name="VIS"; level="L0"; conv_radius = 5; conv_thresh = <=804.672; merge_thresh = <=1207.008; merge_flag = THRESH; }, + { name="WIND"; level="Z10"; conv_radius = 5; conv_thresh = >=8.9408; merge_thresh = >=6.7056; merge_flag = THRESH; }]; + + //${METPLUS_FCST_CENSOR_THRESH} + //${METPLUS_FCST_CENSOR_VAL} + //${METPLUS_FCST_CONV_RADIUS} + //${METPLUS_FCST_CONV_THRESH} + //${METPLUS_FCST_VLD_THRESH} + //${METPLUS_FCST_FILTER_ATTR_NAME} + filter_attr_name = ["AREA"]; + + //${METPLUS_FCST_FILTER_ATTR_THRESH} + filter_attr_thresh = [>=25]; + + //${METPLUS_FCST_MERGE_THRESH} + //${METPLUS_FCST_MERGE_FLAG} + //${METPLUS_FCST_FILE_TYPE} + //file_type = GRIB2; + multivar_name = "Snow"; + multivar_level = "LO"; +} + +obs = { + //${METPLUS_OBS_FIELD} + field = [{ name="PrecipFlag"; level="L0"; conv_radius = 0; conv_thresh = ==3; file_type = GRIB2; merge_flag = NONE; }, + { name="VIS"; level="L0"; conv_radius = 5; conv_thresh = <=804.672; merge_thresh = <=1207.008; merge_flag = THRESH; }, + { name="WIND"; level="Z10"; conv_radius = 5; conv_thresh = >=8.9408; merge_thresh = >=6.7056; merge_flag = THRESH; } ]; + + //${METPLUS_OBS_CENSOR_THRESH} + //${METPLUS_OBS_CENSOR_VAL} + //${METPLUS_OBS_CONV_RADIUS} + //${METPLUS_OBS_CONV_THRESH} + //${METPLUS_OBS_VLD_THRESH} + + //${METPLUS_OBS_FILTER_ATTR_NAME} + filter_attr_name = ["AREA"]; + + //${METPLUS_OBS_FILTER_ATTR_THRESH} + filter_attr_thresh = [>=25]; + + // ${METPLUS_OBS_MERGE_THRESH} + // ${METPLUS_OBS_MERGE_FLAG} + // ${METPLUS_OBS_FILE_TYPE} + multivar_name = "Precip"; + multivar_level = "LO"; +} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Handle missing data +// +// mask_missing_flag = +// ${METPLUS_MASK_MISSING_FLAG} + +// +// Match objects between the forecast and observation fields +// +//match_flag = +//${METPLUS_MATCH_FLAG} +match_flag = MERGE_BOTH; +// +// Maximum centroid distance for objects to be compared +// +//max_centroid_dist = +//${METPLUS_MAX_CENTROID_DIST} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Verification masking regions +// +//mask = { +//${METPLUS_MASK_DICT} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Fuzzy engine weights +// +//weight = { +//${METPLUS_WEIGHT_DICT} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Fuzzy engine interest functions +// +interest_function = { + + //${METPLUS_INTEREST_FUNCTION_CENTROID_DIST} + + //${METPLUS_INTEREST_FUNCTION_BOUNDARY_DIST} + + //${METPLUS_INTEREST_FUNCTION_CONVEX_HULL_DIST} + + angle_diff = ( + ( 0.0, 1.0 ) + ( 30.0, 1.0 ) + ( 90.0, 0.0 ) + ); + + aspect_diff = ( + ( 0.00, 1.0 ) + ( 0.10, 1.0 ) + ( 0.75, 0.0 ) + ); + + corner = 0.8; + ratio_if = ( + ( 0.0, 0.0 ) + ( corner, 1.0 ) + ( 1.0, 1.0 ) + ); + + area_ratio = ratio_if; + + int_area_ratio = ( + ( 0.00, 0.00 ) + ( 0.10, 0.50 ) + ( 0.25, 1.00 ) + ( 1.00, 1.00 ) + ); + + curvature_ratio = ratio_if; + + complexity_ratio = ratio_if; + + inten_perc_ratio = ratio_if; +} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Total interest threshold for determining matches +// +//total_interest_thresh = +//${METPLUS_TOTAL_INTEREST_THRESH} + +// +// Interest threshold for printing output pair information +// +print_interest_thresh = 0.0; + +//////////////////////////////////////////////////////////////////////////////// + +// +// Plotting information +// +met_data_dir = "MET_BASE"; + +fcst_raw_plot = { + color_table = "MET_BASE/colortables/met_default.ctable"; + plot_min = 0.0; + plot_max = 0.0; +} + +obs_raw_plot = { + color_table = "MET_BASE/colortables/met_default.ctable"; + plot_min = 0.0; + plot_max = 0.0; +} + +object_plot = { + color_table = "MET_BASE/colortables/mode_obj.ctable"; +} + +// +// Boolean for plotting on the region of valid data within the domain +// +plot_valid_flag = FALSE; + +// +// Plot polyline edges using great circle arcs instead of straight lines +// +plot_gcarc_flag = FALSE; + +//////////////////////////////////////////////////////////////////////////////// + +// +// NetCDF matched pairs, PostScript, and contingency table output files +// +//ps_plot_flag = +//${METPLUS_PS_PLOT_FLAG} + +//nc_pairs_flag = { +//${METPLUS_NC_PAIRS_FLAG_DICT} + +//ct_stats_flag = +//${METPLUS_CT_STATS_FLAG} + + +//////////////////////////////////////////////////////////////////////////////// + +shift_right = 0; // grid squares + +//////////////////////////////////////////////////////////////////////////////// + +//${METPLUS_OUTPUT_PREFIX} +//version = "V10.0"; + +//tmp_dir = "${MET_TMP_DIR}"; +tmp_dir = "/tmp"; + +//////////////////////////////////////////////////////////////////////////////// + +//${METPLUS_MET_CONFIG_OVERRIDES} diff --git a/internal/test_unit/config/MODEConfig_multivar_3_2 b/internal/test_unit/config/MODEConfig_multivar_3_2 new file mode 100644 index 0000000000..716b93c9be --- /dev/null +++ b/internal/test_unit/config/MODEConfig_multivar_3_2 @@ -0,0 +1,275 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Mode configuration file. +// +// For additional information, see the MET_BASE/config/README file. +// +//////////////////////////////////////////////////////////////////////////////// + +// +// Output model name to be written +// +// model = +model = "HRRR"; + +// +// Output description to be written +// +// desc = + +// +// Output observation type to be written +// +// obtype = +obtype = "ANALYSIS"; + +//////////////////////////////////////////////////////////////////////////////// + +// +// Verification grid +// +// regrid = { +regrid = {to_grid = FCST;method = NEAREST;width = 1;vld_thresh = 0.5;} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Approximate grid resolution (km) +// +// grid_res = +// + +//////////////////////////////////////////////////////////////////////////////// + +// +// Run all permutations of radius and threshold +// +// quilt = +// ${METPLUS_QUILT} + +// +// MODE Multivar boolean combination logic +// +//multivar_logic = +//${METPLUS_MULTIVAR_LOGIC} +multivar_logic = "#1 && #2 && #3"; + +multivar_intensity_compare_fcst = [ 2, 2 ]; +multivar_intensity_compare_obs = [ 1, 2 ]; + +// +// Forecast and observation fields to be verified +// +fcst = { + //${METPLUS_FCST_FIELD} + field = [{ name="CSNOW"; level="L0"; conv_radius = 0; conv_thresh = ==1; file_type = GRIB2; merge_flag = NONE; }, + { name="VIS"; level="L0"; conv_radius = 5; conv_thresh = <=804.672; merge_thresh = <=1207.008; merge_flag = THRESH; }, + { name="WIND"; level="Z10"; conv_radius = 5; conv_thresh = >=8.9408; merge_thresh = >=6.7056; merge_flag = THRESH; } ]; + + multivar_logic = "#1 && #2 && #3"; + + //${METPLUS_FCST_CENSOR_THRESH} + //${METPLUS_FCST_CENSOR_VAL} + //${METPLUS_FCST_CONV_RADIUS} + //${METPLUS_FCST_CONV_THRESH} + //${METPLUS_FCST_VLD_THRESH} + //${METPLUS_FCST_FILTER_ATTR_NAME} + filter_attr_name = ["AREA"]; + + //${METPLUS_FCST_FILTER_ATTR_THRESH} + filter_attr_thresh = [>=25]; + + //${METPLUS_FCST_MERGE_THRESH} + //${METPLUS_FCST_MERGE_FLAG} + //${METPLUS_FCST_FILE_TYPE} + //file_type = GRIB2; + multivar_name = "Snow"; + multivar_level = "LO"; +} + +obs = { + //${METPLUS_OBS_FIELD} + field = [{ name="VIS"; level="L0"; conv_radius = 5; conv_thresh = <=804.672; merge_thresh = <=1207.008; merge_flag = THRESH; }, + { name="WIND"; level="Z10"; conv_radius = 5; conv_thresh = >=8.9408; merge_thresh = >=6.7056; merge_flag = THRESH; } ]; + + multivar_logic = "#1 && #2"; + + //${METPLUS_OBS_CENSOR_THRESH} + //${METPLUS_OBS_CENSOR_VAL} + //${METPLUS_OBS_CONV_RADIUS} + //${METPLUS_OBS_CONV_THRESH} + //${METPLUS_OBS_VLD_THRESH} + + //${METPLUS_OBS_FILTER_ATTR_NAME} + filter_attr_name = ["AREA"]; + + //${METPLUS_OBS_FILTER_ATTR_THRESH} + filter_attr_thresh = [>=25]; + + // ${METPLUS_OBS_MERGE_THRESH} + // ${METPLUS_OBS_MERGE_FLAG} + // ${METPLUS_OBS_FILE_TYPE} + multivar_name = "Precip"; + multivar_level = "LO"; +} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Handle missing data +// +// mask_missing_flag = +// ${METPLUS_MASK_MISSING_FLAG} + +// +// Match objects between the forecast and observation fields +// +//match_flag = +//${METPLUS_MATCH_FLAG} +match_flag = MERGE_BOTH; +// +// Maximum centroid distance for objects to be compared +// +//max_centroid_dist = +//${METPLUS_MAX_CENTROID_DIST} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Verification masking regions +// +//mask = { +//${METPLUS_MASK_DICT} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Fuzzy engine weights +// +//weight = { +//${METPLUS_WEIGHT_DICT} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Fuzzy engine interest functions +// +interest_function = { + + //${METPLUS_INTEREST_FUNCTION_CENTROID_DIST} + + //${METPLUS_INTEREST_FUNCTION_BOUNDARY_DIST} + + //${METPLUS_INTEREST_FUNCTION_CONVEX_HULL_DIST} + + angle_diff = ( + ( 0.0, 1.0 ) + ( 30.0, 1.0 ) + ( 90.0, 0.0 ) + ); + + aspect_diff = ( + ( 0.00, 1.0 ) + ( 0.10, 1.0 ) + ( 0.75, 0.0 ) + ); + + corner = 0.8; + ratio_if = ( + ( 0.0, 0.0 ) + ( corner, 1.0 ) + ( 1.0, 1.0 ) + ); + + area_ratio = ratio_if; + + int_area_ratio = ( + ( 0.00, 0.00 ) + ( 0.10, 0.50 ) + ( 0.25, 1.00 ) + ( 1.00, 1.00 ) + ); + + curvature_ratio = ratio_if; + + complexity_ratio = ratio_if; + + inten_perc_ratio = ratio_if; +} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Total interest threshold for determining matches +// +//total_interest_thresh = +//${METPLUS_TOTAL_INTEREST_THRESH} + +// +// Interest threshold for printing output pair information +// +print_interest_thresh = 0.0; + +//////////////////////////////////////////////////////////////////////////////// + +// +// Plotting information +// +met_data_dir = "MET_BASE"; + +fcst_raw_plot = { + color_table = "MET_BASE/colortables/met_default.ctable"; + plot_min = 0.0; + plot_max = 0.0; +} + +obs_raw_plot = { + color_table = "MET_BASE/colortables/met_default.ctable"; + plot_min = 0.0; + plot_max = 0.0; +} + +object_plot = { + color_table = "MET_BASE/colortables/mode_obj.ctable"; +} + +// +// Boolean for plotting on the region of valid data within the domain +// +plot_valid_flag = FALSE; + +// +// Plot polyline edges using great circle arcs instead of straight lines +// +plot_gcarc_flag = FALSE; + +//////////////////////////////////////////////////////////////////////////////// + +// +// NetCDF matched pairs, PostScript, and contingency table output files +// +//ps_plot_flag = +//${METPLUS_PS_PLOT_FLAG} + +//nc_pairs_flag = { +//${METPLUS_NC_PAIRS_FLAG_DICT} + +//ct_stats_flag = +//${METPLUS_CT_STATS_FLAG} + + +//////////////////////////////////////////////////////////////////////////////// + +shift_right = 0; // grid squares + +//////////////////////////////////////////////////////////////////////////////// + +//${METPLUS_OUTPUT_PREFIX} +//version = "V10.0"; + +//tmp_dir = "${MET_TMP_DIR}"; +tmp_dir = "/tmp"; + +//////////////////////////////////////////////////////////////////////////////// + +//${METPLUS_MET_CONFIG_OVERRIDES} diff --git a/internal/test_unit/config/MODEConfig_multivar_super b/internal/test_unit/config/MODEConfig_multivar_super new file mode 100644 index 0000000000..e035f6ba63 --- /dev/null +++ b/internal/test_unit/config/MODEConfig_multivar_super @@ -0,0 +1,271 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// MODE configuration file. +// +// For additional information, see the MET_BASE/config/README file. +// +//////////////////////////////////////////////////////////////////////////////// + +// +// Output model name to be written +// +// model = +model = "HRRR"; + +// +// Output description to be written +// +// desc = + +// +// Output observation type to be written +// +// obtype = +obtype = "ANALYSIS"; + +//////////////////////////////////////////////////////////////////////////////// + +// +// Verification grid +// +regrid = {to_grid = FCST;method = NEAREST;width = 1;vld_thresh = 0.5;} + +//////////////////////////////////////////////////////////////////////////////// + +// +`// Approximate grid resolution (km) +// +// grid_res = +// + +//////////////////////////////////////////////////////////////////////////////// + +// +// Run all permutations of radius and threshold +// +// quilt = +// ${METPLUS_QUILT} + +// +// MODE Multivar boolean combination logic +// +//multivar_logic = +//${METPLUS_MULTIVAR_LOGIC} +multivar_logic = "#1 && #2 && #3"; + +multivar_intensity_compare_fcst = [ ]; +multivar_intensity_compare_obs = [ ]; + +// +// Forecast and observation fields to be verified +// +fcst = { + //${METPLUS_FCST_FIELD} + field = [{ name="CSNOW"; level="L0"; conv_radius = 0; conv_thresh = ==1; file_type = GRIB2; merge_flag = NONE; }, + { name="VIS"; level="L0"; conv_radius = 5; conv_thresh = <=804.672; merge_thresh = <=1207.008; merge_flag = THRESH; }, + { name="WIND"; level="Z10"; conv_radius = 5; conv_thresh = >=8.9408; merge_thresh = >=6.7056; merge_flag = THRESH; } ]; + + //${METPLUS_FCST_CENSOR_THRESH} + //${METPLUS_FCST_CENSOR_VAL} + //${METPLUS_FCST_CONV_RADIUS} + //${METPLUS_FCST_CONV_THRESH} + //${METPLUS_FCST_VLD_THRESH} + //${METPLUS_FCST_FILTER_ATTR_NAME} + filter_attr_name = ["AREA"]; + + //${METPLUS_FCST_FILTER_ATTR_THRESH} + filter_attr_thresh = [>=25]; + + //${METPLUS_FCST_MERGE_THRESH} + //${METPLUS_FCST_MERGE_FLAG} + //${METPLUS_FCST_FILE_TYPE} + //file_type = GRIB2; + multivar_name = "Snow"; + multivar_level = "LO"; +} + +obs = { + //${METPLUS_OBS_FIELD} + field = [{ name="PrecipFlag"; level="L0"; conv_radius = 0; conv_thresh = ==3; file_type = GRIB2; merge_flag = NONE; }, + { name="VIS"; level="L0"; conv_radius = 5; conv_thresh = <=804.672; merge_thresh = <=1207.008; merge_flag = THRESH; }, + { name="WIND"; level="Z10"; conv_radius = 5; conv_thresh = >=8.9408; merge_thresh = >=6.7056; merge_flag = THRESH; } ]; + + //${METPLUS_OBS_CENSOR_THRESH} + //${METPLUS_OBS_CENSOR_VAL} + //${METPLUS_OBS_CONV_RADIUS} + //${METPLUS_OBS_CONV_THRESH} + //${METPLUS_OBS_VLD_THRESH} + + //${METPLUS_OBS_FILTER_ATTR_NAME} + filter_attr_name = ["AREA"]; + + //${METPLUS_OBS_FILTER_ATTR_THRESH} + filter_attr_thresh = [>=25]; + + // ${METPLUS_OBS_MERGE_THRESH} + // ${METPLUS_OBS_MERGE_FLAG} + // ${METPLUS_OBS_FILE_TYPE} + multivar_name = "Precip"; + multivar_level = "LO"; +} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Handle missing data +// +// mask_missing_flag = +// ${METPLUS_MASK_MISSING_FLAG} + +// +// Match objects between the forecast and observation fields +// +//match_flag = +//${METPLUS_MATCH_FLAG} +match_flag = MERGE_BOTH; +// +// Maximum centroid distance for objects to be compared +// +//max_centroid_dist = +//${METPLUS_MAX_CENTROID_DIST} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Verification masking regions +// +//mask = { +//${METPLUS_MASK_DICT} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Fuzzy engine weights +// +//weight = { +//${METPLUS_WEIGHT_DICT} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Fuzzy engine interest functions +// +interest_function = { + + //${METPLUS_INTEREST_FUNCTION_CENTROID_DIST} + + //${METPLUS_INTEREST_FUNCTION_BOUNDARY_DIST} + + //${METPLUS_INTEREST_FUNCTION_CONVEX_HULL_DIST} + + angle_diff = ( + ( 0.0, 1.0 ) + ( 30.0, 1.0 ) + ( 90.0, 0.0 ) + ); + + aspect_diff = ( + ( 0.00, 1.0 ) + ( 0.10, 1.0 ) + ( 0.75, 0.0 ) + ); + + corner = 0.8; + ratio_if = ( + ( 0.0, 0.0 ) + ( corner, 1.0 ) + ( 1.0, 1.0 ) + ); + + area_ratio = ratio_if; + + int_area_ratio = ( + ( 0.00, 0.00 ) + ( 0.10, 0.50 ) + ( 0.25, 1.00 ) + ( 1.00, 1.00 ) + ); + + curvature_ratio = ratio_if; + + complexity_ratio = ratio_if; + + inten_perc_ratio = ratio_if; +} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Total interest threshold for determining matches +// +//total_interest_thresh = +//${METPLUS_TOTAL_INTEREST_THRESH} + +// +// Interest threshold for printing output pair information +// +print_interest_thresh = 0.0; + +//////////////////////////////////////////////////////////////////////////////// + +// +// Plotting information +// +met_data_dir = "MET_BASE"; + +fcst_raw_plot = { + color_table = "MET_BASE/colortables/met_default.ctable"; + plot_min = 0.0; + plot_max = 0.0; +} + +obs_raw_plot = { + color_table = "MET_BASE/colortables/met_default.ctable"; + plot_min = 0.0; + plot_max = 0.0; +} + +object_plot = { + color_table = "MET_BASE/colortables/mode_obj.ctable"; +} + +// +// Boolean for plotting on the region of valid data within the domain +// +plot_valid_flag = FALSE; + +// +// Plot polyline edges using great circle arcs instead of straight lines +// +plot_gcarc_flag = FALSE; + +//////////////////////////////////////////////////////////////////////////////// + +// +// NetCDF matched pairs, PostScript, and contingency table output files +// +//ps_plot_flag = +//${METPLUS_PS_PLOT_FLAG} + +//nc_pairs_flag = { +//${METPLUS_NC_PAIRS_FLAG_DICT} + +//ct_stats_flag = +//${METPLUS_CT_STATS_FLAG} + + +//////////////////////////////////////////////////////////////////////////////// + +shift_right = 0; // grid squares + +//////////////////////////////////////////////////////////////////////////////// + +//${METPLUS_OUTPUT_PREFIX} +//version = "V10.0"; + +//tmp_dir = "${MET_TMP_DIR}"; +tmp_dir = "/tmp"; + +//////////////////////////////////////////////////////////////////////////////// + +//${METPLUS_MET_CONFIG_OVERRIDES} diff --git a/internal/test_unit/config/PB2NCConfig b/internal/test_unit/config/PB2NCConfig index 9e8c3b3c25..68b7045d9b 100644 --- a/internal/test_unit/config/PB2NCConfig +++ b/internal/test_unit/config/PB2NCConfig @@ -73,7 +73,7 @@ level_category = []; // // BUFR variable names to retain or derive. -// If emtpy, process all available variables. +// If empty, process all available variables. // obs_bufr_var = [ "QOB", "TOB", "ZOB", "UOB", "VOB", "TP24", "D_DPT", "D_WIND", "D_RH", "D_MIXR" ]; diff --git a/internal/test_unit/config/PB2NCConfig_airnow b/internal/test_unit/config/PB2NCConfig_airnow index d25b5484ba..6f71c2298b 100644 --- a/internal/test_unit/config/PB2NCConfig_airnow +++ b/internal/test_unit/config/PB2NCConfig_airnow @@ -78,7 +78,7 @@ level_category = []; // // BUFR variable names to retain or derive. -// If emtpy, process all available variables. +// If empty, process all available variables. // obs_bufr_var = [ "COPO" ]; diff --git a/internal/test_unit/config/PB2NCConfig_all b/internal/test_unit/config/PB2NCConfig_all index 684848b1f3..df110af432 100644 --- a/internal/test_unit/config/PB2NCConfig_all +++ b/internal/test_unit/config/PB2NCConfig_all @@ -73,7 +73,7 @@ level_category = []; // // BUFR variable names to retain or derive. -// If emtpy, process all available variables. +// If empty, process all available variables. // obs_bufr_var = []; diff --git a/internal/test_unit/config/PB2NCConfig_summary b/internal/test_unit/config/PB2NCConfig_summary index 4de66be11f..b75e897177 100644 --- a/internal/test_unit/config/PB2NCConfig_summary +++ b/internal/test_unit/config/PB2NCConfig_summary @@ -73,7 +73,7 @@ level_category = []; // // BUFR variable names to retain or derive. -// If emtpy, process all available variables. +// If empty, process all available variables. // obs_bufr_var = [ "QOB", "TOB", "ZOB", "UOB", "VOB", "D_DPT", "D_WIND", "D_RH", "D_MIXR" ]; diff --git a/internal/test_unit/config/PB2NCConfig_vlevel b/internal/test_unit/config/PB2NCConfig_vlevel index 3de685516b..256ec45c29 100644 --- a/internal/test_unit/config/PB2NCConfig_vlevel +++ b/internal/test_unit/config/PB2NCConfig_vlevel @@ -73,7 +73,7 @@ level_category = []; // // BUFR variable names to retain or derive. -// If emtpy, process all available variables. +// If empty, process all available variables. // obs_bufr_var = [ "RSTD", "RWAZ","RWND" ]; diff --git a/internal/test_unit/config/ref_config/PB2NCConfig b/internal/test_unit/config/ref_config/PB2NCConfig index a8760f4d6c..ce6535a6e3 100644 --- a/internal/test_unit/config/ref_config/PB2NCConfig +++ b/internal/test_unit/config/ref_config/PB2NCConfig @@ -75,7 +75,7 @@ level_category = []; // // BUFR variable names to retain or derive. -// If emtpy, process all available variables. +// If empty, process all available variables. // obs_bufr_var = [ "QOB", "TOB", "ZOB", "UOB", "VOB", "D_WIND", "D_DPT", "D_PRMSL" ]; diff --git a/internal/test_unit/xml/unit_mode_multivar.xml b/internal/test_unit/xml/unit_mode_multivar.xml index 4aad5dc7d6..aea7f7f2a0 100644 --- a/internal/test_unit/xml/unit_mode_multivar.xml +++ b/internal/test_unit/xml/unit_mode_multivar.xml @@ -16,7 +16,90 @@ &TEST_DIR; true - + + echo "&DATA_DIR_MODEL;/mode_multivar/hrrr/2021020100/hrrr.t00z.wrfprsf21.sub.grib2 \ + &DATA_DIR_MODEL;/mode_multivar/hrrr/2021020100/hrrr.t00z.wrfprsf21.sub.grib2 \ + &DATA_DIR_MODEL;/mode_multivar/hrrr/2021020100/hrrr.t00z.wrfprsf21.sub.grib2" \ + > &OUTPUT_DIR;/mode_multivar/input_fcst_file_list; \ + echo "&DATA_DIR_OBS;/mode_multivar/mrms/20210201/PrecipFlag_00.00_20210201-210000.sub.grib2 \ + &DATA_DIR_OBS;/mode_multivar/hrrr/anl/20210201/hrrr.t21z.wrfprsf00.sub.grib2 \ + &DATA_DIR_OBS;/mode_multivar/hrrr/anl/20210201/hrrr.t21z.wrfprsf00.sub.grib2" \ + > &OUTPUT_DIR;/mode_multivar/input_obs_file_list; \ + &MET_BIN;/mode + \ + &OUTPUT_DIR;/mode_multivar/input_fcst_file_list \ + &OUTPUT_DIR;/mode_multivar/input_obs_file_list \ + &CONFIG_DIR;/MODEConfig_multivar \ + -outdir &OUTPUT_DIR;/mode_multivar \ + -v 2 + + + &OUTPUT_DIR;/mode_multivar/mode_Fcst_VIS_L0_Obs_VIS_L0_210000L_20210201_210000V_000000A_obj.nc + &OUTPUT_DIR;/mode_multivar/mode_Fcst_VIS_L0_Obs_VIS_L0_210000L_20210201_210000V_000000A_obj.txt + &OUTPUT_DIR;/mode_multivar/mode_Fcst_VIS_L0_Obs_VIS_L0_210000L_20210201_210000V_000000A_cts.txt + &OUTPUT_DIR;/mode_multivar/mode_Fcst_VIS_L0_Obs_VIS_L0_210000L_20210201_210000V_000000A.ps + &OUTPUT_DIR;/mode_multivar/mode_Fcst_WIND_Z10_Obs_WIND_Z10_210000L_20210201_210000V_000000A_obj.nc + &OUTPUT_DIR;/mode_multivar/mode_Fcst_WIND_Z10_Obs_WIND_Z10_210000L_20210201_210000V_000000A_obj.txt + &OUTPUT_DIR;/mode_multivar/mode_Fcst_WIND_Z10_Obs_WIND_Z10_210000L_20210201_210000V_000000A_cts.txt + &OUTPUT_DIR;/mode_multivar/mode_Fcst_WIND_Z10_Obs_WIND_Z10_210000L_20210201_210000V_000000A.ps + + + + + + echo "&DATA_DIR_MODEL;/mode_multivar/hrrr/2021020100/hrrr.t00z.wrfprsf21.sub.grib2 \ + &DATA_DIR_MODEL;/mode_multivar/hrrr/2021020100/hrrr.t00z.wrfprsf21.sub.grib2 \ + &DATA_DIR_MODEL;/mode_multivar/hrrr/2021020100/hrrr.t00z.wrfprsf21.sub.grib2" \ + > &OUTPUT_DIR;/mode_multivar/input_fcst_file_list; \ + echo "&DATA_DIR_OBS;/mode_multivar/hrrr/anl/20210201/hrrr.t21z.wrfprsf00.sub.grib2 \ + &DATA_DIR_OBS;/mode_multivar/hrrr/anl/20210201/hrrr.t21z.wrfprsf00.sub.grib2" \ + > &OUTPUT_DIR;/mode_multivar/input_obs_file_list; \ + &MET_BIN;/mode + \ + &OUTPUT_DIR;/mode_multivar/input_fcst_file_list \ + &OUTPUT_DIR;/mode_multivar/input_obs_file_list \ + &CONFIG_DIR;/MODEConfig_multivar_3_2 \ + -outdir &OUTPUT_DIR;/mode_multivar \ + -v 2 + + + &OUTPUT_DIR;/mode_multivar/mode_Fcst_VIS_L0_Obs_VIS_L0_210000L_20210201_210000V_000000A_obj.nc + &OUTPUT_DIR;/mode_multivar/mode_Fcst_VIS_L0_Obs_VIS_L0_210000L_20210201_210000V_000000A_obj.txt + &OUTPUT_DIR;/mode_multivar/mode_Fcst_VIS_L0_Obs_VIS_L0_210000L_20210201_210000V_000000A_cts.txt + &OUTPUT_DIR;/mode_multivar/mode_Fcst_VIS_L0_Obs_VIS_L0_210000L_20210201_210000V_000000A.ps + &OUTPUT_DIR;/mode_multivar/mode_Fcst_VIS_L0_Obs_WIND_Z10_210000L_20210201_210000V_000000A_obj.nc + &OUTPUT_DIR;/mode_multivar/mode_Fcst_VIS_L0_Obs_WIND_Z10_210000L_20210201_210000V_000000A_obj.txt + &OUTPUT_DIR;/mode_multivar/mode_Fcst_VIS_L0_Obs_WIND_Z10_210000L_20210201_210000V_000000A_cts.txt + &OUTPUT_DIR;/mode_multivar/mode_Fcst_VIS_L0_Obs_WIND_Z10_210000L_20210201_210000V_000000A.ps + + + + + echo "&DATA_DIR_MODEL;/mode_multivar/hrrr/2021020100/hrrr.t00z.wrfprsf21.sub.grib2 \ + &DATA_DIR_MODEL;/mode_multivar/hrrr/2021020100/hrrr.t00z.wrfprsf21.sub.grib2 \ + &DATA_DIR_MODEL;/mode_multivar/hrrr/2021020100/hrrr.t00z.wrfprsf21.sub.grib2" \ + > &OUTPUT_DIR;/mode_multivar/input_fcst_file_list; \ + echo "&DATA_DIR_OBS;/mode_multivar/mrms/20210201/PrecipFlag_00.00_20210201-210000.sub.grib2 \ + &DATA_DIR_OBS;/mode_multivar/hrrr/anl/20210201/hrrr.t21z.wrfprsf00.sub.grib2 \ + &DATA_DIR_OBS;/mode_multivar/hrrr/anl/20210201/hrrr.t21z.wrfprsf00.sub.grib2" \ + > &OUTPUT_DIR;/mode_multivar/input_obs_file_list; \ + &MET_BIN;/mode + \ + &OUTPUT_DIR;/mode_multivar/input_fcst_file_list \ + &OUTPUT_DIR;/mode_multivar/input_obs_file_list \ + &CONFIG_DIR;/MODEConfig_multivar_super \ + -outdir &OUTPUT_DIR;/mode_multivar \ + -v 2 + + + &OUTPUT_DIR;/mode_multivar/mode_Fcst_Snow_LO_Obs_Precip_LO_210000L_20210201_210000V_000000A_obj.nc + &OUTPUT_DIR;/mode_multivar/mode_Fcst_Snow_LO_Obs_Precip_LO_210000L_20210201_210000V_000000A_obj.txt + &OUTPUT_DIR;/mode_multivar/mode_Fcst_Snow_LO_Obs_Precip_LO_210000L_20210201_210000V_000000A_cts.txt + &OUTPUT_DIR;/mode_multivar/mode_Fcst_Snow_LO_Obs_Precip_LO_210000L_20210201_210000V_000000A.ps + + + + echo "&DATA_DIR_MODEL;/mode_multivar/alpha_fcst.nc \ &DATA_DIR_MODEL;/mode_multivar/beta_fcst.nc \ &DATA_DIR_MODEL;/mode_multivar/gamma_fcst.nc" \ diff --git a/internal/test_util/libcode/Makefile.in b/internal/test_util/libcode/Makefile.in index 0f56d40950..4d4f244b34 100644 --- a/internal/test_util/libcode/Makefile.in +++ b/internal/test_util/libcode/Makefile.in @@ -151,7 +151,7 @@ am__define_uniq_tagged_files = \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` DIST_SUBDIRS = vx_data2d vx_data2d_grib vx_data2d_nc_met \ - vx_data2d_nccf vx_data2d_factory vx_geodesy vx_grid vx_ps \ + vx_data2d_nc_cf vx_data2d_factory vx_geodesy vx_grid vx_ps \ vx_solar vx_plot_util vx_tc_util vx_nc_util vx_physics \ vx_series_data vx_python3_utils am__DIST_COMMON = $(srcdir)/Makefile.in diff --git a/scripts/config/PB2NCConfig_G212 b/scripts/config/PB2NCConfig_G212 index a89ab486ea..5836727a79 100644 --- a/scripts/config/PB2NCConfig_G212 +++ b/scripts/config/PB2NCConfig_G212 @@ -73,7 +73,7 @@ level_category = []; // // BUFR variable names to retain or derive. -// If emtpy, process all available variables. +// If empty, process all available variables. // obs_bufr_var = [ "QOB", "TOB", "ZOB", "UOB", "VOB", "D_DPT", "D_WIND", "D_RH", "D_MIXR" ]; diff --git a/scripts/python/pyembed/python_embedding.py b/scripts/python/pyembed/python_embedding.py index 8897f1406f..cc63fe47df 100644 --- a/scripts/python/pyembed/python_embedding.py +++ b/scripts/python/pyembed/python_embedding.py @@ -20,6 +20,7 @@ import os import sys import json +import math from importlib import util as import_util class pyembed_tools(): @@ -93,27 +94,50 @@ def read_tmp_ascii(filename): Returns: (list of lists): point or mpr data """ - f = open(filename, 'r') - lines = f.readlines() - f.close() - - ascii_data = [eval(line.strip('\n')) for line in lines] + with open(filename, 'r') as f: + lines = f.readlines() + f.close() + + try: + ascii_data = [eval(line.strip('\n')) for line in lines] + except: + try: + print(f' PYTHON INFO pyembed_tools.read_tmp_ascii() nan and inf are changed to -9999 from {filename}.') + ascii_data = [eval(line.strip('\n').replace("nan", "-9999").replace("inf", "-9999")) for line in lines] + except: + # Log where the problem happens + line_no = 0 + line_buf = "" + try: + for line_buf in lines: + line_no += 1 + eval(line_buf.strip('\n').replace("nan", "-9999").replace("inf", "-9999")) + except: + print(f' PYTHON ERROR pyembed_tools.read_tmp_ascii() failed parsing "{line_buf}" at line {line_no}') + raise return ascii_data @staticmethod def write_tmp_ascii(filename, met_data): with open(filename, 'w') as f: + inf_count = 0 + nan_count = 0 for line in met_data: f.write(str(line) + '\n') + inf_count += line.count(math.inf) + nan_count += line.count(math.nan) + + if 0 < (nan_count + inf_count): + print(f' PYTHON WARNING pyembed_tools.write_tmp_ascii() Saved {nan_count} nan and {inf_count} infinite values to "{filename}"') @staticmethod def write_tmp_diag(filename, diag_data): - json.dump(diag_data, open(filename,'w')) + json.dump(diag_data, open(filename,'w')) @staticmethod def read_tmp_diag(filename): - return json.load(open(filename)) + return json.load(open(filename)) if __name__ == '__main__': argv_org = sys.argv[:] # save original sys.argv diff --git a/src/basic/vx_cal/time_array.h b/src/basic/vx_cal/time_array.h index baa834675f..807aec977e 100644 --- a/src/basic/vx_cal/time_array.h +++ b/src/basic/vx_cal/time_array.h @@ -20,6 +20,8 @@ #include +typedef long long unixtime; + //////////////////////////////////////////////////////////////////////// diff --git a/src/basic/vx_cal/vx_cal.h b/src/basic/vx_cal/vx_cal.h index 1784cfb5d1..f7d553a55b 100644 --- a/src/basic/vx_cal/vx_cal.h +++ b/src/basic/vx_cal/vx_cal.h @@ -19,9 +19,6 @@ #include "concat_string.h" - -typedef long long unixtime; - #include "time_array.h" diff --git a/src/basic/vx_config/config_constants.h b/src/basic/vx_config/config_constants.h index 5985e0ba8e..4b51aa15ac 100644 --- a/src/basic/vx_config/config_constants.h +++ b/src/basic/vx_config/config_constants.h @@ -525,9 +525,11 @@ static const char config_map_data_filename[] = "MET_BASE/config/ConfigMapData"; // Parameter key names common to multiple tools // -static const char conf_key_exit_on_warning[] = "exit_on_warning"; -static const char conf_key_nc_compression[] = "nc_compression"; -static const char conf_key_output_precision[] = "output_precision"; +static const char conf_key_exit_on_warning[] = "exit_on_warning"; +static const char conf_key_time_offset_warning[] = "time_offset_warning"; +static const char conf_key_nc_compression[] = "nc_compression"; +static const char conf_key_output_precision[] = "output_precision"; + static const char conf_key_version[] = "version"; static const char conf_key_model[] = "model"; static const char conf_key_desc[] = "desc"; diff --git a/src/basic/vx_config/config_file.cc b/src/basic/vx_config/config_file.cc index 67c8af8959..0ca0bf5aa0 100644 --- a/src/basic/vx_config/config_file.cc +++ b/src/basic/vx_config/config_file.cc @@ -115,6 +115,20 @@ assign(c); } +//////////////////////////////////////////////////////////////////////// + +MetConfig & MetConfig::operator=(const MetConfig &s) +{ + if(this == &s) return(*this); + + init_from_scratch(); + + assign(s); + + return(*this); + +} + //////////////////////////////////////////////////////////////////////// @@ -255,27 +269,10 @@ return; } -//////////////////////////////////////////////////////////////////////// - - -ConcatString MetConfig::get_tmp_dir() -{ - ConcatString tmp_dir; - - // Use the MET_TMP_DIR environment variable, if set. - if(!get_env("MET_TMP_DIR", tmp_dir)) { - const DictionaryEntry * _e = lookup(conf_key_tmp_dir); - if ( LastLookupStatus ) tmp_dir = _e->string_value(); - else tmp_dir = default_tmp_dir; - } - - return tmp_dir; -} - - //////////////////////////////////////////////////////////////////////// int MetConfig::nc_compression() + { ConcatString cs; int n = 0; @@ -310,6 +307,41 @@ return n; //////////////////////////////////////////////////////////////////////// +ConcatString MetConfig::get_tmp_dir() + +{ + ConcatString tmp_dir; + + // Use the MET_TMP_DIR environment variable, if set. + if(!get_env("MET_TMP_DIR", tmp_dir)) { + const DictionaryEntry * _e = lookup(conf_key_tmp_dir); + if ( LastLookupStatus ) tmp_dir = _e->string_value(); + else tmp_dir = default_tmp_dir; + } + + return tmp_dir; +} + + +//////////////////////////////////////////////////////////////////////// + + +bool MetConfig::time_offset_warning(int offset) + +{ + +int allowable_offset = lookup_int(conf_key_time_offset_warning, false); + +if (!LastLookupStatus ) allowable_offset = 0; + +return ( abs(offset) > allowable_offset ); + +} + + +//////////////////////////////////////////////////////////////////////// + + bool MetConfig::read(const char * filename) { diff --git a/src/basic/vx_config/config_file.h b/src/basic/vx_config/config_file.h index 9fff9a35f8..0af9da6a6c 100644 --- a/src/basic/vx_config/config_file.h +++ b/src/basic/vx_config/config_file.h @@ -78,6 +78,8 @@ class MetConfig : public Dictionary { ConcatString get_tmp_dir(); + bool time_offset_warning(int); + StringArray filename() const; bool debug() const; diff --git a/src/libcode/vx_data2d/var_info.h b/src/libcode/vx_data2d/var_info.h index 7dcf6e386a..15fa2f90b7 100644 --- a/src/libcode/vx_data2d/var_info.h +++ b/src/libcode/vx_data2d/var_info.h @@ -91,11 +91,14 @@ class VarInfo VarInfo(const VarInfo &); VarInfo & operator=(const VarInfo &); + virtual VarInfo *clone() const = 0; + // Conversion function UserFunc_1Arg ConvertFx; void clear(); - + void clone_base() const; + virtual void dump(std::ostream &) const; // diff --git a/src/libcode/vx_data2d_grib/var_info_grib.cc b/src/libcode/vx_data2d_grib/var_info_grib.cc index c414bc94f8..e6bb74198f 100644 --- a/src/libcode/vx_data2d_grib/var_info_grib.cc +++ b/src/libcode/vx_data2d_grib/var_info_grib.cc @@ -75,6 +75,15 @@ VarInfoGrib & VarInfoGrib::operator=(const VarInfoGrib &f) { /////////////////////////////////////////////////////////////////////////////// +VarInfo *VarInfoGrib::clone() const { + + VarInfoGrib *ret = new VarInfoGrib(*this); + + return (VarInfo *)ret; +} + +/////////////////////////////////////////////////////////////////////////////// + void VarInfoGrib::init_from_scratch() { // First call the parent's initialization diff --git a/src/libcode/vx_data2d_grib/var_info_grib.h b/src/libcode/vx_data2d_grib/var_info_grib.h index cdf0947b90..d695eed007 100644 --- a/src/libcode/vx_data2d_grib/var_info_grib.h +++ b/src/libcode/vx_data2d_grib/var_info_grib.h @@ -67,6 +67,7 @@ class VarInfoGrib : public VarInfo ~VarInfoGrib(); VarInfoGrib(const VarInfoGrib &); VarInfoGrib & operator=(const VarInfoGrib &); + VarInfo *clone() const; void dump(std::ostream &) const; void clear(); diff --git a/src/libcode/vx_data2d_grib2/var_info_grib2.cc b/src/libcode/vx_data2d_grib2/var_info_grib2.cc index 6ea70eecfa..ef3a3c7db6 100644 --- a/src/libcode/vx_data2d_grib2/var_info_grib2.cc +++ b/src/libcode/vx_data2d_grib2/var_info_grib2.cc @@ -77,6 +77,15 @@ VarInfoGrib2 & VarInfoGrib2::operator=(const VarInfoGrib2 &f) { /////////////////////////////////////////////////////////////////////////////// +VarInfo *VarInfoGrib2::clone() const { + + VarInfoGrib2 *ret = new VarInfoGrib2(*this); + + return (VarInfo *)ret; +} + +/////////////////////////////////////////////////////////////////////////////// + void VarInfoGrib2::init_from_scratch() { // First call the parent's initialization diff --git a/src/libcode/vx_data2d_grib2/var_info_grib2.h b/src/libcode/vx_data2d_grib2/var_info_grib2.h index 619508358b..5f8e320339 100644 --- a/src/libcode/vx_data2d_grib2/var_info_grib2.h +++ b/src/libcode/vx_data2d_grib2/var_info_grib2.h @@ -72,6 +72,7 @@ class VarInfoGrib2 : public VarInfo ~VarInfoGrib2(); VarInfoGrib2(const VarInfoGrib2 &); VarInfoGrib2 & operator=(const VarInfoGrib2 &); + VarInfo *clone() const; void dump(std::ostream &) const; void clear(); diff --git a/src/libcode/vx_data2d_nc_cf/data2d_nc_cf.cc b/src/libcode/vx_data2d_nc_cf/data2d_nc_cf.cc index 6a7cfaef48..36c5479921 100644 --- a/src/libcode/vx_data2d_nc_cf/data2d_nc_cf.cc +++ b/src/libcode/vx_data2d_nc_cf/data2d_nc_cf.cc @@ -77,7 +77,7 @@ MetNcCFDataFile & MetNcCFDataFile::operator=(const MetNcCFDataFile &) { void MetNcCFDataFile::nccf_init_from_scratch() { - _file = (NcCfFile *) 0; + _file = (NcCfFile *) nullptr; _cur_time_index = -1; close(); diff --git a/src/libcode/vx_data2d_nc_cf/nc_cf_file.cc b/src/libcode/vx_data2d_nc_cf/nc_cf_file.cc index 3de1882c7a..5018f62e8c 100644 --- a/src/libcode/vx_data2d_nc_cf/nc_cf_file.cc +++ b/src/libcode/vx_data2d_nc_cf/nc_cf_file.cc @@ -10,8 +10,6 @@ //////////////////////////////////////////////////////////////////////// -using namespace std; - #include #include #include @@ -23,7 +21,6 @@ using namespace std; #include #include -using namespace netCDF; #include "vx_math.h" #include "vx_cal.h" @@ -31,6 +28,9 @@ using namespace netCDF; #include "nc_cf_file.h" +using namespace std; +using namespace netCDF; + //////////////////////////////////////////////////////////////////////// @@ -221,7 +221,6 @@ bool NcCfFile::open(const char * filepath) Nvars = get_var_names(_ncFile, &varNames); Var = new NcVarInfo [Nvars]; - NcDim dim; for (int j=0; jValidTime.n(); static const string method_name = "MetUGridDataFile::data_plane(VarInfo &, DataPlane &) -> "; @@ -229,15 +228,13 @@ bool MetUGridDataFile::data_plane(VarInfo &vinfo, DataPlane &plane, NcVarInfo *d plane.clear(); - int zdim_slot = bad_data_int; - int time_dim_slot = bad_data_int; NumArray dim_value = vinfo_nc->dim_value(); LongArray dimension(vinfo_nc->dimension()); BoolArray is_offset(vinfo_nc->is_offset()); if (nullptr != data_var) { - zdim_slot = data_var->z_slot; - time_dim_slot = data_var->t_slot; + int zdim_slot = data_var->z_slot; + int time_dim_slot = data_var->t_slot; // Adjust dimension and is_offset int dim_size = dimension.n_elements(); @@ -310,7 +307,6 @@ int MetUGridDataFile::data_plane_array(VarInfo &vinfo, DataPlaneArray &plane_array) { int n_rec = 0; DataPlane plane; - bool status = false; static const string method_name = "MetUGridDataFile::data_plane_array(VarInfo &, DataPlaneArray &) -> "; @@ -320,7 +316,6 @@ int MetUGridDataFile::data_plane_array(VarInfo &vinfo, LevelInfo level = vinfo.level(); long lvl_lower = level.lower(); long lvl_upper = level.upper(); - const int debug_level = 7; ConcatString req_name = vinfo.req_name(); NcVarInfo *data_vinfo = _file->find_by_name(req_name.c_str()); if (level.type() == LevelType_Time) { @@ -328,6 +323,7 @@ int MetUGridDataFile::data_plane_array(VarInfo &vinfo, << "LevelType_Time for unstructured grid is not enabled\n\n"; exit(1); /* Not enabled + const int debug_level = 7; LongArray time_offsets = collect_time_offsets(vinfo); if (0 < time_offsets.n_elements()) { for (int idx=0; idxz_slot >= 0) { int zdim_size = get_dim_size(data_vinfo->var, data_vinfo->z_slot); if (tmp_lower >= zdim_size) tmp_lower = zdim_size - 1; diff --git a/src/libcode/vx_data2d_ugrid/ugrid_file.cc b/src/libcode/vx_data2d_ugrid/ugrid_file.cc index 13cfde1f6f..21d5a2227d 100644 --- a/src/libcode/vx_data2d_ugrid/ugrid_file.cc +++ b/src/libcode/vx_data2d_ugrid/ugrid_file.cc @@ -195,7 +195,6 @@ bool UGridFile::open(const char * filepath) bool UGridFile::open_metadata(const char * filepath) { unixtime ut = 0; - int sec_per_unit; const char *method_name = "UGridFile::open_metadata() -> "; // Open the file @@ -319,6 +318,8 @@ bool UGridFile::open_metadata(const char * filepath) ValidTime.add(ut); } else { + int sec_per_unit; + // Store the dimension for the time variable as the time dimension ConcatString units; bool use_bounds_var = false; @@ -374,7 +375,6 @@ bool UGridFile::open_metadata(const char * filepath) else { if (use_bounds_var) { double bounds_diff; - double time_fraction; for(int i=0; i observations, const bool do_header) { int prev_hdr_idx = -1; - string prev_header_type = ""; - string prev_station_id = ""; ConcatString obs_qty; int headerOffset = data_buffer.cur_hdr_idx; const string method_name = " write_obs_data()"; diff --git a/src/libcode/vx_nc_util/nc_utils.cc b/src/libcode/vx_nc_util/nc_utils.cc index 2cd3e9aa56..6a86c236b5 100644 --- a/src/libcode/vx_nc_util/nc_utils.cc +++ b/src/libcode/vx_nc_util/nc_utils.cc @@ -199,9 +199,9 @@ bool get_att_value_chars(const NcAtt *att, ConcatString &value) { att->getValues(att_value); value = att_value[0]; } - catch (exceptions::NcException &ex) { + catch (exceptions::NcException &ex2) { mlog << Warning << "\n" << method_name - << "Exception: " << ex.what() << "\n" + << "Exception: " << ex2.what() << "\n" << "Fail to read " << GET_NC_NAME_P(att) << " attribute (" << GET_NC_TYPE_NAME_P(att) << " type).\n" << "Please check the encoding of the "<< GET_NC_NAME_P(att) << " attribute.\n\n"; @@ -1273,7 +1273,7 @@ float get_float_var(NcVar * var, const int index) { exit(1); } } - else if ((index > dim_size) && (0 < dim_size)){ + else if (index > dim_size){ NcDim nc_dim = get_nc_dim(var, dim_idx); mlog << Error << "\n" << method_name << "The start offset (" << index << ") exceeds the dimension " << dim_size << " " @@ -1391,11 +1391,10 @@ bool get_nc_data(NcVar *var, float *data) { { double *packed_data = new double[cell_count]; if (get_nc_data_t(var, packed_data)) { - double a_data; double fill_value; bool has_fill_value = get_var_fill_value(var, fill_value); for (int idx=0; idxisNull())) +#define IS_VALID_NC_P(ncObjPtr) ((ncObjPtr != nullptr && !ncObjPtr->isNull())) #define IS_INVALID_NC(ncObj) ncObj.isNull() -#define IS_INVALID_NC_P(ncObjPtr) (ncObjPtr == 0 || ncObjPtr->isNull()) +#define IS_INVALID_NC_P(ncObjPtr) (ncObjPtr == nullptr || ncObjPtr->isNull()) #define GET_NC_NAME(ncObj) ncObj.getName() #define GET_NC_NAME_P(ncObjPtr) ncObjPtr->getName() diff --git a/src/libcode/vx_nc_util/nc_utils.hpp b/src/libcode/vx_nc_util/nc_utils.hpp index 9c87db76cb..4a6c0a1a92 100644 --- a/src/libcode/vx_nc_util/nc_utils.hpp +++ b/src/libcode/vx_nc_util/nc_utils.hpp @@ -396,7 +396,7 @@ bool get_nc_data_(netCDF::NcVar *var, T *data, T met_missing, const long dim, co exit(1); } } - else if (((cur+dim) > dim_size) && (0 < dim_size)) { + else if ((cur+dim) > dim_size) { netCDF::NcDim nc_dim = get_nc_dim(var, dim_idx); mlog << Error << "\n" << method_name << "The start offset and count (" << cur << " + " << dim << ") exceeds the dimension " << dim_size << " " @@ -433,7 +433,7 @@ bool get_nc_data_(netCDF::NcVar *var, T *data, T met_missing, const long dim, co template bool get_nc_data_(netCDF::NcVar *var, T *data, T bad_data, const LongArray &curs) { bool return_status = false; - const char *method_name = "get_nc_data_(*curs) "; + //const char *method_name = "get_nc_data_(*curs) "; if (IS_VALID_NC_P(var)) { @@ -559,11 +559,12 @@ void copy_nc_data_t(netCDF::NcVar *var, double *data, const T *packed_data, const int cell_count, const char *data_type, double add_offset, double scale_factor, bool has_missing, T missing_value) { - int unpacked_count = 0; const char *method_name = "copy_nc_data_t(double) "; if (cell_count > 0) { - int idx, first_idx; + int idx; + int first_idx; + int unpacked_count = 0; double min_value, max_value; bool do_scale_factor = has_scale_factor_attr(var) || has_add_offset_attr(var); diff --git a/src/libcode/vx_nc_util/write_netcdf.cc b/src/libcode/vx_nc_util/write_netcdf.cc index a3614167cb..b9d5e2bdc5 100644 --- a/src/libcode/vx_nc_util/write_netcdf.cc +++ b/src/libcode/vx_nc_util/write_netcdf.cc @@ -9,7 +9,6 @@ /////////////////////////////////////////////////////////////////////////////// -using namespace std; #include #include @@ -21,7 +20,6 @@ using namespace std; #include #include -using namespace netCDF; #include "vx_log.h" #include "vx_cal.h" @@ -29,6 +27,9 @@ using namespace netCDF; #include "write_netcdf.h" #include "grid_output.h" +using namespace std; +using namespace netCDF; + /////////////////////////////////////////////////////////////////////////////// static void write_netcdf_latlon_1d(NcFile *, NcDim *, NcDim *, const Grid &); @@ -107,10 +108,13 @@ void write_netcdf_latlon(NcFile *f_out, NcDim *lat_dim, NcDim *lon_dim, void write_netcdf_latlon_1d(NcFile *f_out, NcDim *lat_dim, NcDim *lon_dim, const Grid &grid) { int i; - double lat, lon; - NcVar lat_var, lon_var; - float *lat_data = (float *) 0; - float *lon_data = (float *) 0; + double lat; + double lon; + NcVar lat_var; + NcVar lon_var; + // Allocate space for lat/lon values + float *lat_data = new float [grid.ny()]; + float *lon_data = new float [grid.nx()]; // Define Variables lat_var = f_out->addVar("lat", ncFloat, *lat_dim); @@ -125,10 +129,6 @@ void write_netcdf_latlon_1d(NcFile *f_out, NcDim *lat_dim, NcDim *lon_dim, add_att(&lon_var, units_att_name, "degrees_east"); add_att(&lon_var, standard_name_att_name, "longitude"); - // Allocate space for lat/lon values - lat_data = new float [grid.ny()]; - lon_data = new float [grid.nx()]; - // Compute latitude values for(i=0; igetSize(), 0); - if ( lat_data ) { delete [] lat_data; lat_data = 0; } - if ( lon_data ) { delete [] lon_data; lon_data = 0; } + if ( lat_data ) { delete [] lat_data; lat_data = nullptr; } + if ( lon_data ) { delete [] lon_data; lon_data = nullptr; } return; } @@ -160,11 +160,12 @@ void write_netcdf_latlon_2d(NcFile *f_out, NcDim *lat_dim, NcDim *lon_dim, int i, x, y; double lat, lon; NcVar lat_var, lon_var; - float *lat_data = (float *) 0; - float *lon_data = (float *) 0; vector dims; long counts[2] = {grid.ny(), grid.nx()}; long offsets[2] = {0 , 0}; + // Allocate space for lat/lon values + float *lat_data = new float [grid.nx()*grid.ny()]; + float *lon_data = new float [grid.nx()*grid.ny()]; // Define Variables dims.push_back(*lat_dim); @@ -181,10 +182,6 @@ void write_netcdf_latlon_2d(NcFile *f_out, NcDim *lat_dim, NcDim *lon_dim, add_att(&lon_var, units_att_name, "degrees_east"); add_att(&lon_var, standard_name_att_name, "longitude"); - // Allocate space for lat/lon values - lat_data = new float [grid.nx()*grid.ny()]; - lon_data = new float [grid.nx()*grid.ny()]; - // Compute lat/lon values for(x=0; x dims; vector count; + // Allocate space for weight values + float *wgt_data = new float [wgt_dp.nx()*wgt_dp.ny()]; // Define Variables dims.push_back(*lat_dim); @@ -247,12 +244,10 @@ void write_netcdf_grid_weight(NcFile *f_out, NcDim *lat_dim, NcDim *lon_dim, break; } - // Allocate space for weight values - wgt_data = new float [wgt_dp.nx()*wgt_dp.ny()]; // Store weight values - for(x=0; x #include #include @@ -24,6 +22,8 @@ using namespace std; #include "vx_log.h" #include "copy_bytes.h" +using namespace std; + ////////////////////////////////////////////////////////////////////////////////// @@ -40,15 +40,12 @@ void copy_bytes(int in, int out, int N) { -int bytes, bytes_left; -int n_read, n_written; - - -bytes_left = N; +int bytes_left = N; while ( bytes_left > 0 ) { - - bytes = bytes_left; + int n_read; + int n_written; + int bytes = bytes_left; if ( bytes > buf_size ) bytes = buf_size; diff --git a/src/libcode/vx_pointdata_python/python_pointdata.cc b/src/libcode/vx_pointdata_python/python_pointdata.cc index 8133f81ac7..2947a2c611 100644 --- a/src/libcode/vx_pointdata_python/python_pointdata.cc +++ b/src/libcode/vx_pointdata_python/python_pointdata.cc @@ -195,7 +195,7 @@ bool process_point_data(PyObject *python_met_point_data, { int int_value; -PyObject *python_value = 0; +PyObject *python_value; ConcatString cs, user_dir, user_base; const char *method_name = "process_point_data -> "; const char *method_name_s = "process_point_data()"; @@ -289,7 +289,6 @@ bool process_point_data_list(PyObject *python_point_data, MetPointDataPython &me Observation obs; time_t vld_time; int hid, vid, qid, sid, typ_idx, vld_idx; - double lat, lon, elv, hgt, level, obs_value; double prev_lat, prev_lon, prev_elv, prev_vld, prev_typ, prev_sid; Python3_List list(python_point_data); const char *method_name = "process_point_data_list -> "; @@ -325,9 +324,9 @@ bool process_point_data_list(PyObject *python_point_data, MetPointDataPython &me } obs.set(py_value); - lat = obs.getLatitude(); - lon = obs.getLongitude(); - elv = obs.getElevation(); + double lat = obs.getLatitude(); + double lon = obs.getLongitude(); + double elv = obs.getElevation(); if(filters) { if (filters->is_filtered(lat, lon)) continue; if (filters->is_filtered_sid(obs.getStationId().c_str())) continue; @@ -405,16 +404,17 @@ bool process_point_data_list(PyObject *python_point_data, MetPointDataPython &me } // for j - met_pd_out.set_use_var_id(use_var_id); - mlog << Debug(9) << method_name << "use_var_id: \"" << use_var_id - << "\" from python. is_using_var_id(): " << met_pd_out.is_using_var_id() << "\n"; - - if (hid <= 0) { + int h_cnt = hid + 1; // hid starts with -1 + if (h_cnt < 0) { mlog << Error << "\n" << method_name << "The header is empty. Please check the python script and input\n\n"; exit (1); } - met_pd_out.set_hdr_cnt(hid + 1); + met_pd_out.set_hdr_cnt(h_cnt); + + met_pd_out.set_use_var_id(use_var_id); + mlog << Debug(9) << method_name << "use_var_id: \"" << use_var_id + << "\" from python. is_using_var_id(): " << met_pd_out.is_using_var_id() << "\n"; check_obs_data(obs_data, use_var_id, method_name); check_header_data(header_data, method_name); @@ -441,8 +441,7 @@ bool straight_python_point_data(const char * script_name, int script_argc, char { int int_value; -PyObject *module_obj = 0; -PyObject *python_value = 0; +PyObject *module_obj; ConcatString cs, user_dir, user_base; const char *method_name = "straight_python_point_data -> "; @@ -530,7 +529,7 @@ if ( PyErr_Occurred() ) { << "an error occurred importing module \"" << script_name << "\"\n\n"; - return ( false ); + return false; } @@ -540,7 +539,7 @@ if ( ! module_obj ) { << "error running python script \"" << script_name << "\"\n\n"; - return ( false ); + return false; } @@ -579,7 +578,7 @@ int status; ConcatString command; ConcatString path; ConcatString tmp_nc_path; -const char * tmp_dir = 0; +const char * tmp_dir = nullptr; Wchar_Argv wa; const char *method_name = "tmp_nc_point_obs() -> "; diff --git a/src/libcode/vx_shapedata/Makefile.am b/src/libcode/vx_shapedata/Makefile.am index f54e937535..ea52307df3 100644 --- a/src/libcode/vx_shapedata/Makefile.am +++ b/src/libcode/vx_shapedata/Makefile.am @@ -21,6 +21,7 @@ libvx_shapedata_a_SOURCES = \ mode_columns.h \ mode_field_info.cc mode_field_info.h \ mode_conf_info.cc mode_conf_info.h \ + mode_input_data.h \ engine.cc engine.h \ ihull.cc ihull.h \ vx_shapedata.h diff --git a/src/libcode/vx_shapedata/Makefile.in b/src/libcode/vx_shapedata/Makefile.in index df218fceda..290ee7582f 100644 --- a/src/libcode/vx_shapedata/Makefile.in +++ b/src/libcode/vx_shapedata/Makefile.in @@ -369,6 +369,7 @@ libvx_shapedata_a_SOURCES = \ mode_columns.h \ mode_field_info.cc mode_field_info.h \ mode_conf_info.cc mode_conf_info.h \ + mode_input_data.h \ engine.cc engine.h \ ihull.cc ihull.h \ vx_shapedata.h diff --git a/src/libcode/vx_shapedata/engine.cc b/src/libcode/vx_shapedata/engine.cc index e81c2d21bc..6087bf62bd 100644 --- a/src/libcode/vx_shapedata/engine.cc +++ b/src/libcode/vx_shapedata/engine.cc @@ -144,6 +144,7 @@ void ModeFuzzyEngine::init_from_scratch() { // // Reset all fcst and obs processing flags to initial state // + need_fcst_conv = true; need_fcst_thresh = true; need_fcst_filter = true; @@ -964,9 +965,7 @@ void ModeFuzzyEngine::do_obs_merging(const char *default_config, /////////////////////////////////////////////////////////////////////// -void ModeFuzzyEngine::do_fcst_merging(const char *default_config, - const char *merge_config, - const ShapeData &merge_data) +void ModeFuzzyEngine::do_fcst_merging(const ShapeData &merge_data) { if(need_fcst_thresh) do_fcst_thresholding(); @@ -985,7 +984,7 @@ void ModeFuzzyEngine::do_fcst_merging(const char *default_config, if(conf_info.Fcst->merge_flag == MergeType_Both || conf_info.Fcst->merge_flag == MergeType_Engine) - do_fcst_merge_engine(default_config, merge_config); + do_fcst_merge_engine("", ""); // // Done @@ -1000,9 +999,7 @@ void ModeFuzzyEngine::do_fcst_merging(const char *default_config, /////////////////////////////////////////////////////////////////////// -void ModeFuzzyEngine::do_obs_merging(const char *default_config, - const char *merge_config, - const ShapeData &merge_data) +void ModeFuzzyEngine::do_obs_merging(const ShapeData &merge_data) { if(need_obs_thresh) do_obs_thresholding(); @@ -1014,7 +1011,6 @@ void ModeFuzzyEngine::do_obs_merging(const char *default_config, << "inconsistent array dims\n\n"; exit(1); } - if(conf_info.Obs->merge_flag == MergeType_Both || conf_info.Obs->merge_flag == MergeType_Thresh) @@ -1022,7 +1018,7 @@ void ModeFuzzyEngine::do_obs_merging(const char *default_config, if(conf_info.Obs->merge_flag == MergeType_Both || conf_info.Obs->merge_flag == MergeType_Engine) - do_obs_merge_engine(default_config, merge_config); + do_obs_merge_engine("", ""); // // Done @@ -1724,8 +1720,8 @@ void ModeFuzzyEngine::do_fcst_merge_engine(const char *default_config, fcst_engine->ctable = ctable; if(default_config && merge_config) { fcst_engine->conf_info.read_config(default_config, merge_config); - fcst_engine->conf_info.process_config(conf_info.Fcst->var_info->file_type(), - conf_info.Obs->var_info->file_type()); + fcst_engine->conf_info.process_config_traditional(conf_info.Fcst->var_info->file_type(), + conf_info.Obs->var_info->file_type()); path = replace_path(fcst_engine->conf_info.object_pi.color_table.c_str()); fcst_engine->ctable.read(path.c_str()); } @@ -1891,8 +1887,8 @@ void ModeFuzzyEngine::do_obs_merge_engine(const char *default_config, obs_engine->ctable = ctable; if(default_config && merge_config) { obs_engine->conf_info.read_config(default_config, merge_config); - obs_engine->conf_info.process_config(conf_info.Fcst->var_info->file_type(), - conf_info.Obs->var_info->file_type()); + obs_engine->conf_info.process_config_traditional(conf_info.Fcst->var_info->file_type(), + conf_info.Obs->var_info->file_type()); path = replace_path(obs_engine->conf_info.object_pi.color_table.c_str()); obs_engine->ctable.read(path.c_str()); } diff --git a/src/libcode/vx_shapedata/engine.h b/src/libcode/vx_shapedata/engine.h index 8bc5995dfb..50d67628cf 100644 --- a/src/libcode/vx_shapedata/engine.h +++ b/src/libcode/vx_shapedata/engine.h @@ -215,16 +215,19 @@ class ModeFuzzyEngine { void do_fcst_merging(); void do_obs_merging(); + // traditional version void do_fcst_merging(const char *default_config, const char *merge_config); - void do_fcst_merging(const char *default_config, - const char *merge_config, - const ShapeData &merge_data); + + // multivar version + void do_fcst_merging(const ShapeData &merge_data); + + // traditional version void do_obs_merging(const char *default_config, const char *merge_config); - void do_obs_merging(const char *default_config, - const char *merge_config, - const ShapeData &merge_data); + + // multivar version + void do_obs_merging(const ShapeData &merge_data); void do_matching(); diff --git a/src/libcode/vx_shapedata/mode_conf_info.cc b/src/libcode/vx_shapedata/mode_conf_info.cc index a2ebee3c5b..9008152e6f 100644 --- a/src/libcode/vx_shapedata/mode_conf_info.cc +++ b/src/libcode/vx_shapedata/mode_conf_info.cc @@ -17,6 +17,7 @@ using namespace std; #include #include #include +#include #include "mode_conf_info.h" #include "configobjecttype_to_string.h" @@ -51,6 +52,16 @@ ModeConfInfo::~ModeConfInfo() clear(); } +ModeConfInfo & ModeConfInfo::operator=(const ModeConfInfo &s) +{ + if(this == &s) return(*this); + + clear(); + assign(s); + + return(*this); +} + //////////////////////////////////////////////////////////////////////// void ModeConfInfo::init_from_scratch() @@ -77,6 +88,99 @@ void ModeConfInfo::init_from_scratch() //////////////////////////////////////////////////////////////////////// +void ModeConfInfo::assign( const ModeConfInfo &m) +{ + Field_Index_f = m.Field_Index_f; + Field_Index_o = m.Field_Index_o; + N_fields_f = m.N_fields_f; + N_fields_o = m.N_fields_o; + fcst_multivar_logic = m.fcst_multivar_logic; + obs_multivar_logic = m.obs_multivar_logic; + fcst_multivar_compare_index = m.fcst_multivar_compare_index; + obs_multivar_compare_index = m.obs_multivar_compare_index; + fcst_multivar_name = m.fcst_multivar_name; + fcst_multivar_level = m.fcst_multivar_level; + obs_multivar_name = m.obs_multivar_name; + obs_multivar_level = m.obs_multivar_level; + data_type = m.data_type; + conf = m.conf; + centroid_dist_wt = m.centroid_dist_wt; + boundary_dist_wt = m.boundary_dist_wt; + convex_hull_dist_wt = m.convex_hull_dist_wt; + angle_diff_wt = m.angle_diff_wt; + aspect_diff_wt = m.aspect_diff_wt; + area_ratio_wt = m.area_ratio_wt; + int_area_ratio_wt = m.int_area_ratio_wt; + curvature_ratio_wt = m.curvature_ratio_wt; + complexity_ratio_wt = m.complexity_ratio_wt; + inten_perc_ratio_wt = m.inten_perc_ratio_wt; + + fcst_array = 0; + Fcst = 0; + if (N_fields_f > 0) { + fcst_array = new Mode_Field_Info[N_fields_f]; + for (int i=0; i 0) { + obs_array = new Mode_Field_Info[N_fields_o]; + for (int i=0; ilookup_int(conf_key_shift_right); - read_fields (fcst_array, fcst_dict, ftype, 'F'); // the order is important here - read_fields ( obs_array, obs_dict, otype, 'O'); // the order is important here - Fcst = fcst_array; // + 0 - Obs = obs_array; // + 0 + if (field_index == 0) { + read_fields_0 (fcst_array, fcst_dict, ftype, 'F'); + read_fields_1 (fcst_array, fcst_dict, ftype, 'F', 0); + read_fields_0 (obs_array, obs_dict, otype, 'O'); + read_fields_1 (obs_array, obs_dict, otype, 'O', 0); + fcst_array->raw_pi = parse_conf_plot_info(conf.lookup_dictionary(conf_key_fcst_raw_plot)); + obs_array->raw_pi = parse_conf_plot_info(conf.lookup_dictionary(conf_key_obs_raw_plot)); + + } else { + read_fields_1 (fcst_array, fcst_dict, ftype, 'F', field_index); + read_fields_1 (obs_array, obs_dict, otype, 'O', field_index); + } + + Fcst = fcst_array + field_index; + Obs = obs_array + field_index; // Dump the contents of the VarInfo objects if(mlog.verbosity_level() >= 5) { - for (j=0; jdump(cout); - } - for (j=0; jdump(cout); - } // for j - } - for (j=0; jdump(cout); + mlog << Debug(5) + << "Parsed observation field:\n"; + obs_array[field_index].var_info->dump(cout); } - for (j=0; j " - << "fcst and obs convolution radius arrays need to be the same size for traditional mode\n\n"; - exit ( 1 ); - } - if ( fcst_array[j].conv_thresh_array.n_elements() != obs_array[j].conv_thresh_array.n_elements() ) { + if ( fcst_array[field_index].conv_radius_array.n_elements() != + obs_array[field_index].conv_radius_array.n_elements() ) { + mlog << Error << "\nModeConfInfo::process_config_both() -> " + << "fcst and obs convolution radius arrays need to be the same size for traditional mode\n\n"; + exit ( 1 ); + } + if ( fcst_array[field_index].conv_thresh_array.n_elements() != + obs_array[field_index].conv_thresh_array.n_elements() ) { - mlog << Error << "\nModeConfInfo::process_config_both() -> " - << "fcst and obs convolution threshold arrays need to be the same size\n\n"; - exit ( 1 ); - } + mlog << Error << "\nModeConfInfo::process_config_both() -> " + << "fcst and obs convolution threshold arrays need to be the same size\n\n"; + exit ( 1 ); } } - // Conf: fcst_raw_plot - - fcst_array->raw_pi = parse_conf_plot_info(conf.lookup_dictionary(conf_key_fcst_raw_plot)); - - // Conf: obs_raw_plot - - obs_array->raw_pi = parse_conf_plot_info(conf.lookup_dictionary(conf_key_obs_raw_plot)); - - return; + return; } //////////////////////////////////////////////////////////////////////// -void ModeConfInfo::process_config_fcst(GrdFileType ftype) +void ModeConfInfo::process_config_fcst(GrdFileType ftype, int field_index) { int j, k, n; @@ -501,25 +613,26 @@ void ModeConfInfo::process_config_fcst(GrdFileType ftype) fcst_dict = conf.lookup_dictionary(conf_key_fcst); shift_right = fcst_dict->lookup_int(conf_key_shift_right); - read_fields (fcst_array, fcst_dict, ftype, 'F'); // the order is important here - Fcst = fcst_array; // + 0 + if (field_index == 0) { + read_fields_0 (fcst_array, fcst_dict, ftype, 'F'); + read_fields_1 (fcst_array, fcst_dict, ftype, 'F', 0); + fcst_array->raw_pi = parse_conf_plot_info(conf.lookup_dictionary(conf_key_fcst_raw_plot)); + } else { + read_fields_1 (fcst_array, fcst_dict, ftype, 'F', field_index); + } + + Fcst = fcst_array + field_index; - // Dump the contents of the VarInfo objects + // Dump the contents of the VarInfo object if(mlog.verbosity_level() >= 5) { - for (j=0; jdump(cout); - } // for j + mlog << Debug(5) + << "Parsed forecast field:\n"; + fcst_array[field_index].var_info->dump(cout); } - for (j=0; jraw_pi = parse_conf_plot_info(conf.lookup_dictionary(conf_key_fcst_raw_plot)); + evaluate_fcst_settings(field_index); return; @@ -528,7 +641,7 @@ void ModeConfInfo::process_config_fcst(GrdFileType ftype) //////////////////////////////////////////////////////////////////////// -void ModeConfInfo::process_config_obs(GrdFileType otype) +void ModeConfInfo::process_config_obs(GrdFileType otype, int field_index) { int j, k, n; @@ -537,25 +650,25 @@ void ModeConfInfo::process_config_obs(GrdFileType otype) obs_dict = conf.lookup_dictionary(conf_key_obs); shift_right = obs_dict->lookup_int(conf_key_shift_right); - read_fields ( obs_array, obs_dict, otype, 'O'); // the order is important here - Obs = obs_array; // + 0 + if (field_index == 0) { + read_fields_0 (obs_array, obs_dict, otype, 'O'); + read_fields_1 (obs_array, obs_dict, otype, 'O', 0); + obs_array->raw_pi = parse_conf_plot_info(conf.lookup_dictionary(conf_key_obs_raw_plot)); + } else { + read_fields_1 (obs_array, obs_dict, otype, 'O', field_index); + } + Obs = obs_array + field_index; - // Dump the contents of the VarInfo objects + // Dump the contents of the VarInfo object if(mlog.verbosity_level() >= 5) { - for (j=0; jdump(cout); - } // for j + mlog << Debug(5) + << "Parsed observation field:\n"; + obs_array[field_index].var_info->dump(cout); } - for (j=0; jraw_pi = parse_conf_plot_info(conf.lookup_dictionary(conf_key_obs_raw_plot)); + evaluate_obs_settings(field_index); return; @@ -563,6 +676,140 @@ void ModeConfInfo::process_config_obs(GrdFileType otype) //////////////////////////////////////////////////////////////////////// +void ModeConfInfo::config_set_all_percentile_thresholds(const std::vector &fdata, + const std::vector &odata) +{ + + // for each forecast input, it's either not a percentile threshold, + // is a simple (single input) percentile, is a frequency bias percentile threshold, or is a + // climatology percentile (we don't have climatology). + // + // As a complication we have both conv_thresh and merge_thresh, so what if they are different? + // right now I simply go with frequency bias as highest priority + + // indices of forecast and obs inputs that have frequency bias percentile thresholding + vector fcst_freq, obs_freq; + + // indices (common to forecast and obs) that require both inputs (fcst and obs) + // which is either frequency bias, or sample obs with a forecast input, or sample fcst + // with an obs input + vector indices_with_both; + + for (int j=0; j= N_fields_o) { + mlog << Error << "\nModeConfInfo::config_set_all_percentile_thresholds\n" + << " SOP Thresholding on fcst index " << j+1 + << " out of range of obs " << N_fields_o + 1 << "\n\n"; + exit ( 1 ); + } + // deal with this later + indices_with_both.push_back(j); + break; + case perc_thresh_freq_bias: + // currently expect matching index in obs and fcst, so check to make sure not both + if (j >= N_fields_o) { + mlog << Error << "\nModeConfInfo::config_set_all_percentile_thresholds\n" + << " FBIAS Thresholding on fcst index " << j+1 + << " out of range of obs " << N_fields_o + 1 << "\n\n"; + exit ( 1 ); + } + fcst_freq.push_back(j); + indices_with_both.push_back(j); + break; + default: + break; + } + } + for (int j=0; j= N_fields_f) { + mlog << Error << "\nModeConfInfo::config_set_all_percentile_thresholds\n" + << " SFP Thresholding on obs index " << j+1 + << " out of range of fcst " << N_fields_f + 1 << "\n\n"; + exit ( 1 ); + } + // deal with this later + if (find(indices_with_both.begin(), indices_with_both.end(), j) == indices_with_both.end()) { + indices_with_both.push_back(j); + } + break; + case perc_thresh_freq_bias: + // currently expect matching index in obs and fcst, so check to make sure not both + if (j >= N_fields_f) { + mlog << Error << "\nModeConfInfo::config_set_all_percentile_thresholds\n" + << " FBIAS Thresholding on obs index " << j+1 + << " out of range of fcst " << N_fields_f + 1 << "\n\n"; + exit ( 1 ); + } + // deal with this later + obs_freq.push_back(j); + if (find(indices_with_both.begin(), indices_with_both.end(), j) == indices_with_both.end()) { + indices_with_both.push_back(j); + } + break; + default: + break; + } + } + + // make sure no overlap in fcst/obs frequencies, as that's a no no + for (size_t i=0; iis_array() ) { exit ( 1 ); } + } - info_array[j].set(true, j, e->dict_value(), &conf, type, _fo, false); + return; - if ( j == 0 ) { +} // if is array - for (k=1; klookup(conf_key_field); + if ( !ee ) { + + mlog << "\nModeConfInfo::read_fields () -> \"field\" entry not found in dictionary!\n\n"; + + exit ( 1 ); + +} + +const Dictionary * field = ee->dict(); + +const int N = ( (field->is_array()) ? (field->n_entries()) : 1 ); + +if ( field->is_array() ) { + + const DictionaryEntry * e = 0; + const Dictionary & D = *field; + + e = D[field_index]; + if ( (e->type() != DictionaryType) && (e->type() != ArrayType) ) { + mlog << Error + << "\nModeConfInfo::read_fields() -> field entry # " << field_index+1 << " is not a dictionary!\n\n"; + exit ( 1 ); + + } + + info_array[field_index].set(true, field_index, e->dict_value(), &conf, type, _fo, false); return; } // if is array - // // nope, traditional mode // -N_fields_f = N; -N_fields_o = N; + if (field_index != 0) { + mlog << Error + << "\nModeConfInfo::read_fields() -> traditional mode but looking for a field array index " << field_index + << "\n\n"; + exit ( 1 ); + } + + info_array[0].set(false, 0, dict, &conf, type, _fo); // @@ -1287,6 +1582,46 @@ void ModeConfInfo::set_data_type(ModeDataType type) //////////////////////////////////////////////////////////////////////// +GrdFileType ModeConfInfo::file_type_for_field(bool isFcst, int field_index) +{ + // look at the dictionary for the obs or forecast at index, with + // parents + + Dictionary * dict = (Dictionary *) 0; + if (isFcst) { + dict = conf.lookup_dictionary(conf_key_fcst); + } else { + dict = conf.lookup_dictionary(conf_key_obs); + } + + const DictionaryEntry *e = dict->lookup(conf_key_field); + if ( !e ) { + mlog << "\nModeConfInfo::read_fields () -> \"field\" entry not found in dictionary!\n\n"; + exit ( 1 ); + } + + const Dictionary * field = e->dict(); + const int N = ( (field->is_array()) ? (field->n_entries()) : 1 ); + if (field_index < 0 || field_index >= N) { + mlog << Error + << "\nModeConfInfo::file_type_for_field() -> " + << "index " << field_index+1 << " out of range 0 to " << N + << "\n\n"; + exit (1); + } + e = (*field)[field_index]; + if ( (e->type() != DictionaryType) && (e->type() != ArrayType) ) { + mlog << Error + << "\nModeConfInfo::file_type_for_field() -> " + << "field entry # " << field_index+1 << " is not a dictionary!\n\n"; + exit ( 1 ); + } + Dictionary *dictf = e->dict_value(); + return parse_conf_file_type(dictf); +} + +//////////////////////////////////////////////////////////////////////// + int ModeConfInfo::n_runs() const @@ -1406,7 +1741,12 @@ void ModeConfInfo::check_multivar_not_implemented() << "\nModeConfInfo::check_multivar_not_implemented():\n" << " merge_flag ENGINE or BOTH not implemented for multivariate mode\n\n"; status = true; - break; + } + if (fcst_array[i].conv_thresh_array.n() > 1 || fcst_array[i].merge_thresh_array.n() > 1) { + mlog << Error + << "\nModeConfInfo::check_multivar_not_implemented():\n" + << " more than one conv_thresh or merge_thresh per input is not allowed in multivariate mode\n\n"; + status = true; } } } @@ -1419,6 +1759,12 @@ void ModeConfInfo::check_multivar_not_implemented() status = true; break; } + if (obs_array[i].conv_thresh_array.n() > 1 || obs_array[i].merge_thresh_array.n() > 1) { + mlog << Error + << "\nModeConfInfo::check_multivar_not_implemented():\n" + << " more than one conv_thresh or merge_thresh per input is not allowed in multivariate mode\n\n"; + status = true; + } } } @@ -1433,52 +1779,43 @@ void ModeConfInfo::check_multivar_not_implemented() //////////////////////////////////////////////////////////////////////// -void ModeConfInfo::check_multivar_perc_thresh(bool isSimple, bool isSimpleMerge) const +PercThreshType ModeConfInfo::perctype(const Mode_Field_Info &f) const { - if (isSimple) { - if (data_type == ModeDataType_MvMode_Fcst) { - for (int j=0; j< Fcst->conv_thresh_array.n(); ++j) { - if (Fcst->conv_thresh_array[j].get_ptype() == perc_thresh_sample_obs) { - mlog << Warning - << "\nModeConfInfo::check_multivar_perc_thresh:\n" - << " Thresholding with 'SOP' in a forecast input not well defined for multivariate mode simple object creation\n" - << " 'SFP' will be used as a replacement\n\n"; - } - } - } else if (data_type == ModeDataType_MvMode_Obs) { - for (int j=0; j< Obs->conv_thresh_array.n(); ++j) { - if (Obs->conv_thresh_array[j].get_ptype() == perc_thresh_sample_fcst) { - mlog << Warning - << "\nModeConfInfo::check_multivar_perc_thresh:\n" - << " Thresholding with 'SFP' in an obs input not well defined for multivariate mode simple object creation\n" - << " 'SOP' will be used as a replacement\n\n"; - } - } - } + PercThreshType pm=no_perc_thresh_type;; + PercThreshType pc=no_perc_thresh_type;; + if (f.merge_thresh_array.n() > 0) { + pm = f.merge_thresh_array[0].get_ptype(); } - - if (isSimpleMerge) { - if (data_type == ModeDataType_MvMode_Fcst) { - for (int j=0; j< Fcst->merge_thresh_array.n(); ++j) { - if (Fcst->merge_thresh_array[j].get_ptype() == perc_thresh_sample_obs) { - mlog << Warning - << "\nModeConfInfo::check_multivar_perc_thresh:\n" - << " Thresholding with 'SOP' in a forecast input not well defined for multivariate mode simple object creation\n" - << " 'SFP' will be used as a replacement\n\n"; - } - } - } else if (data_type == ModeDataType_MvMode_Obs) { - for (int j=0; j< Obs->merge_thresh_array.n(); ++j) { - if (Obs->merge_thresh_array[j].get_ptype() == perc_thresh_sample_fcst) { - mlog << Warning - << "\nModeConfInfo::check_multivar_perc_thresh():\n" - << " Thresholding with 'SFP' in an obs input not well defined for multivariate mode simple object creation\n" - << " 'SOP' will be used as a replacement\n\n"; - } - } - } + if (f.conv_thresh_array.n() > 0) { + pc = f.conv_thresh_array[0].get_ptype(); } -} + if (pm == perc_thresh_sample_climo || pc == perc_thresh_sample_climo) { + mlog << Error << "\nModeConfInfo::perctype()\n" + << " Thresholding with 'SCP' in an input not implemented for multivariate mode\n\n"; + exit ( 1 ); + } + if (pm == perc_thresh_climo_dist || pc == perc_thresh_climo_dist) { + mlog << Error << "\nModeConfInfo::perctype()\n" + << " Thresholding with 'CDP' in an input not implemented for multivariate mode\n\n"; + exit ( 1 ); + } + if (pm == perc_thresh_freq_bias || + pc == perc_thresh_freq_bias) { + return perc_thresh_freq_bias; + } + // put in tests if they are not the same and + // both are sample settings, so the right one + // gets returned (pass in fcst/obs boolean) + if (pm == perc_thresh_sample_obs || + pc == perc_thresh_sample_obs) { + return perc_thresh_sample_obs; + } + if (pm == perc_thresh_sample_fcst || + pc == perc_thresh_sample_fcst) { + return perc_thresh_sample_fcst; + } + return no_perc_thresh_type; +} //////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_shapedata/mode_conf_info.h b/src/libcode/vx_shapedata/mode_conf_info.h index 7fab368dc4..fcaca3e5a0 100644 --- a/src/libcode/vx_shapedata/mode_conf_info.h +++ b/src/libcode/vx_shapedata/mode_conf_info.h @@ -25,6 +25,7 @@ #include "mode_field_info.h" #include "mode_data_type.h" +#include "mode_input_data.h" //////////////////////////////////////////////////////////////////////// @@ -74,12 +75,17 @@ class ModeConfInfo { int N_fields_f; // for traditional mode expect N_fields_f/_o to be the same int N_fields_o; + void assign(const ModeConfInfo &); + public: ModeConfInfo(); ~ModeConfInfo(); + // attempt to implement this + ModeConfInfo & operator=(const ModeConfInfo &); + void clear(); // sets both obs and fcst field indices if traditional mode, or mvmode 'both' @@ -106,7 +112,15 @@ class ModeConfInfo { void check_multivar_not_implemented(); - void check_multivar_perc_thresh(bool isSimple, bool isSimpleMerge) const; + // check all inputs for climatology percentiles, which are not implemented + // warn about forecasts and obs set backwards + // void check_multivar_perc_thresh(const Mode_Field_Info &f, bool isFcst) const; + + + // // return index to other input if the particular input is a frequency bias percentile + // // return -1 if input is not a fequency bias percentile + // // if input is forecast, 'other' is obs, and vice versa + // int frequency_bias_other_index(bool input_is_fcst, int input_index) const; ///////////////////////////////////////////////////////////////////// @@ -122,7 +136,7 @@ class ModeConfInfo { // and to distinguish traditional mode from mvmode ModeDataType data_type; - + ///////////////////////////////////////////////////////////////////// @@ -135,9 +149,21 @@ class ModeConfInfo { void read_config (const char * default_filename, const char * user_filename); - void process_config (GrdFileType ftype, GrdFileType otype, ModeDataType dt=ModeDataType_Traditional); + void process_config_traditional(GrdFileType ftype, GrdFileType otype); - void read_fields (Mode_Field_Info * &, Dictionary * dict, GrdFileType, char _fo); + void process_config_except_fields(); + + void process_config_field (GrdFileType ftype, GrdFileType otype, ModeDataType dt, int field_index); + + void config_set_all_percentile_thresholds(const std::vector &fdata, + const std::vector &odata); + PercThreshType perctype(const Mode_Field_Info &f) const; + + // deal with zeroth field + void read_fields_0 (Mode_Field_Info * &, Dictionary * dict, GrdFileType, char _fo); + + // deal with non-zeroth field + void read_fields_1 (Mode_Field_Info * &, Dictionary * dict, GrdFileType, char _fo, int field_index); PiecewiseLinear * parse_interest_function(Dictionary * dict, const char * conf_key_if); @@ -269,12 +295,14 @@ class ModeConfInfo { void set_data_type(ModeDataType type); + GrdFileType file_type_for_field(bool isFcst, int field_index); + private: // some private methods - void process_config_both(GrdFileType ftype, GrdFileType otype); - void process_config_fcst(GrdFileType ftype); - void process_config_obs(GrdFileType otype); + void process_config_both(GrdFileType ftype, GrdFileType otype, int field_index=0); + void process_config_fcst(GrdFileType ftype, int field_index=0); + void process_config_obs(GrdFileType otype, int field_index=0); void evaluate_fcst_settings(int); void evaluate_obs_settings(int); diff --git a/src/libcode/vx_shapedata/mode_field_info.cc b/src/libcode/vx_shapedata/mode_field_info.cc index 8dd024e803..fcd652a09a 100644 --- a/src/libcode/vx_shapedata/mode_field_info.cc +++ b/src/libcode/vx_shapedata/mode_field_info.cc @@ -85,6 +85,32 @@ return ( * this ); } +//////////////////////////////////////////////////////////////////////// + +void Mode_Field_Info::clone(const Mode_Field_Info & i) +{ + dict = i.dict; + conf = i.conf; + gft = i.gft; + FO = i.FO; + Multivar = i.Multivar; + index = i.index; + conv_radius = i.conv_radius; + vld_thresh = i.vld_thresh; + + var_info = i.var_info->clone(); + + conv_radius_array = i.conv_radius_array; + conv_thresh_array = i.conv_thresh_array; + merge_thresh_array = i.merge_thresh_array; + conv_thresh = i.conv_thresh; + merge_thresh = i.merge_thresh; + merge_flag = i.merge_flag; + raw_pi = i.raw_pi; + filter_attr_map = i.filter_attr_map; + file_type = i.file_type; +} + //////////////////////////////////////////////////////////////////////// @@ -143,6 +169,8 @@ merge_thresh.clear(); merge_flag = MergeType_Engine; +file_type = FileType_None; + // raw_pi.clear(); } @@ -310,8 +338,8 @@ if ( FO == 'F' ) raw_pi = parse_conf_plot_info(conf->lookup_dictionary(conf_key else raw_pi = parse_conf_plot_info(conf->lookup_dictionary(conf_key_obs_raw_plot)); - - +file_type = parse_conf_file_type(dict); + // // done // diff --git a/src/libcode/vx_shapedata/mode_field_info.h b/src/libcode/vx_shapedata/mode_field_info.h index 132f2df4b2..bb7e08cb57 100644 --- a/src/libcode/vx_shapedata/mode_field_info.h +++ b/src/libcode/vx_shapedata/mode_field_info.h @@ -42,10 +42,9 @@ class Mode_Field_Info { void assign(const Mode_Field_Info &); + Dictionary * dict; // not allocated, based on reading the config file - Dictionary * dict; // not allocated - - MetConfig * conf; // not allocated + MetConfig * conf; // not allocated, based on reading the config file GrdFileType gft; @@ -59,6 +58,7 @@ class Mode_Field_Info { ~Mode_Field_Info(); Mode_Field_Info(const Mode_Field_Info &); Mode_Field_Info & operator=(const Mode_Field_Info &); + void clone(const Mode_Field_Info &); void clear(); @@ -85,6 +85,8 @@ class Mode_Field_Info { PlotInfo raw_pi; // Raw forecast plotting info + GrdFileType file_type; // each field can have a configured file type + // // member functions // diff --git a/src/libcode/vx_shapedata/mode_input_data.h b/src/libcode/vx_shapedata/mode_input_data.h new file mode 100644 index 0000000000..0aa9b4c36d --- /dev/null +++ b/src/libcode/vx_shapedata/mode_input_data.h @@ -0,0 +1,42 @@ +// ** Copyright UCAR (c) 1992 - 2023 +// ** University Corporation for Atmospheric Research (UCAR) +// ** National Center for Atmospheric Research (NCAR) +// ** Research Applications Lab (RAL) +// ** P.O.Box 3000, Boulder, Colorado, 80307-3000, USA +// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* + + +//////////////////////////////////////////////////////////////////////// + + +#ifndef __MODE_INPUT_DATA_H__ +#define __MODE_INPUT_DATA_H__ + +#include +#include "data_plane.h" +#include "grid_base.h" + +//////////////////////////////////////////////////////////////////////// + + +class ModeInputData { + + private: + + public: + + ModeInputData(const std::string &name, const DataPlane &dp, const Grid &g) : + _name(name), _dataPlane(dp), _grid(g) {} + + ~ModeInputData() {} + + std::string _name; + DataPlane _dataPlane; + Grid _grid; +}; + + +#endif + + +///////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_summary/summary_calc.cc b/src/libcode/vx_summary/summary_calc.cc index 35e6822538..9f05153c32 100644 --- a/src/libcode/vx_summary/summary_calc.cc +++ b/src/libcode/vx_summary/summary_calc.cc @@ -10,12 +10,13 @@ //////////////////////////////////////////////////////////////////////// -using namespace std; - #include #include "summary_calc.h" +using namespace std; + + //////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_summary/summary_calc_max.cc b/src/libcode/vx_summary/summary_calc_max.cc index 6ea4f77a70..d796bd1589 100644 --- a/src/libcode/vx_summary/summary_calc_max.cc +++ b/src/libcode/vx_summary/summary_calc_max.cc @@ -10,12 +10,13 @@ //////////////////////////////////////////////////////////////////////// -using namespace std; - #include #include "summary_calc_max.h" +using namespace std; + + //////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_summary/summary_calc_max.h b/src/libcode/vx_summary/summary_calc_max.h index 11d897eb7a..1196f12ba1 100644 --- a/src/libcode/vx_summary/summary_calc_max.h +++ b/src/libcode/vx_summary/summary_calc_max.h @@ -32,7 +32,7 @@ class SummaryCalcMax : public SummaryCalc SummaryCalcMax(); virtual ~SummaryCalcMax(); - virtual string getType() const + virtual std::string getType() const { return "MAX"; } diff --git a/src/libcode/vx_summary/summary_calc_mean.cc b/src/libcode/vx_summary/summary_calc_mean.cc index 5ddb2f2e10..aa384a9c7a 100644 --- a/src/libcode/vx_summary/summary_calc_mean.cc +++ b/src/libcode/vx_summary/summary_calc_mean.cc @@ -10,12 +10,13 @@ //////////////////////////////////////////////////////////////////////// -using namespace std; - #include #include "summary_calc_mean.h" +using namespace std; + + //////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_summary/summary_calc_mean.h b/src/libcode/vx_summary/summary_calc_mean.h index 034cf14784..4144dbfe75 100644 --- a/src/libcode/vx_summary/summary_calc_mean.h +++ b/src/libcode/vx_summary/summary_calc_mean.h @@ -32,7 +32,7 @@ class SummaryCalcMean : public SummaryCalc SummaryCalcMean(); virtual ~SummaryCalcMean(); - virtual string getType() const + virtual std::string getType() const { return "MEAN"; } diff --git a/src/libcode/vx_summary/summary_calc_median.cc b/src/libcode/vx_summary/summary_calc_median.cc index f87f94c0f3..c027253ac6 100644 --- a/src/libcode/vx_summary/summary_calc_median.cc +++ b/src/libcode/vx_summary/summary_calc_median.cc @@ -10,13 +10,14 @@ //////////////////////////////////////////////////////////////////////// -using namespace std; - #include #include #include "summary_calc_median.h" +using namespace std; + + //////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_summary/summary_calc_min.cc b/src/libcode/vx_summary/summary_calc_min.cc index 12c0bd9b53..8c998e3837 100644 --- a/src/libcode/vx_summary/summary_calc_min.cc +++ b/src/libcode/vx_summary/summary_calc_min.cc @@ -10,12 +10,13 @@ //////////////////////////////////////////////////////////////////////// -using namespace std; - #include #include "summary_calc_min.h" +using namespace std; + + //////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_summary/summary_calc_min.h b/src/libcode/vx_summary/summary_calc_min.h index b1ed0277de..9b17e61dd0 100644 --- a/src/libcode/vx_summary/summary_calc_min.h +++ b/src/libcode/vx_summary/summary_calc_min.h @@ -32,7 +32,7 @@ class SummaryCalcMin : public SummaryCalc SummaryCalcMin(); virtual ~SummaryCalcMin(); - virtual string getType() const + virtual std::string getType() const { return "MIN"; } diff --git a/src/libcode/vx_summary/summary_calc_percentile.cc b/src/libcode/vx_summary/summary_calc_percentile.cc index c6a0b4d288..26ef0ba620 100644 --- a/src/libcode/vx_summary/summary_calc_percentile.cc +++ b/src/libcode/vx_summary/summary_calc_percentile.cc @@ -10,13 +10,14 @@ //////////////////////////////////////////////////////////////////////// -using namespace std; - #include #include #include "summary_calc_percentile.h" +using namespace std; + + //////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_summary/summary_calc_percentile.h b/src/libcode/vx_summary/summary_calc_percentile.h index 353ef963ae..1dcfc4bb3f 100644 --- a/src/libcode/vx_summary/summary_calc_percentile.h +++ b/src/libcode/vx_summary/summary_calc_percentile.h @@ -29,10 +29,10 @@ class SummaryCalcPercentile : public SummaryCalc public: - SummaryCalcPercentile(const string &type_string); + SummaryCalcPercentile(const std::string &type_string); virtual ~SummaryCalcPercentile(); - virtual string getType() const + virtual std::string getType() const { return _type; } @@ -49,7 +49,7 @@ class SummaryCalcPercentile : public SummaryCalc protected: double _percentile; - string _type; + std::string _type; }; diff --git a/src/libcode/vx_summary/summary_calc_range.cc b/src/libcode/vx_summary/summary_calc_range.cc index ca207ed396..83da9fb483 100644 --- a/src/libcode/vx_summary/summary_calc_range.cc +++ b/src/libcode/vx_summary/summary_calc_range.cc @@ -10,12 +10,12 @@ //////////////////////////////////////////////////////////////////////// -using namespace std; - #include #include "summary_calc_range.h" +using namespace std; + //////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_summary/summary_calc_range.h b/src/libcode/vx_summary/summary_calc_range.h index 043cf882a2..fbb6bdfbbf 100644 --- a/src/libcode/vx_summary/summary_calc_range.h +++ b/src/libcode/vx_summary/summary_calc_range.h @@ -32,7 +32,7 @@ class SummaryCalcRange : public SummaryCalc SummaryCalcRange(); virtual ~SummaryCalcRange(); - virtual string getType() const + virtual std::string getType() const { return "RANGE"; } diff --git a/src/libcode/vx_summary/summary_calc_stdev.cc b/src/libcode/vx_summary/summary_calc_stdev.cc index 87e0a79f48..07e2d1d81c 100644 --- a/src/libcode/vx_summary/summary_calc_stdev.cc +++ b/src/libcode/vx_summary/summary_calc_stdev.cc @@ -10,12 +10,12 @@ //////////////////////////////////////////////////////////////////////// -using namespace std; - #include #include "summary_calc_stdev.h" +using namespace std; + //////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_summary/summary_calc_stdev.h b/src/libcode/vx_summary/summary_calc_stdev.h index 6076839c7a..db02988e30 100644 --- a/src/libcode/vx_summary/summary_calc_stdev.h +++ b/src/libcode/vx_summary/summary_calc_stdev.h @@ -32,7 +32,7 @@ class SummaryCalcStdev : public SummaryCalc SummaryCalcStdev(); virtual ~SummaryCalcStdev(); - virtual string getType() const + virtual std::string getType() const { return "SDEV"; } diff --git a/src/libcode/vx_summary/summary_calc_sum.cc b/src/libcode/vx_summary/summary_calc_sum.cc index 79587f0f80..daed7306df 100644 --- a/src/libcode/vx_summary/summary_calc_sum.cc +++ b/src/libcode/vx_summary/summary_calc_sum.cc @@ -10,12 +10,12 @@ //////////////////////////////////////////////////////////////////////// -using namespace std; - #include #include "summary_calc_sum.h" +using namespace std; + //////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_summary/summary_calc_sum.h b/src/libcode/vx_summary/summary_calc_sum.h index daec131260..d3e149c22d 100644 --- a/src/libcode/vx_summary/summary_calc_sum.h +++ b/src/libcode/vx_summary/summary_calc_sum.h @@ -32,7 +32,7 @@ class SummaryCalcSum : public SummaryCalc SummaryCalcSum(); virtual ~SummaryCalcSum(); - virtual string getType() const + virtual std::string getType() const { return "SUM"; } diff --git a/src/libcode/vx_summary/summary_key.cc b/src/libcode/vx_summary/summary_key.cc index 9e6d1e519b..62b207dcb1 100644 --- a/src/libcode/vx_summary/summary_key.cc +++ b/src/libcode/vx_summary/summary_key.cc @@ -10,10 +10,10 @@ //////////////////////////////////////////////////////////////////////// -using namespace std; +#include "summary_key.h" -#include "summary_key.h" +using namespace std; //////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_summary/summary_key.h b/src/libcode/vx_summary/summary_key.h index 84716eabf5..ee693a6bfe 100644 --- a/src/libcode/vx_summary/summary_key.h +++ b/src/libcode/vx_summary/summary_key.h @@ -27,12 +27,12 @@ class SummaryKey public: - SummaryKey(const string &header_type, - const string &station_id, + SummaryKey(const std::string &header_type, + const std::string &station_id, const double lat, const double lon, const double elev, const int var_code, const double height_m, const double pressure_level, - const string &var_name = ""); + const std::string &var_name = ""); virtual ~SummaryKey(); @@ -41,12 +41,12 @@ class SummaryKey // Access methods // //////////////////// - string getHeaderType() const + std::string getHeaderType() const { return _headerType; } - string getStationId() const + std::string getStationId() const { return _stationId; } @@ -91,7 +91,7 @@ class SummaryKey return _pressureLevel; } - string getVarName() const + std::string getVarName() const { return _varName; } @@ -154,8 +154,8 @@ class SummaryKey // Protected members // /////////////////////// - string _headerType; - string _stationId; + std::string _headerType; + std::string _stationId; double _latitude; double _longitude; double _elevation; @@ -163,7 +163,7 @@ class SummaryKey int hdrIndex; double _height; double _pressureLevel; - string _varName; + std::string _varName; }; diff --git a/src/libcode/vx_summary/summary_obs.cc b/src/libcode/vx_summary/summary_obs.cc index d1f7336421..8aa8f4a3eb 100644 --- a/src/libcode/vx_summary/summary_obs.cc +++ b/src/libcode/vx_summary/summary_obs.cc @@ -10,8 +10,6 @@ //////////////////////////////////////////////////////////////////////// -using namespace std; - #include #include #include @@ -29,6 +27,9 @@ using namespace std; #include "summary_key.h" #include "summary_obs.h" +using namespace std; + + //////////////////////////////////////////////////////////////////////// SummaryObs::SummaryObs(): diff --git a/src/libcode/vx_summary/time_summary_interval.cc b/src/libcode/vx_summary/time_summary_interval.cc index 73a3224deb..c1d8c8a656 100644 --- a/src/libcode/vx_summary/time_summary_interval.cc +++ b/src/libcode/vx_summary/time_summary_interval.cc @@ -10,10 +10,10 @@ //////////////////////////////////////////////////////////////////////// -using namespace std; +#include "time_summary_interval.h" -#include "time_summary_interval.h" +using namespace std; //////////////////////////////////////////////////////////////////////// diff --git a/src/tools/core/grid_stat/grid_stat.cc b/src/tools/core/grid_stat/grid_stat.cc index 67b8379d0f..8489ca9963 100644 --- a/src/tools/core/grid_stat/grid_stat.cc +++ b/src/tools/core/grid_stat/grid_stat.cc @@ -111,6 +111,7 @@ // 053 12/11/21 Halley Gotway MET #1991 Fix VCNT output. // 054 07/06/22 Howard Soh METplus-Internal #19 Rename main to met_main // 055 10/03/22 Prestopnik MET #2227 Remove using namespace netCDF from header files +// 056 01/29/24 Halley Gotway MET #2801 Configure time difference warnings // //////////////////////////////////////////////////////////////////////// @@ -745,12 +746,21 @@ void process_scores() { // Check that the valid times match if(fcst_dp.valid() != obs_dp.valid()) { - mlog << Warning << "\nprocess_scores() -> " - << "Forecast and observation valid times do not match " - << unix_to_yyyymmdd_hhmmss(fcst_dp.valid()) << " != " - << unix_to_yyyymmdd_hhmmss(obs_dp.valid()) << " for " - << conf_info.vx_opt[i].fcst_info->magic_str() << " versus " - << conf_info.vx_opt[i].obs_info->magic_str() << ".\n\n"; + cs << cs_erase + << "Forecast and observation valid times do not match (" + << unix_to_yyyymmdd_hhmmss(fcst_dp.valid()) << " != " + << unix_to_yyyymmdd_hhmmss(obs_dp.valid()) << ") for " + << conf_info.vx_opt[i].fcst_info->magic_str() << " versus " + << conf_info.vx_opt[i].obs_info->magic_str() << "."; + + if(conf_info.conf.time_offset_warning( + (int) (fcst_dp.valid() - obs_dp.valid()))) { + mlog << Warning << "\nprocess_scores() -> " + << cs << "\n\n"; + } + else { + mlog << Debug(3) << cs << "\n"; + } } // Check that the accumulation intervals match @@ -760,9 +770,9 @@ void process_scores() { mlog << Warning << "\nprocess_scores() -> " << "Forecast and observation accumulation times " - << "do not match " << sec_to_hhmmss(fcst_dp.accum()) + << "do not match (" << sec_to_hhmmss(fcst_dp.accum()) << " != " << sec_to_hhmmss(obs_dp.accum()) - << " for " << conf_info.vx_opt[i].fcst_info->magic_str() + << ") for " << conf_info.vx_opt[i].fcst_info->magic_str() << " versus " << conf_info.vx_opt[i].obs_info->magic_str() << ".\n\n"; } diff --git a/src/tools/core/mode/Makefile.am b/src/tools/core/mode/Makefile.am index d8dc507385..34bb6cc8dd 100644 --- a/src/tools/core/mode/Makefile.am +++ b/src/tools/core/mode/Makefile.am @@ -13,6 +13,7 @@ include ${top_srcdir}/Make-include bin_PROGRAMS = mode mode_SOURCES = mode_usage.cc \ mode_frontend.cc \ + mode_superobject.cc \ multivar_data.cc \ multivar_frontend.cc \ mode.cc \ diff --git a/src/tools/core/mode/Makefile.in b/src/tools/core/mode/Makefile.in index 741c5fea4f..10f09a3200 100644 --- a/src/tools/core/mode/Makefile.in +++ b/src/tools/core/mode/Makefile.in @@ -102,9 +102,9 @@ CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" PROGRAMS = $(bin_PROGRAMS) am_mode_OBJECTS = mode-mode_usage.$(OBJEXT) \ - mode-mode_frontend.$(OBJEXT) mode-multivar_data.$(OBJEXT) \ - mode-multivar_frontend.$(OBJEXT) mode-mode.$(OBJEXT) \ - mode-combine_boolplanes.$(OBJEXT) \ + mode-mode_frontend.$(OBJEXT) mode-mode_superobject.$(OBJEXT) \ + mode-multivar_data.$(OBJEXT) mode-multivar_frontend.$(OBJEXT) \ + mode-mode.$(OBJEXT) mode-combine_boolplanes.$(OBJEXT) \ mode-objects_from_netcdf.$(OBJEXT) mode-mode_ps_file.$(OBJEXT) \ mode-plot_engine.$(OBJEXT) mode-page_1.$(OBJEXT) \ mode-fcst_enlarge_page.$(OBJEXT) \ @@ -138,6 +138,7 @@ am__depfiles_remade = ./$(DEPDIR)/mode-cluster_page.Po \ ./$(DEPDIR)/mode-mode_exec.Po \ ./$(DEPDIR)/mode-mode_frontend.Po \ ./$(DEPDIR)/mode-mode_ps_file.Po \ + ./$(DEPDIR)/mode-mode_superobject.Po \ ./$(DEPDIR)/mode-mode_usage.Po \ ./$(DEPDIR)/mode-multivar_data.Po \ ./$(DEPDIR)/mode-multivar_frontend.Po \ @@ -350,6 +351,7 @@ top_srcdir = @top_srcdir@ MAINTAINERCLEANFILES = Makefile.in mode_SOURCES = mode_usage.cc \ mode_frontend.cc \ + mode_superobject.cc \ multivar_data.cc \ multivar_frontend.cc \ mode.cc \ @@ -503,6 +505,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mode-mode_exec.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mode-mode_frontend.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mode-mode_ps_file.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mode-mode_superobject.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mode-mode_usage.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mode-multivar_data.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mode-multivar_frontend.Po@am__quote@ # am--include-marker @@ -560,6 +563,20 @@ mode-mode_frontend.obj: mode_frontend.cc @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mode_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o mode-mode_frontend.obj `if test -f 'mode_frontend.cc'; then $(CYGPATH_W) 'mode_frontend.cc'; else $(CYGPATH_W) '$(srcdir)/mode_frontend.cc'; fi` +mode-mode_superobject.o: mode_superobject.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mode_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT mode-mode_superobject.o -MD -MP -MF $(DEPDIR)/mode-mode_superobject.Tpo -c -o mode-mode_superobject.o `test -f 'mode_superobject.cc' || echo '$(srcdir)/'`mode_superobject.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mode-mode_superobject.Tpo $(DEPDIR)/mode-mode_superobject.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='mode_superobject.cc' object='mode-mode_superobject.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mode_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o mode-mode_superobject.o `test -f 'mode_superobject.cc' || echo '$(srcdir)/'`mode_superobject.cc + +mode-mode_superobject.obj: mode_superobject.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mode_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT mode-mode_superobject.obj -MD -MP -MF $(DEPDIR)/mode-mode_superobject.Tpo -c -o mode-mode_superobject.obj `if test -f 'mode_superobject.cc'; then $(CYGPATH_W) 'mode_superobject.cc'; else $(CYGPATH_W) '$(srcdir)/mode_superobject.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mode-mode_superobject.Tpo $(DEPDIR)/mode-mode_superobject.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='mode_superobject.cc' object='mode-mode_superobject.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mode_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o mode-mode_superobject.obj `if test -f 'mode_superobject.cc'; then $(CYGPATH_W) 'mode_superobject.cc'; else $(CYGPATH_W) '$(srcdir)/mode_superobject.cc'; fi` + mode-multivar_data.o: multivar_data.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mode_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT mode-multivar_data.o -MD -MP -MF $(DEPDIR)/mode-multivar_data.Tpo -c -o mode-multivar_data.o `test -f 'multivar_data.cc' || echo '$(srcdir)/'`multivar_data.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mode-multivar_data.Tpo $(DEPDIR)/mode-multivar_data.Po @@ -876,6 +893,7 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/mode-mode_exec.Po -rm -f ./$(DEPDIR)/mode-mode_frontend.Po -rm -f ./$(DEPDIR)/mode-mode_ps_file.Po + -rm -f ./$(DEPDIR)/mode-mode_superobject.Po -rm -f ./$(DEPDIR)/mode-mode_usage.Po -rm -f ./$(DEPDIR)/mode-multivar_data.Po -rm -f ./$(DEPDIR)/mode-multivar_frontend.Po @@ -936,6 +954,7 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/mode-mode_exec.Po -rm -f ./$(DEPDIR)/mode-mode_frontend.Po -rm -f ./$(DEPDIR)/mode-mode_ps_file.Po + -rm -f ./$(DEPDIR)/mode-mode_superobject.Po -rm -f ./$(DEPDIR)/mode-mode_usage.Po -rm -f ./$(DEPDIR)/mode-multivar_data.Po -rm -f ./$(DEPDIR)/mode-multivar_frontend.Po diff --git a/src/tools/core/mode/combine_boolplanes.h b/src/tools/core/mode/combine_boolplanes.h index e879827740..084a31aed1 100644 --- a/src/tools/core/mode/combine_boolplanes.h +++ b/src/tools/core/mode/combine_boolplanes.h @@ -20,7 +20,8 @@ #include "two_d_array.h" #include "bool_calc.h" #include "vx_pxm.h" - +#include +#include //////////////////////////////////////////////////////////////////////// diff --git a/src/tools/core/mode/mode.cc b/src/tools/core/mode/mode.cc index b7277b0110..d71a7698b5 100644 --- a/src/tools/core/mode/mode.cc +++ b/src/tools/core/mode/mode.cc @@ -57,6 +57,7 @@ // 020 07/06/22 Howard Soh METplus-Internal #19 Rename main to met_main // 021 06/09/23 Albo Major changes for multivariate mode // 022 11/02/23 Halley Gotway MET #2724 add OpenMP to convolution +// 023 12/27/23 Albo MET #2745 more unit tests, read data one time, percentile thresholding // //////////////////////////////////////////////////////////////////////// @@ -78,6 +79,7 @@ using namespace std; #include "string_array.h" #include "mode_usage.h" #include "mode_frontend.h" +#include "multivar_frontend.h" #include "mode_conf_info.h" #ifdef WITH_PYTHON @@ -97,7 +99,6 @@ using namespace std; /////////////////////////////////////////////////////////////////////// -extern int mode_frontend(const StringArray &); extern int multivar_frontend(const StringArray &); extern const char * const program_name; @@ -186,8 +187,10 @@ int met_main(int argc, char * argv []) config.check_multivar_not_implemented(); // run the multivar version of mode - - status = multivar_frontend(Argv); + + MultivarFrontEnd *frontend = new MultivarFrontEnd(); + status = frontend->run(Argv); + if ( frontend ) { delete frontend; frontend = 0; } } else { diff --git a/src/tools/core/mode/mode_exec.cc b/src/tools/core/mode/mode_exec.cc index 82cb261b11..d0c2e01b7a 100644 --- a/src/tools/core/mode/mode_exec.cc +++ b/src/tools/core/mode/mode_exec.cc @@ -35,7 +35,6 @@ static const char * cts_str[n_cts] = {"RAW", "OBJECT"}; static const char program_name [] = "mode"; static const char * default_config_filename = "MET_BASE/config/MODEConfig_default"; -static const char * default_multivar_config_filename = "MET_BASE/config/MODEMultivarConfig_default"; // took this out of the do_conv_thresh() method static int local_r_index = -1; @@ -60,12 +59,11 @@ static void replaceAll(std::string& str, const std::string& from, const std::str /////////////////////////////////////////////////////////////////////// -ModeExecutive::ModeExecutive(Processing_t p) +ModeExecutive::ModeExecutive()//Processing_t p) { init_from_scratch(); - ptype = p; } @@ -132,8 +130,6 @@ void ModeExecutive::clear() R_index = T_index = 0; - ptype = TRADITIONAL; - // // done // @@ -154,7 +150,7 @@ void ModeExecutive::init_traditional(int n_files) R_index = T_index = 0; - conf_read(default_config_filename); + conf_read(); // Get the forecast and observation file types from config, if present ftype = parse_conf_file_type(engine.conf_info.conf.lookup_dictionary(conf_key_fcst)); @@ -180,7 +176,7 @@ void ModeExecutive::init_traditional(int n_files) otype = obs_mtddf->file_type(); // Process the configuration - engine.conf_info.process_config(ftype, otype); + engine.conf_info.process_config_traditional(ftype, otype); int nf = engine.conf_info.n_fields_f(); // same as obs for traditional mode if (nf != n_files) { mlog << Error << "\nNumber of input files " << n_files << " Not equal to config number of fields " @@ -206,161 +202,19 @@ void ModeExecutive::init_traditional(int n_files) /////////////////////////////////////////////////////////////////////// - -void ModeExecutive::init_multivar_verif_grid() +void ModeExecutive::init_multivar_simple(int j, int n_files, ModeDataType dtype, + const ModeConfInfo &conf) { - - Met2dDataFileFactory mtddf_factory; - R_index = T_index = 0; - - conf_read(default_multivar_config_filename); - - // Get the forecast and observation file types from config, if present - ftype = parse_conf_file_type(engine.conf_info.conf.lookup_dictionary(conf_key_fcst)); - otype = parse_conf_file_type(engine.conf_info.conf.lookup_dictionary(conf_key_obs)); - - - // Read observation file - if(!(obs_mtddf = mtddf_factory.new_met_2d_data_file(obs_file.c_str(), otype))) { - mlog << Error << "\nTrouble reading observation file \"" - << obs_file << "\"\n\n"; - exit(1); - } - - // Read forecast file - if(!(fcst_mtddf = mtddf_factory.new_met_2d_data_file(fcst_file.c_str(), ftype))) { - mlog << Error << "\nTrouble reading forecast file \"" - << fcst_file << "\"\n\n"; - exit(1); - } - - // Store the input data file types - ftype = fcst_mtddf->file_type(); - otype = obs_mtddf->file_type(); - - // Process the configuration - engine.conf_info.process_config(ftype, otype, ModeDataType_MvMode_Both); - - // check one again for multivar problems - engine.conf_info.check_multivar_not_implemented(); - - const int shift = engine.conf_info.shift_right; - - fcst_mtddf->set_shift_right(shift); - obs_mtddf->set_shift_right(shift); - - // List the input files - mlog << Debug(1) - << "Forecast File: " << fcst_file << "\n" - << "Observation File: " << obs_file << "\n"; - - engine.conf_info.nc_info.compress_level = engine.conf_info.get_compression_level(); - - return; - -} - -/////////////////////////////////////////////////////////////////////// - -void ModeExecutive::init_multivar_simple(int n_files, ModeDataType dtype) - -{ - - Met2dDataFileFactory mtddf_factory; - - R_index = T_index = 0; - - conf_read(default_multivar_config_filename); + engine.conf_info = conf; // tell the engine which type of data it is engine.set_data_type(dtype); - // Get the forecast and observation file types from config, if present - ftype = parse_conf_file_type(engine.conf_info.conf.lookup_dictionary(conf_key_fcst)); - otype = parse_conf_file_type(engine.conf_info.conf.lookup_dictionary(conf_key_obs)); - - - // Read observation file or forecast file - if (dtype == ModeDataType_MvMode_Obs) - { - if(!(obs_mtddf = mtddf_factory.new_met_2d_data_file(obs_file.c_str(), otype))) { - mlog << Error << "\nTrouble reading observation file \"" - << obs_file << "\"\n\n"; - exit(1); - - } - // Store the input data file types - otype = obs_mtddf->file_type(); - } - else if (dtype == ModeDataType_MvMode_Fcst) - { - // Read forecast file - if(!(fcst_mtddf = mtddf_factory.new_met_2d_data_file(fcst_file.c_str(), ftype))) { - mlog << Error << "\nTrouble reading forecast file \"" - << fcst_file << "\"\n\n"; - exit(1); - } - // Store the input data file types - ftype = fcst_mtddf->file_type(); - } - else { - mlog << Error << "\nModeExecutive::init_multivar_simple()->" - << "simple object creation requires obs or forecast mode, got " - << sprintModeDataType(dtype) << "\n\n"; - exit(1); - } - - // Process the configuration - engine.conf_info.process_config(ftype, otype, dtype); - int nf; - if (dtype == ModeDataType_MvMode_Obs) { - nf = engine.conf_info.n_fields_o(); - } else if (dtype == ModeDataType_MvMode_Fcst) { - nf = engine.conf_info.n_fields_f(); - } else { - mlog << Error << "\nModeExecutive::init_multivar_simple()->" - << "simple object creation requires obs or forecast mode, got " - << sprintModeDataType(dtype) << "\n\n"; - exit(1); - } - if (nf != n_files) { - mlog << Error << "\nModeExecutive::init_multivar_simple()->" - << "Number of input files " << n_files << " Not equal to config number of fields " - << nf << "\n\n"; - exit(1); - } - - // check one again for multivar problems - engine.conf_info.check_multivar_not_implemented(); - - const int shift = engine.conf_info.shift_right; - - if (dtype != ModeDataType_MvMode_Fcst) { - obs_mtddf->set_shift_right(shift); - } - - if (dtype != ModeDataType_MvMode_Obs) { - fcst_mtddf->set_shift_right(shift); - } - - if (dtype == ModeDataType_MvMode_Obs) { - mlog << Debug(1) - << "Observation File: " << obs_file << "\n"; - - } else if (dtype == ModeDataType_MvMode_Fcst) { - mlog << Debug(1) - << "Forecast File: " << fcst_file << "\n"; - } - else { - // List the input files - mlog << Debug(1) - << "Forecast File: " << fcst_file << "\n" - << "Observation File: " << obs_file << "\n"; - } + engine.conf_info.set_field_index(j); - engine.conf_info.nc_info.compress_level = engine.conf_info.get_compression_level(); + // engine.conf_info.nc_info.compress_level = engine.conf_info.get_compression_level(); return; @@ -368,34 +222,21 @@ void ModeExecutive::init_multivar_simple(int n_files, ModeDataType dtype) /////////////////////////////////////////////////////////////////////// -void ModeExecutive::init_multivar_intensities(GrdFileType ftype, GrdFileType otype) +void ModeExecutive::init_multivar_intensities(const ModeConfInfo &conf) { R_index = T_index = 0; - conf_read(default_multivar_config_filename); - - // Get the forecast and observation file types from config, if present - GrdFileType l_ftype = parse_conf_file_type(engine.conf_info.conf.lookup_dictionary(conf_key_fcst)); - GrdFileType l_otype = parse_conf_file_type(engine.conf_info.conf.lookup_dictionary(conf_key_obs)); - - // actually use the values passed in instead, the config ones are not set for this second pass - l_ftype = ftype; - l_otype = otype; - - // Process the configuration - engine.conf_info.process_config(l_ftype, l_otype, ModeDataType_MvMode_Both); + engine.conf_info = conf; + + // tell the engine which type of data it is + engine.set_data_type(ModeDataType_MvMode_Both); // check one again for multivar problems engine.conf_info.check_multivar_not_implemented(); - // NOTE: do not do shifting, should have been done in the first pass - // const int shift = engine.conf_info.shift_right; - // fcst_mtddf->set_shift_right(shift); - // obs_mtddf->set_shift_right(shift); - - engine.conf_info.nc_info.compress_level = engine.conf_info.get_compression_level(); + // engine.conf_info.nc_info.compress_level = engine.conf_info.get_compression_level(); return; @@ -404,16 +245,8 @@ void ModeExecutive::init_multivar_intensities(GrdFileType ftype, GrdFileType oty /////////////////////////////////////////////////////////////////////// -void ModeExecutive::check_multivar_perc_thresh_settings() -{ - engine.conf_info.check_multivar_perc_thresh(ptype == ModeExecutive::MULTIVAR_SIMPLE, - ptype == ModeExecutive::MULTIVAR_SIMPLE_MERGE); -} -/////////////////////////////////////////////////////////////////////// - - -void ModeExecutive::setup_fcst_obs_data_traditional() +void ModeExecutive::setup_traditional_fcst_obs_data() { @@ -427,7 +260,7 @@ void ModeExecutive::setup_fcst_obs_data_traditional() if ( !(fcst_mtddf->data_plane(*(engine.conf_info.Fcst->var_info), Fcst_sd.data)) ) { - mlog << Error << "\nModeExecutive::setup_fcst_obs_data_traditional() -> " + mlog << Error << "\nModeExecutive::setup_traditionalfcst_obs_data() -> " << "can't get forecast data \"" << engine.conf_info.Fcst->var_info->magic_str() << "\" from file \"" << fcst_file << "\"\n\n"; @@ -438,7 +271,7 @@ void ModeExecutive::setup_fcst_obs_data_traditional() if ( !(obs_mtddf->data_plane(*(engine.conf_info.Obs->var_info), Obs_sd.data)) ) { - mlog << Error << "\nModeExecutive::setup_fcst_obs_data_traditional() -> " + mlog << Error << "\nModeExecutive::setup_traditional_fcst_obs_data() -> " << "can't get observation data \"" << engine.conf_info.Obs->var_info->magic_str() << "\" from file \"" << obs_file << "\"\n\n"; @@ -482,28 +315,37 @@ void ModeExecutive::setup_fcst_obs_data_traditional() if ( engine.conf_info.Obs->var_info->p_flag() ) rescale_probability(Obs_sd.data); - // Print a warning if the valid times do not match + // Check that the valid times match if(Fcst_sd.data.valid() != Obs_sd.data.valid()) { - mlog << Warning << "\nModeExecutive::setup_fcst_obs_data_traditional() -> " - << "Forecast and observation valid times do not match " - << unix_to_yyyymmdd_hhmmss(Fcst_sd.data.valid()) << " != " - << unix_to_yyyymmdd_hhmmss(Obs_sd.data.valid()) << " for " - << engine.conf_info.Fcst->var_info->magic_str() << " versus " - << engine.conf_info.Obs->var_info->magic_str() << ".\n\n"; + ConcatString cs; + cs << "Forecast and observation valid times do not match (" + << unix_to_yyyymmdd_hhmmss(Fcst_sd.data.valid()) << " != " + << unix_to_yyyymmdd_hhmmss(Obs_sd.data.valid()) << ") for " + << engine.conf_info.Fcst->var_info->magic_str() << " versus " + << engine.conf_info.Obs->var_info->magic_str() << "."; + + if(engine.conf_info.conf.time_offset_warning( + (int) (Fcst_sd.data.valid() - Obs_sd.data.valid()))) { + mlog << Warning << "\nModeExecutive::setup_fcst_obs_data_traditional() ->" + << cs << "\n\n"; + } + else { + mlog << Debug(3) << cs << "\n"; + } } - // Print a warning if the accumulation intervals do not match + // Check that the accumulation intervals match if(engine.conf_info.Fcst->var_info->level().type() == LevelType_Accum && engine.conf_info.Obs->var_info->level().type() == LevelType_Accum && - Fcst_sd.data.accum() != Obs_sd.data.accum()) { + Fcst_sd.data.accum() != Obs_sd.data.accum()) { mlog << Warning << "\nModeExecutive::setup_fcst_obs_data_traditional() -> " - << "Forecast and observation accumulation times do not match " - << sec_to_hhmmss(Fcst_sd.data.valid()) << " != " - << sec_to_hhmmss(Obs_sd.data.valid()) << " for " + << "Forecast and observation accumulation times do not match (" + << sec_to_hhmmss(Fcst_sd.data.accum()) << " != " + << sec_to_hhmmss(Obs_sd.data.accum()) << ") for " << engine.conf_info.Fcst->var_info->magic_str() << " versus " << engine.conf_info.Obs->var_info->magic_str() << ".\n\n"; } @@ -568,68 +410,39 @@ void ModeExecutive::setup_fcst_obs_data_traditional() /////////////////////////////////////////////////////////////////////// - -void ModeExecutive::setup_verification_grid() - +void ModeExecutive::setup_verification_grid(const ModeInputData &fcst, + const ModeInputData &obs, + const ModeConfInfo &conf) { - - // ShapeData fcst_sd, obs_sd; - double fmin, omin, fmax, omax; + R_index = T_index = 0; + engine.conf_info = conf; + engine.conf_info.check_multivar_not_implemented(); Fcst_sd.clear(); Obs_sd.clear(); - - // Read the gridded data from the input forecast file - - if ( !(fcst_mtddf->data_plane(*(engine.conf_info.Fcst->var_info), Fcst_sd.data)) ) { - - mlog << Error << "\nModeExecutive::setup_verification_grid() -> " - << "can't get forecast data \"" - << engine.conf_info.Fcst->var_info->magic_str() - << "\" from file \"" << fcst_file << "\"\n\n"; - exit(1); - } - - // Read the gridded data from the input observation file - - if ( !(obs_mtddf->data_plane(*(engine.conf_info.Obs->var_info), Obs_sd.data)) ) { - - mlog << Error << "\nModeExecutive::setup_verification_grid() -> " - << "can't get observation data \"" - << engine.conf_info.Obs->var_info->magic_str() - << "\" from file \"" << obs_file << "\"\n\n"; - exit(1); - } - - // Determine the verification grid - + Fcst_sd.data = fcst._dataPlane; + Obs_sd.data = obs._dataPlane; + + // set this local conf to point to forecast 0, so that that regrid info + // can be accessed and it can be decided if we are to use fcst or obs + // input + engine.conf_info.set_data_type(ModeDataType_MvMode_Fcst); + engine.conf_info.set_field_index(0); grid = parse_vx_grid(engine.conf_info.Fcst->var_info->regrid(), - &(fcst_mtddf->grid()), &(obs_mtddf->grid())); - + &fcst._grid, &obs._grid); return; } /////////////////////////////////////////////////////////////////////// - -void ModeExecutive::setup_fcst_data(const Grid &verification_grid) - +void ModeExecutive::setup_multivar_fcst_data(const Grid &verification_grid, + const ModeInputData &input) { - // ShapeData fcst_sd, obs_sd; double fmin, fmax; Fcst_sd.clear(); - - // Read the gridded data from the input forecast file - if ( !(fcst_mtddf->data_plane(*(engine.conf_info.Fcst->var_info), Fcst_sd.data)) ) { - - mlog << Error << "\nModeExecutive::setup_fcst_data() -> " - << "can't get forecast data \"" - << engine.conf_info.Fcst->var_info->magic_str() - << "\" from file \"" << fcst_file << "\"\n\n"; - exit(1); - } + Fcst_sd.data = input._dataPlane; grid = verification_grid; @@ -638,12 +451,11 @@ void ModeExecutive::setup_fcst_data(const Grid &verification_grid) engine.set_grid(&grid); // Regrid, if necessary - - if ( !(fcst_mtddf->grid() == grid) ) { + if ( !(input._grid == grid) ) { mlog << Debug(1) << "Regridding forecast " << engine.conf_info.Fcst->var_info->magic_str() << " to the verification grid.\n"; - Fcst_sd.data = met_regrid(Fcst_sd.data, fcst_mtddf->grid(), grid, + Fcst_sd.data = met_regrid(Fcst_sd.data, input._grid, grid, engine.conf_info.Fcst->var_info->regrid()); } @@ -667,10 +479,6 @@ void ModeExecutive::setup_fcst_data(const Grid &verification_grid) data_min = fmin; data_max = fmax; - // Process percentile thresholds - - engine.conf_info.set_perc_thresh(Fcst_sd.data); - // store the input data units funits = engine.conf_info.Fcst->var_info->units(); ounits = na_str; @@ -689,9 +497,8 @@ void ModeExecutive::setup_fcst_data(const Grid &verification_grid) /////////////////////////////////////////////////////////////////////// - -void ModeExecutive::setup_obs_data(const Grid &verification_grid) - +void ModeExecutive::setup_multivar_obs_data(const Grid &verification_grid, + const ModeInputData &input) { // ShapeData fcst_sd, obs_sd; @@ -700,15 +507,7 @@ void ModeExecutive::setup_obs_data(const Grid &verification_grid) Obs_sd.clear(); // Read the gridded data from the input observation file - - if ( !(obs_mtddf->data_plane(*(engine.conf_info.Obs->var_info), Obs_sd.data)) ) { - - mlog << Error << "\nModeExecutive::setup_obs_data() -> " - << "can't get observation data \"" - << engine.conf_info.Obs->var_info->magic_str() - << "\" from file \"" << obs_file << "\"\n\n"; - exit(1); - } + Obs_sd.data = input._dataPlane; grid = verification_grid; @@ -718,11 +517,11 @@ void ModeExecutive::setup_obs_data(const Grid &verification_grid) // Regrid, if necessary - if ( !(obs_mtddf->grid() == grid) ) { + if ( !(input._grid == grid) ) { mlog << Debug(1) << "Regridding observation " << engine.conf_info.Obs->var_info->magic_str() << " to the verification grid.\n"; - Obs_sd.data = met_regrid(Obs_sd.data, obs_mtddf->grid(), grid, + Obs_sd.data = met_regrid(Obs_sd.data, input._grid, grid, engine.conf_info.Obs->var_info->regrid()); } @@ -746,10 +545,6 @@ void ModeExecutive::setup_obs_data(const Grid &verification_grid) data_min = omin; data_max = omax; - // Process percentile thresholds - - engine.conf_info.set_perc_thresh(Obs_sd.data); - // store the input data units funits = na_str; ounits = engine.conf_info.Obs->var_info->units(); @@ -768,7 +563,7 @@ void ModeExecutive::setup_obs_data(const Grid &verification_grid) /////////////////////////////////////////////////////////////////////// -void ModeExecutive::setup_fcst_obs_data_multivar_intensities(const MultiVarData &mvdf, +void ModeExecutive::setup_multivar_fcst_obs_data_intensities(const MultiVarData &mvdf, const MultiVarData &mvdo) { double fmin, fmax, omin, omax; @@ -780,38 +575,43 @@ void ModeExecutive::setup_fcst_obs_data_multivar_intensities(const MultiVarData Obs_sd.debug_examine(); grid = *(mvdf._grid); - // do not need to read any data, it is stored in the mvd input - // the verification grid was created in the first pass, so we have that as well - // Store the grid engine.set_grid(&grid); - // regridding of inputs is not needed in the second pass, as regridded data outputs - // are stored to input mvd + // Rescale probabilites from [0, 100] to [0, 1] not needed in the second pass - // Rescale probabilites from [0, 100] to [0, 1] also not neede in the second pass + // Check that the valid times match - // Print a warning if the valid times do not match if(Fcst_sd.data.valid() != Obs_sd.data.valid()) { - mlog << Warning << "\nModeExecutive::setup_fcst_obs_data_multivar_intensities() -> " - << "Forecast and observation valid times do not match " - << unix_to_yyyymmdd_hhmmss(Fcst_sd.data.valid()) << " != " - << unix_to_yyyymmdd_hhmmss(Obs_sd.data.valid()) << " for " - << engine.conf_info.Fcst->var_info->magic_str() << " versus " - << engine.conf_info.Obs->var_info->magic_str() << ".\n\n"; - } - - // Print a warning if the accumulation intervals do not match + ConcatString cs; + cs << "Forecast and observation valid times do not match (" + << unix_to_yyyymmdd_hhmmss(Fcst_sd.data.valid()) << " != " + << unix_to_yyyymmdd_hhmmss(Obs_sd.data.valid()) << ") for " + << engine.conf_info.Fcst->var_info->magic_str() << " versus " + << engine.conf_info.Obs->var_info->magic_str() << "."; + + if(engine.conf_info.conf.time_offset_warning( + (int) (Fcst_sd.data.valid() - Obs_sd.data.valid()))) { + mlog << Warning << "\nModeExecutive::setup_fcst_obs_data_multivar_intensities() ->" + << cs << "\n\n"; + } + else { + mlog << Debug(3) << cs << "\n"; + } + } + + // Check that the accumulation intervals match + if(engine.conf_info.Fcst->var_info->level().type() == LevelType_Accum && engine.conf_info.Obs->var_info->level().type() == LevelType_Accum && - Fcst_sd.data.accum() != Obs_sd.data.accum()) { + Fcst_sd.data.accum() != Obs_sd.data.accum()) { mlog << Warning << "\nModeExecutive::setup_fcst_obs_data_multivar_intensities() -> " - << "Forecast and observation accumulation times do not match " - << sec_to_hhmmss(Fcst_sd.data.valid()) << " != " - << sec_to_hhmmss(Obs_sd.data.valid()) << " for " + << "Forecast and observation accumulation times do not match (" + << sec_to_hhmmss(Fcst_sd.data.accum()) << " != " + << sec_to_hhmmss(Obs_sd.data.accum()) << ") for " << engine.conf_info.Fcst->var_info->magic_str() << " versus " << engine.conf_info.Obs->var_info->magic_str() << ".\n\n"; } @@ -835,15 +635,12 @@ void ModeExecutive::setup_fcst_obs_data_multivar_intensities(const MultiVarData // in case perc thresh was done, retrieve the values created in the // first pass, which have been stored in mvdo and mvdf + // NOTE: this might be removable with new design engine.conf_info.Obs->conv_thresh_array = mvdo._merge->_convThreshArray; engine.conf_info.Obs->merge_thresh_array = mvdo._merge->_mergeThreshArray; engine.conf_info.Fcst->conv_thresh_array = mvdf._merge->_convThreshArray; engine.conf_info.Fcst->merge_thresh_array = mvdf._merge->_mergeThreshArray; - // This doesn't work with perc_thresh, it uses values restricted to within - // superobjects, which gives different thresholds, so we do the above instead. - //engine.conf_info.set_perc_thresh(Fcst_sd.data, Obs_sd.data); - // masking of inputs not needed, as it was done in the first pass // and stored to Fcst_sd and Obs_sd @@ -867,7 +664,8 @@ void ModeExecutive::setup_fcst_obs_data_multivar_intensities(const MultiVarData /////////////////////////////////////////////////////////////////////// -void ModeExecutive::setup_fcst_obs_data_multivar_super(ShapeData &f_super, ShapeData &o_super, +void ModeExecutive::setup_multivar_fcst_obs_data_super(const ShapeData &f_super, + const ShapeData &o_super, const Grid &igrid) { double fmin, omin, fmax, omax; @@ -891,26 +689,37 @@ void ModeExecutive::setup_fcst_obs_data_multivar_super(ShapeData &f_super, Shape // Rescale probabilites from [0, 100] to [0, 1] also not neede in the second pass - // Print a warning if the valid times do not match + // Check that the valid times match + if(Fcst_sd.data.valid() != Obs_sd.data.valid()) { - mlog << Warning << "\nModeExecutive::setup_fcst_obs_data_multivar_super() -> " - << "Forecast and observation valid times do not match " - << unix_to_yyyymmdd_hhmmss(Fcst_sd.data.valid()) << " != " - << unix_to_yyyymmdd_hhmmss(Obs_sd.data.valid()) << " for " - << engine.conf_info.Fcst->var_info->magic_str() << " versus " - << engine.conf_info.Obs->var_info->magic_str() << ".\n\n"; + ConcatString cs; + cs << "Forecast and observation valid times do not match (" + << unix_to_yyyymmdd_hhmmss(Fcst_sd.data.valid()) << " != " + << unix_to_yyyymmdd_hhmmss(Obs_sd.data.valid()) << ") for " + << engine.conf_info.Fcst->var_info->magic_str() << " versus " + << engine.conf_info.Obs->var_info->magic_str() << "."; + + if(engine.conf_info.conf.time_offset_warning( + (int) (Fcst_sd.data.valid() != Obs_sd.data.valid()))) { + mlog << Warning << "\nModeExecutive::setup_fcst_obs_data_multivar_super() ->" + << cs << "\n\n"; + } + else { + mlog << Debug(3) << cs << "\n"; + } } - // Print a warning if the accumulation intervals do not match + // Check that the accumulation intervals match + if(engine.conf_info.Fcst->var_info->level().type() == LevelType_Accum && engine.conf_info.Obs->var_info->level().type() == LevelType_Accum && - Fcst_sd.data.accum() != Obs_sd.data.accum()) { + Fcst_sd.data.accum() != Obs_sd.data.accum()) { mlog << Warning << "\nModeExecutive::setup_fcst_obs_data_multivar_super() -> " - << "Forecast and observation accumulation times do not match " - << sec_to_hhmmss(Fcst_sd.data.valid()) << " != " - << sec_to_hhmmss(Obs_sd.data.valid()) << " for " + << "Forecast and observation accumulation times do not match (" + << sec_to_hhmmss(Fcst_sd.data.accum()) << " != " + << sec_to_hhmmss(Obs_sd.data.accum()) << ") for " << engine.conf_info.Fcst->var_info->magic_str() << " versus " << engine.conf_info.Obs->var_info->magic_str() << ".\n\n"; } @@ -941,10 +750,6 @@ void ModeExecutive::setup_fcst_obs_data_multivar_super(ShapeData &f_super, Shape else if(!is_bad_data(fmax) && is_bad_data(omax)) data_max = fmax; else if( is_bad_data(fmax) && !is_bad_data(omax)) data_max = omax; - // Process percentile thresholds...Not applicable for this pass - // do this in the second pass using masked Fcst_sd and Obs_sd - // engine.conf_info.set_perc_thresh(Fcst_sd.data, Obs_sd.data); - // // done // @@ -957,7 +762,7 @@ void ModeExecutive::setup_fcst_obs_data_multivar_super(ShapeData &f_super, Shape /////////////////////////////////////////////////////////////////////// -void ModeExecutive::do_conv_thresh(const int r_index, const int t_index) +void ModeExecutive::do_conv_thresh_traditional(const int r_index, const int t_index) { ModeConfInfo & conf = engine.conf_info; @@ -969,65 +774,162 @@ void ModeExecutive::do_conv_thresh(const int r_index, const int t_index) R_index = r_index; T_index = t_index; - if (ptype == MULTIVAR_SIMPLE_MERGE) { - conf.set_conv_thresh_by_merge_index(T_index); - } else if (ptype == MULTIVAR_SUPER) { - SingleThresh s("ne-9999"); - conf.set_conv_thresh(s); - conf.set_conv_radius(0.0); + conf.set_conv_radius_by_index(R_index); + conf.set_conv_thresh_by_index(T_index); + conf.set_merge_thresh_by_index(T_index); + + // + // Set up the engine with these raw fields + // + + string what = "forecast and observation fields"; + mlog << Debug(2) << "Identifying objects in the " << what << "...\n"; + + if ( r_index != local_r_index ) { + // need to do convolution + engine.set(Fcst_sd, Obs_sd); } else { - // this could break down if obs and fcst have different arrays - // currently not allowed in multivar mode, should work correctly - // for traditional mode - conf.set_conv_radius_by_index(R_index); - conf.set_conv_thresh_by_index(T_index); + // don't need to do convolution + engine.set_no_conv(Fcst_sd, Obs_sd); } + // + // Compute the contingency table statistics for the fields, if needed + // + if ( conf.ct_stats_flag ) compute_ct_stats(); + + // + // done + // + + local_r_index = r_index; + + return; + +} + +/////////////////////////////////////////////////////////////////////// + +void ModeExecutive::do_conv_thresh_multivar_super() +{ + + ModeConfInfo & conf = engine.conf_info; + + R_index = 0; + T_index = 0; + + SingleThresh s("ne-9999"); + conf.set_conv_thresh(s); + conf.set_conv_radius(0.0); conf.set_merge_thresh_by_index(T_index); // // Set up the engine with these raw fields // - string what = "forecast and observation fields"; - if (ptype == MULTIVAR_SIMPLE || ptype == MULTIVAR_SIMPLE_MERGE) { - if (conf.data_type == ModeDataType_MvMode_Obs) { - what = "observation field"; - } else { - what = "forecast field"; - } - } mlog << Debug(2) << "Identifying objects in the " << what << "...\n"; - if ( r_index != local_r_index ) { // need to do convolution + engine.set_only_split(Fcst_sd, Obs_sd); - if (ptype == MULTIVAR_INTENSITY || ptype == MULTIVAR_SUPER) { - engine.set_only_split(Fcst_sd, Obs_sd); - } else {// MULTIVAR_SIMPLE, MULTIVAR_SIMPLE_MERGE, TRADITIONAL - engine.set(Fcst_sd, Obs_sd); - } - } else { // don't need to do convolution + // + // Compute the contingency table statistics for the fields, if needed + // (not needed for simple or merge, only one field) + // + if ( conf.ct_stats_flag ) compute_ct_stats(); - if (ptype == MULTIVAR_INTENSITY || ptype == MULTIVAR_SUPER) { - engine.set_only_split(Fcst_sd, Obs_sd); - } else {// MULTIVAR_SIMPLE, MULTIVAR_SIMPLE_MERGE, TRADITIONAL - engine.set_no_conv(Fcst_sd, Obs_sd); - } - } + // + // done + // + + local_r_index = 0; + + return; + +} + + +/////////////////////////////////////////////////////////////////////// + + +void ModeExecutive::do_conv_thresh_multivar_intensity_compare() +{ + + ModeConfInfo & conf = engine.conf_info; + + R_index = 0; + T_index = 0; + + conf.set_conv_radius_by_index(R_index); + conf.set_conv_thresh_by_index(T_index); + conf.set_merge_thresh_by_index(T_index); + + // + // Set up the engine with these raw fields + // + + string what = "forecast and observation fields"; + mlog << Debug(2) << "Identifying objects in the " << what << "...\n"; + + engine.set_only_split(Fcst_sd, Obs_sd); // // Compute the contingency table statistics for the fields, if needed - // (not needed for simple or merge, only one field) // - if (ptype != MULTIVAR_SIMPLE && ptype != MULTIVAR_SIMPLE_MERGE) { - if ( conf.ct_stats_flag ) compute_ct_stats(); - } + if ( conf.ct_stats_flag ) compute_ct_stats(); // // done // - local_r_index = r_index; + local_r_index = 0; + + return; + +} + +/////////////////////////////////////////////////////////////////////// + +void ModeExecutive::do_conv_thresh_multivar_simple(Processing_t p) +{ + + ModeConfInfo & conf = engine.conf_info; + + R_index = 0; + T_index = 0; + + if (p == MULTIVAR_SIMPLE_MERGE) { + conf.set_conv_thresh_by_merge_index(T_index); + } else if (p == MULTIVAR_SIMPLE) { + conf.set_conv_radius_by_index(0); + conf.set_conv_thresh_by_index(0); + } else { + mlog << Error << "\nModeExecutive::do_conv_thresh_multivar_simple() -> " + << "Wrong processing type input " << stype(p) << "\"\n\n"; + exit(1); + } + + conf.set_merge_thresh_by_index(0); + + // + // Set up the engine with these raw fields + // + + string what; + if (conf.data_type == ModeDataType_MvMode_Obs) { + what = "observation field"; + } else { + what = "forecast field"; + } + mlog << Debug(2) << "Identifying objects in the " << what << "...\n"; + + engine.set(Fcst_sd, Obs_sd); + + // (ct_stats not needed for simple or merge, only one field) + // + // done + // + + local_r_index = 0; return; @@ -1042,7 +944,7 @@ void ModeExecutive::clear_internal_r_index() /////////////////////////////////////////////////////////////////////// -void ModeExecutive::do_merging() +void ModeExecutive::do_merging_traditional() { mlog << Debug(2) << "Identified: " << engine.n_fcst << " forecast objects " @@ -1074,10 +976,11 @@ void ModeExecutive::do_merging() /////////////////////////////////////////////////////////////////////// -void ModeExecutive::do_merging(ShapeData &f_merge, ShapeData &o_merge) - +void ModeExecutive::do_merging_multivar(const ShapeData &f_merge, + const ShapeData &o_merge, + Processing_t p) { - if (ptype == MULTIVAR_SUPER) { + if (p == MULTIVAR_SUPER) { // set the merge flag and merge_thresh appropriately ModeConfInfo & conf = engine.conf_info; SingleThresh s("ne-9999"); @@ -1085,14 +988,11 @@ void ModeExecutive::do_merging(ShapeData &f_merge, ShapeData &o_merge) conf.set_fcst_merge_thresh(s); conf.set_obs_merge_flag(MergeType_Thresh); conf.set_obs_merge_thresh(s); - } else { - if (ptype != MULTIVAR_INTENSITY) { - mlog << Error << "\nModeExecutive::do_merging(shapedata, shapedata) -> " - << "wrong method for processing type " << stype(ptype) << "\n\n"; - exit(1); - } + } else if (p != MULTIVAR_INTENSITY) { + mlog << Error << "\nModeExecutive::do_merging(shapedata, shapedata, p) -> " + << "wrong method for processing type " << stype(p) << "\n\n"; + exit(1); } - mlog << Debug(2) << "Identified: " << engine.n_fcst << " forecast objects " @@ -1105,8 +1005,7 @@ void ModeExecutive::do_merging(ShapeData &f_merge, ShapeData &o_merge) // Do the forecast merging - engine.do_fcst_merging(default_config_file.c_str(), merge_config_file.c_str(), - f_merge); + engine.do_fcst_merging(f_merge); mlog << Debug(2) << "Performing merging (" @@ -1115,8 +1014,7 @@ void ModeExecutive::do_merging(ShapeData &f_merge, ShapeData &o_merge) // Do the observation merging - engine.do_obs_merging(default_config_file.c_str(), merge_config_file.c_str(), - o_merge); + engine.do_obs_merging(o_merge); mlog << Debug(2) << "Remaining: " << engine.n_fcst << " forecast objects " @@ -1127,10 +1025,12 @@ void ModeExecutive::do_merging(ShapeData &f_merge, ShapeData &o_merge) /////////////////////////////////////////////////////////////////////// -void ModeExecutive::do_match_merge(ShapeData &f_merge, ShapeData &o_merge) +void ModeExecutive::do_match_merge_multivar(const ShapeData &f_merge, + const ShapeData &o_merge, + Processing_t p) { - do_merging(f_merge, o_merge); + do_merging_multivar(f_merge, o_merge, p); // Do the matching of objects between fields @@ -1143,10 +1043,10 @@ void ModeExecutive::do_match_merge(ShapeData &f_merge, ShapeData &o_merge) /////////////////////////////////////////////////////////////////////// -void ModeExecutive::do_match_merge() +void ModeExecutive::do_match_merge_traditional() { - do_merging(); + do_merging_traditional(); // Do the matching of objects between fields @@ -1317,82 +1217,104 @@ void ModeExecutive::set_raw_to_full(float *fcst_raw_data, /////////////////////////////////////////////////////////////////////// -void ModeExecutive::process_output(const MultiVarData *mvdf, - const MultiVarData *mvdo) - +void ModeExecutive::process_output_multivar_super() { + isMultivarOutput = false; + isMultivarSuperOutput = true; - // store to class member so don't have to pass it around - isMultivarOutput = (ptype == MULTIVAR_INTENSITY); - isMultivarSuperOutput = (ptype == MULTIVAR_SUPER); + // use the configured multivar name and level + fcst_magic_string = engine.conf_info.fcst_multivar_name.string() + "_" + engine.conf_info.fcst_multivar_level.string(); + obs_magic_string = engine.conf_info.obs_multivar_name.string() + "_" + engine.conf_info.obs_multivar_level.string(); - if (isMultivarOutput) { + // Create output stats files and plots - // get the magic strings, which will be used in file naming - fcst_magic_string = engine.conf_info.Fcst->var_info->magic_str().c_str(); - obs_magic_string = engine.conf_info.Obs->var_info->magic_str().c_str(); + write_obj_stats(); - // replace forward slashes with underscores to prevent new directories - replace(fcst_magic_string.begin(), fcst_magic_string.end(), '/', '_'); - replace(obs_magic_string.begin(), obs_magic_string.end(), '/', '_'); + if ( engine.conf_info.ct_stats_flag ) write_ct_stats(); + + write_obj_netcdf(engine.conf_info.nc_info); - // replace (*,*) with '_all_all_' - replaceAll(fcst_magic_string, "*", "all"); - replaceAll(obs_magic_string, "*", "all"); - replaceAll(fcst_magic_string, ",", "_"); - replaceAll(obs_magic_string, ",", "_"); - replaceAll(fcst_magic_string, "(", "_"); - replaceAll(obs_magic_string, "(", "_"); - replaceAll(fcst_magic_string, ")", ""); - replaceAll(obs_magic_string, ")", ""); - - } else if (isMultivarSuperOutput) { + if ( engine.conf_info.ps_plot_flag ) plot_engine(); - // use the configured multivar name and level - fcst_magic_string = engine.conf_info.fcst_multivar_name.string() + "_" + engine.conf_info.fcst_multivar_level.string(); - obs_magic_string = engine.conf_info.obs_multivar_name.string() + "_" + engine.conf_info.obs_multivar_level.string(); + return; - } else { +} - // just in case make these empty - fcst_magic_string = ""; - obs_magic_string = ""; +/////////////////////////////////////////////////////////////////////// - } - +void ModeExecutive::process_output_multivar_intensity_compare(const MultiVarData *mvdf, + const MultiVarData *mvdo) + +{ + isMultivarOutput = true; + isMultivarSuperOutput = false; + + // get the magic strings, which will be used in file naming + fcst_magic_string = engine.conf_info.Fcst->var_info->magic_str().c_str(); + obs_magic_string = engine.conf_info.Obs->var_info->magic_str().c_str(); + + // replace forward slashes with underscores to prevent new directories + replace(fcst_magic_string.begin(), fcst_magic_string.end(), '/', '_'); + replace(obs_magic_string.begin(), obs_magic_string.end(), '/', '_'); + + // replace (*,*) with '_all_all_' + replaceAll(fcst_magic_string, "*", "all"); + replaceAll(obs_magic_string, "*", "all"); + replaceAll(fcst_magic_string, ",", "_"); + replaceAll(obs_magic_string, ",", "_"); + replaceAll(fcst_magic_string, "(", "_"); + replaceAll(obs_magic_string, "(", "_"); + replaceAll(fcst_magic_string, ")", ""); + replaceAll(obs_magic_string, ")", ""); + // Create output stats files and plots write_obj_stats(); if ( engine.conf_info.ct_stats_flag ) write_ct_stats(); - - if (isMultivarOutput) { - if (mvdf && mvdo) { - double fmin = mvdf->_data_min; - double fmax = mvdf->_data_max; - double omin = mvdo->_data_min; - double omax = mvdo->_data_max; - double data_min, data_max; - if (!is_bad_data(fmin) && !is_bad_data(omin)) data_min = min(fmin, omin); - else if(!is_bad_data(fmin) && is_bad_data(omin)) data_min = fmin; - else if( is_bad_data(fmin) && !is_bad_data(omin)) data_min = omin; - - if (!is_bad_data(fmax) && !is_bad_data(omax)) data_max = max(fmax, omax); - else if(!is_bad_data(fmax) && is_bad_data(omax)) data_max = fmax; - else if( is_bad_data(fmax) && !is_bad_data(omax)) data_max = omax; + double fmin = mvdf->_data_min; + double fmax = mvdf->_data_max; + double omin = mvdo->_data_min; + double omax = mvdo->_data_max; + double data_min, data_max; + if (!is_bad_data(fmin) && !is_bad_data(omin)) data_min = min(fmin, omin); + else if(!is_bad_data(fmin) && is_bad_data(omin)) data_min = fmin; + else if( is_bad_data(fmin) && !is_bad_data(omin)) data_min = omin; + + if (!is_bad_data(fmax) && !is_bad_data(omax)) data_max = max(fmax, omax); + else if(!is_bad_data(fmax) && is_bad_data(omax)) data_max = fmax; + else if( is_bad_data(fmax) && !is_bad_data(omax)) data_max = omax; - set_raw_to_full(mvdf->_simple->_raw_data, - mvdo->_simple->_raw_data, - mvdf->_nx, mvdf->_ny, - data_min, data_max); - } else { - mlog << Error << "\nModeExecutive::process_output() -> " - << "no multivar data when multivar data is expected\n\n"; - exit(1); - } - } + set_raw_to_full(mvdf->_simple->_raw_data,mvdo->_simple->_raw_data, + mvdf->_nx, mvdf->_ny, data_min, data_max); + + write_obj_netcdf(engine.conf_info.nc_info); + + if ( engine.conf_info.ps_plot_flag ) plot_engine(); + + return; + +} + +/////////////////////////////////////////////////////////////////////// + +void ModeExecutive::process_output_traditional() +{ + isMultivarOutput = false; + isMultivarSuperOutput = false; + + // just in case make these empty + fcst_magic_string = ""; + obs_magic_string = ""; + + // Create output stats files and plots + + write_obj_stats(); + + if ( engine.conf_info.ct_stats_flag ) write_ct_stats(); + write_obj_netcdf(engine.conf_info.nc_info); if ( engine.conf_info.ps_plot_flag ) plot_engine(); @@ -1706,7 +1628,8 @@ MultiVarData *ModeExecutive::get_multivar_data(ModeDataType dtype) obs_magic_string = engine.conf_info.Obs->var_info->magic_str().c_str(); // replace forward slashes with underscores to prevent new directories replace(obs_magic_string.begin(), obs_magic_string.end(), '/', '_'); - mvd->init(dtype, obs_magic_string, grid, otype, ounits, olevel, data_min, data_max); + mvd->init(dtype, obs_magic_string, grid, + ounits, olevel, data_min, data_max); mvd->set_obj(engine.obs_split, simple); mvd->set_raw(engine.obs_raw, simple); mvd->set_shapedata(Obs_sd, simple); @@ -1717,7 +1640,8 @@ MultiVarData *ModeExecutive::get_multivar_data(ModeDataType dtype) fcst_magic_string = engine.conf_info.Fcst->var_info->magic_str().c_str(); // replace forward slashes with underscores to prevent new directories replace(fcst_magic_string.begin(), fcst_magic_string.end(), '/', '_'); - mvd->init(dtype, fcst_magic_string, grid, ftype, funits, flevel, data_min, data_max); + mvd->init(dtype, fcst_magic_string, grid, + funits, flevel, data_min, data_max); mvd->set_obj(engine.fcst_split, simple); mvd->set_raw(engine.fcst_raw, simple); mvd->set_shapedata(Fcst_sd, simple); @@ -2707,7 +2631,7 @@ void ModeExecutive::write_ct_stats() /////////////////////////////////////////////////////////////////////// -void ModeExecutive::conf_read(const string &default_config_filename) +void ModeExecutive::conf_read() { // Create the default config file name diff --git a/src/tools/core/mode/mode_exec.h b/src/tools/core/mode/mode_exec.h index 34c8d7fb5e..f3c9937d35 100644 --- a/src/tools/core/mode/mode_exec.h +++ b/src/tools/core/mode/mode_exec.h @@ -75,83 +75,118 @@ class ModeExecutive { // the various mode algorithm settings typedef enum {TRADITIONAL, MULTIVAR_SIMPLE, MULTIVAR_SIMPLE_MERGE, MULTIVAR_INTENSITY, MULTIVAR_SUPER} Processing_t; - ModeExecutive(Processing_t p=TRADITIONAL); + ModeExecutive(); ~ModeExecutive(); void clear(); void init_traditional(int n_files); - void init_multivar_verif_grid(); - void init_multivar_simple(int n_files, ModeDataType dtype); - void init_multivar_intensities(GrdFileType ftype, GrdFileType otype); - void check_multivar_perc_thresh_settings(); - + void init_multivar_simple(int j, int n_files, ModeDataType dtype, const ModeConfInfo &conf); + void init_multivar_intensities(const ModeConfInfo &conf); int n_conv_radii () const; int n_conv_threshs () const; - int n_runs() const; + // these are used only for traditional mode int R_index; // indices into the convolution radius and threshold arrays int T_index; // for the current run // - // Input configuration files + // Input configuration files, all 3 used only for traditional mode + // Multivar mode handles the configs outside of the exec // - + // the hardwired default traditional mode config ConcatString default_config_file; - ConcatString match_config_file; + + // set for both trad and multivar, this is the default file on the command line + // used only for traditional + ConcatString match_config_file; + + // the extra one that can be set only for traditional mode ConcatString merge_config_file; // - // Input files + // Input filenames, set for both multivar and trad mode + // but used only for trad mode // - ConcatString fcst_file; ConcatString obs_file; + + // set and used only for trad mode Met2dDataFile * fcst_mtddf; Met2dDataFile * obs_mtddf; + // used for both trad and multivar mode TTContingencyTable cts[n_cts]; + // used for both trad and multivar mode ModeFuzzyEngine engine; // verification grid + // used for both trad and multivar mode, set by both Grid grid; Box xy_bb; ConcatString out_dir; + + // set for both trad and multivar mode, used for plotting limits double data_min, data_max; + // set for trad and multivar, used in the engine mode algorithm ShapeData Fcst_sd, Obs_sd; + // not used by multivar GrdFileType ftype, otype; + + // set into execs's conf varInfo object, only for multivar intensity comparisons + // for trad it's read in from the config string funits, ounits; + + // set into execs's conf varInfo object, only for multivar intensity comparisons + // for trad it's read in from the config string flevel, olevel; + // used in multivar only to customize outputs correctly bool isMultivarOutput; bool isMultivarSuperOutput; - Processing_t ptype; - + void setup_verification_grid(const ModeInputData &fcst, + const ModeInputData &obs, + const ModeConfInfo &conf); + void clear_internal_r_index(); - void setup_verification_grid(); - void setup_fcst_obs_data_traditional(); - void setup_fcst_data(const Grid &verification_grid); - void setup_obs_data(const Grid &verification_grid); - void setup_fcst_obs_data_multivar_intensities(const MultiVarData &mvdf, const MultiVarData &mvdo); - void setup_fcst_obs_data_multivar_super(ShapeData &f_super, ShapeData &o_super, const Grid &igrid); - void do_conv_thresh(const int r_index, const int t_index); - void do_merging(); - void do_merging(ShapeData &f_merge, ShapeData &o_merge); - void do_match_merge(); - void do_match_merge(ShapeData &f_merge, ShapeData &o_merge); + + void setup_traditional_fcst_obs_data(); + void setup_multivar_fcst_data(const Grid &verification_grid, const ModeInputData &input); + void setup_multivar_obs_data(const Grid &verification_grid, const ModeInputData &input); + void setup_multivar_fcst_obs_data_intensities(const MultiVarData &mvdf, + const MultiVarData &mvdo); + void setup_multivar_fcst_obs_data_super(const ShapeData &f_super, + const ShapeData &o_super, + const Grid &igrid); + + void do_conv_thresh_traditional(const int r_index, const int t_index); + void do_conv_thresh_multivar_super(); + void do_conv_thresh_multivar_intensity_compare(); + void do_conv_thresh_multivar_simple(Processing_t p); + + void do_merging_traditional(); + void do_merging_multivar(const ShapeData &f_merge, const ShapeData &o_merge, + Processing_t p); + + void do_match_merge_traditional(); + void do_match_merge_multivar(const ShapeData &f_merge, const ShapeData &o_merge, + Processing_t p); void process_masks(ShapeData &, ShapeData &); void process_fcst_masks(ShapeData &); void process_obs_masks(ShapeData &); - void process_output(const MultiVarData *mvdf=NULL, - const MultiVarData *mvdo=NULL); + + void process_output_traditional(); + void process_output_multivar_intensity_compare(const MultiVarData *mvdf, + const MultiVarData *mvdo); + void process_output_multivar_super(); void set_raw_to_full(float *fcst_raw_data, float *obs_raw_data, @@ -178,7 +213,9 @@ class ModeExecutive { void write_poly_netcdf(netCDF::NcFile *, const ObjPolyType); void write_ct_stats(); - void conf_read(const string &default_config_filename); + // traditional only, multivar reads outside of the exec + void conf_read(); + static string stype(Processing_t t); }; diff --git a/src/tools/core/mode/mode_frontend.cc b/src/tools/core/mode/mode_frontend.cc index 456ae71f7a..4831cb59d2 100644 --- a/src/tools/core/mode/mode_frontend.cc +++ b/src/tools/core/mode/mode_frontend.cc @@ -35,9 +35,10 @@ using namespace std; extern const char * const program_name; static ModeExecutive *mode_exec = 0; -static ModeExecutive::Processing_t ptype = ModeExecutive::TRADITIONAL; -static int compress_level = -1; +// used only for traditional mode, multivar sets it into config previous +// to the frontend creation +static int compress_level = -1; /////////////////////////////////////////////////////////////////////// @@ -61,160 +62,13 @@ ModeFrontEnd::~ModeFrontEnd() } -/////////////////////////////////////////////////////////////////////// - - -Grid ModeFrontEnd::create_verification_grid(const StringArray & Argv) - -{ - if ( mode_exec ) { delete mode_exec; mode_exec = 0; } - mode_exec = new ModeExecutive; - compress_level = -1; - - // - // Process the command line arguments - // - - process_command_line(Argv, false); - - mode_exec->init_multivar_verif_grid(); - - ModeConfInfo & conf = mode_exec->engine.conf_info; - conf.set_field_index(0); - if (compress_level >= 0) conf.nc_info.set_compress_level(compress_level); - - - // - // read in data (Note multiple reads of same data) - // - mode_exec->setup_verification_grid(); - Grid g = mode_exec->grid; - delete mode_exec; mode_exec = 0; - return g; -} - - -/////////////////////////////////////////////////////////////////////// - -int ModeFrontEnd::create_multivar_simple_objects(const StringArray & Argv, ModeDataType dtype, - const Grid &verification_grid, int field_index, int n_files) - -{ - init(ModeExecutive::MULTIVAR_SIMPLE); - - // - // Process the command line arguments - // - - process_command_line_for_simple_objects(Argv, dtype); - - mode_exec->init_multivar_simple(n_files, dtype); - - ModeConfInfo & conf = mode_exec->engine.conf_info; - if ( field_index >= 0 ) conf.set_field_index(field_index); - if (compress_level >= 0) conf.nc_info.set_compress_level(compress_level); - - // need to do this after setting field index above - mode_exec->check_multivar_perc_thresh_settings(); - - // - // read in data (Note multiple reads of data) - // - - if (dtype == ModeDataType_MvMode_Fcst) { - mode_exec->setup_fcst_data(verification_grid); - } else { - mode_exec->setup_obs_data(verification_grid); - } - - // - // mode algorithm - // - if ( conf.quilt ) { - - do_quilt(); - - } else { - - do_straight(); - - } - - // - // done - // - -#ifdef WITH_PYTHON - GP.finalize(); -#endif - return (0); -} - -/////////////////////////////////////////////////////////////////////// - -int ModeFrontEnd::create_multivar_merge_objects(const StringArray & Argv, ModeDataType dtype, - const Grid &verification_grid, int field_index, - int n_files) - -{ - init(ModeExecutive::MULTIVAR_SIMPLE_MERGE); - - // - // Process the command line arguments - // - - process_command_line_for_simple_objects(Argv, dtype); - - mode_exec->init_multivar_simple(n_files, dtype); - - ModeConfInfo & conf = mode_exec->engine.conf_info; - if ( field_index >= 0 ) conf.set_field_index(field_index); - if (compress_level >= 0) conf.nc_info.set_compress_level(compress_level); - - // need to do this after setting field index above - mode_exec->check_multivar_perc_thresh_settings(); - - - // - // read in data (Note multiple reads not desired) - // - - if (dtype == ModeDataType_MvMode_Fcst) { - mode_exec->setup_fcst_data(verification_grid); - } else { - mode_exec->setup_obs_data(verification_grid); - } - - - // - // mode algorithm - // - if ( conf.quilt ) { - - do_quilt(); - - } else { - - do_straight(); - - } - - // - // done - // - -#ifdef WITH_PYTHON - GP.finalize(); -#endif - return (0); -} /////////////////////////////////////////////////////////////////////// int ModeFrontEnd::run_traditional(const StringArray & Argv) { - init(ModeExecutive::TRADITIONAL); + init(); int field_index = -1; int n_files = 1; @@ -223,7 +77,7 @@ int ModeFrontEnd::run_traditional(const StringArray & Argv) // Process the command line arguments // - process_command_line(Argv, false); + process_command_line(Argv); mode_exec->init_traditional(n_files); @@ -236,7 +90,7 @@ int ModeFrontEnd::run_traditional(const StringArray & Argv) // read in data // - mode_exec->setup_fcst_obs_data_traditional(); + mode_exec->setup_traditional_fcst_obs_data(); // // mode algorithm @@ -263,129 +117,6 @@ int ModeFrontEnd::run_traditional(const StringArray & Argv) /////////////////////////////////////////////////////////////////////// -int ModeFrontEnd::multivar_intensity_comparisons(const StringArray & Argv, const MultiVarData &mvdf, - const MultiVarData &mvdo, bool has_union_f, - bool has_union_o, ShapeData &merge_f, - ShapeData &merge_o, int field_index_f, int field_index_o) -{ - init(ModeExecutive::MULTIVAR_INTENSITY); - - // - // Process the command line arguments - // - - process_command_line(Argv, false); - - mode_exec->init_multivar_intensities(mvdf._type, mvdo._type); - - ModeConfInfo & conf = mode_exec->engine.conf_info; - if (compress_level >= 0) conf.nc_info.set_compress_level(compress_level); - conf.set_field_index(field_index_f, field_index_o); - - // for multivar intensities, explicity set the level and units using stored values - // from pass1 - conf.Fcst->var_info->set_level_name(mvdf._level.c_str()); - conf.Fcst->var_info->set_units(mvdf._units.c_str()); - if (has_union_f && conf.Fcst->merge_flag == MergeType_Thresh) { - mlog << Warning << "\nModeFrontEnd::multivar_intensity_comparisons() -> " - << "Logic includes union '||' along with 'merge_flag=THRESH' " - << ". This can lead to bad results\n\n"; - } - conf.Obs->var_info->set_level_name(mvdo._level.c_str()); - conf.Obs->var_info->set_units(mvdo._units.c_str()); - if (has_union_o && conf.Obs->merge_flag == MergeType_Thresh) { - mlog << Warning << "\nModeFrontEnd::multivar_intensity_comparisons() -> " - << "Logic includes union '||' along with 'merge_flag=THRESH' " - << ". This can lead to bad results\n\n"; - } - - // - // set up data access using inputs - // - mode_exec->setup_fcst_obs_data_multivar_intensities(mvdf, mvdo); - - // - // run the mode algorithm - // - - if ( conf.quilt ) { - - do_quilt(); - - } else { - - do_straight_multivar_intensity(mvdf, mvdo, merge_f, merge_o); - - } - - // - // done - // - -#ifdef WITH_PYTHON - GP.finalize(); -#endif - return (0); -} - -/////////////////////////////////////////////////////////////////////// - -int ModeFrontEnd::run_super(const StringArray & Argv, - ShapeData &f_super, ShapeData &o_super, - ShapeData &f_merge, ShapeData &o_merge, - GrdFileType ftype, GrdFileType otype, const Grid &grid, - bool has_union) -{ - init(ModeExecutive::MULTIVAR_SUPER); - - // - // Process the command line arguments - // - - process_command_line(Argv, true); - - mode_exec->init_multivar_intensities(ftype, otype); - - ModeConfInfo & conf = mode_exec->engine.conf_info; - if (compress_level >= 0) conf.nc_info.set_compress_level(compress_level); - if (has_union && (conf.Fcst->merge_flag == MergeType_Thresh || - conf.Obs->merge_flag == MergeType_Thresh)) { - mlog << Warning << "\nModeFrontEnd::run_super() -> " - << "Logic includes union '||' along with 'merge_flag=THRESH' " - << ". This can lead to bad results\n\n"; - } - - // - // set up data access using inputs - // - mode_exec->setup_fcst_obs_data_multivar_super(f_super, o_super, grid); - - // - // run the mode algorithm - // - - if ( conf.quilt ) { - - do_quilt(); - - } else { - - do_straight_multivar_super(f_merge, o_merge); - - } - - // - // done - // - -#ifdef WITH_PYTHON - GP.finalize(); -#endif - return (0); -} - -/////////////////////////////////////////////////////////////////////// - void ModeFrontEnd::do_straight() @@ -398,78 +129,9 @@ void ModeFrontEnd::do_straight() for (int index=0; indexdo_conv_thresh(index, index); - if (ptype == ModeExecutive::TRADITIONAL) { - - mode_exec->do_match_merge(); - mode_exec->process_output(); - } - } - - mode_exec->clear_internal_r_index(); - - // - // done - // - - return; - -} - -/////////////////////////////////////////////////////////////////////// - - -void ModeFrontEnd::do_straight_multivar_intensity(const MultiVarData &mvdf, - const MultiVarData &mvdo, - ShapeData &f_merge, - ShapeData &o_merge) - -{ - int NCT, NCR; - - do_straight_init(NCT, NCR); - - mode_exec->clear_internal_r_index(); - - for (int index=0; indexdo_conv_thresh(index, index); - mode_exec->do_match_merge(f_merge, o_merge); - - // here replace raw data and min/max for plotting - - mode_exec->process_output(&mvdf, &mvdo); - } - - mode_exec->clear_internal_r_index(); - - // - // done - // - - return; - -} - - - -/////////////////////////////////////////////////////////////////////// - - -void ModeFrontEnd::do_straight_multivar_super(ShapeData &f_merge, ShapeData &o_merge) - -{ - int NCT, NCR; - - do_straight_init(NCT, NCR); - - mode_exec->clear_internal_r_index(); - - for (int index=0; indexdo_conv_thresh(index, index); - mode_exec->do_match_merge(f_merge, o_merge); - mode_exec->process_output(); + mode_exec->do_conv_thresh_traditional(index, index); + mode_exec->do_match_merge_traditional(); + mode_exec->process_output_traditional(); } mode_exec->clear_internal_r_index(); @@ -482,19 +144,12 @@ void ModeFrontEnd::do_straight_multivar_super(ShapeData &f_merge, ShapeData &o_m } - /////////////////////////////////////////////////////////////////////// void ModeFrontEnd::do_quilt() { - if (ptype != ModeExecutive::TRADITIONAL) { - mlog << Error << "\nModeFrontend::do_quilt() -> quilting not yet implemented for multivar mode \n\n"; - exit ( 1 ); - } - - int t_index, r_index; // indices into the convolution threshold and radius arrays @@ -503,14 +158,9 @@ void ModeFrontEnd::do_quilt() for (r_index=0; r_index<(mode_exec->n_conv_radii()); ++r_index) { for (t_index=0; t_index<(mode_exec->n_conv_threshs()); ++t_index) { - - mode_exec->do_conv_thresh(r_index, t_index); - - mode_exec->do_match_merge(); - - if (ptype == ModeExecutive::TRADITIONAL) { - mode_exec->process_output(); - } + mode_exec->do_conv_thresh_traditional(r_index, t_index); + mode_exec->do_match_merge_traditional(); + mode_exec->process_output_traditional(); } } @@ -526,29 +176,13 @@ void ModeFrontEnd::do_quilt() /////////////////////////////////////////////////////////////////////// -MultiVarData *ModeFrontEnd::get_multivar_data(ModeDataType dtype) -{ - return mode_exec->get_multivar_data(dtype); -} - - -/////////////////////////////////////////////////////////////////////// - -void ModeFrontEnd::add_multivar_merge_data(MultiVarData *mvdi, ModeDataType dtype) -{ - return mode_exec->add_multivar_merge_data(mvdi, dtype); -} - -/////////////////////////////////////////////////////////////////////// - -void ModeFrontEnd::init(ModeExecutive::Processing_t p) +void ModeFrontEnd::init() { - ptype = p; - mlog << Debug(1) << "Running multivar front end for " << ModeExecutive::stype(ptype) << "\n"; + mlog << Debug(1) << "Running traditional mode front end\n"; if ( mode_exec ) { delete mode_exec; mode_exec = 0; } - mode_exec = new ModeExecutive(ptype); + mode_exec = new ModeExecutive();//ModeExecutive::TRADITIONAL); compress_level = -1; } @@ -569,20 +203,12 @@ void ModeFrontEnd::do_straight_init(int &NCT, int &NCR) const exit ( 1 ); } - - if (NCT > 1 && ptype != ModeExecutive::TRADITIONAL) { - - mlog << Error << "\nModeFrontEnd::do_straight_init() ->" - << ": multiple convolution radii and thresholds not implemented in multivar mode\n\n"; - - exit ( 1 ); - } } /////////////////////////////////////////////////////////////////////// -void ModeFrontEnd::process_command_line(const StringArray & argv, bool ismultivar) +void ModeFrontEnd::process_command_line(const StringArray & argv) { CommandLine cline; ConcatString s; @@ -595,7 +221,7 @@ void ModeFrontEnd::process_command_line(const StringArray & argv, bool ismultiva mode_exec->out_dir = replace_path(default_out_dir); // - // Check for zero arguments (note not correct for multivar mode, want to show multivar_usage + // Check for zero arguments // if(argc == 1) singlevar_usage(); @@ -607,7 +233,7 @@ void ModeFrontEnd::process_command_line(const StringArray & argv, bool ismultiva cline.set(argv); // - // Set the usage function NOTE wrong for multivar, want multivar_usage + // Set the usage function // cline.set_usage(singlevar_usage); @@ -628,103 +254,19 @@ void ModeFrontEnd::process_command_line(const StringArray & argv, bool ismultiva cline.parse(); - if (ismultivar) { - // - // Check for error. There should be 1 argument left: - // config filename - // - if(cline.n() != 1) singlevar_usage(); // wrong need multivar usage - - // - // Store the input forecast and observation file names, placeholders - // - mode_exec->fcst_file = "not set"; - mode_exec->obs_file = "not set"; - mode_exec->match_config_file = cline[0]; - - } else { - // - // Check for error. There should be three arguments left: - // forecast, observation, and config filenames - // - if(cline.n() != 3) singlevar_usage(); - - // - // Store the input forecast and observation file names - // - mode_exec->fcst_file = cline[0]; - mode_exec->obs_file = cline[1]; - mode_exec->match_config_file = cline[2]; - - } -} - - -/////////////////////////////////////////////////////////////////////// - -void ModeFrontEnd::process_command_line_for_simple_objects(const StringArray &argv, ModeDataType dtype) -{ - CommandLine cline; - ConcatString s; - const int argc = argv.n(); - - // - // Set the default output directory - // - - mode_exec->out_dir = replace_path(default_out_dir); - - // - // Check for zero arguments (note not correct for multivar mode, want to show multivar_usage - // - - if(argc == 1) singlevar_usage(); - - // - // Parse the command line into tokens - // - - cline.set(argv); - - // - // Set the usage function NOTE wrong for multivar, want multivar_usage - // - - cline.set_usage(singlevar_usage); - - // - // Add the options function calls - // - - cline.add(set_config_merge_file, "-config_merge", 1); - cline.add(set_outdir, "-outdir", 1); - cline.add(set_logfile, "-log", 1); - cline.add(set_verbosity, "-v", 1); - cline.add(set_compress, "-compress", 1); - // - // Parse the command line + // Check for error. There should be three arguments left: + // forecast, observation, and config filenames // - - cline.parse(); + if(cline.n() != 3) singlevar_usage(); // - // Check for error. There should be two arguments left: - // data and config filenames + // Store the input forecast and observation file names // - if(cline.n() != 2) singlevar_usage(); + mode_exec->fcst_file = cline[0]; + mode_exec->obs_file = cline[1]; + mode_exec->match_config_file = cline[2]; - // - // Store the file name - // - if (dtype == ModeDataType_MvMode_Fcst) { - mode_exec->fcst_file = cline[0]; - mode_exec->obs_file = "None"; - } else { - mode_exec->obs_file = cline[0]; - mode_exec->fcst_file = "None"; - } - mode_exec->match_config_file = cline[1]; } /////////////////////////////////////////////////////////////////////// diff --git a/src/tools/core/mode/mode_frontend.h b/src/tools/core/mode/mode_frontend.h index 55ecdf7f50..658f90ff4c 100644 --- a/src/tools/core/mode/mode_frontend.h +++ b/src/tools/core/mode/mode_frontend.h @@ -19,8 +19,6 @@ #include #include "mode_exec.h" #include "string_array.h" -#include "multivar_data.h" -#include "mode_data_type.h" class ModeFrontEnd { @@ -34,54 +32,21 @@ class ModeFrontEnd { string default_out_dir; - Grid create_verification_grid(const StringArray & Argv); - - // run the multivar simple object, where there is only one input data, either forecast or obs - int create_multivar_simple_objects(const StringArray & Argv, ModeDataType dtype, const Grid &verification_grid, - int field_index=-1, int n_files=1); - - // run the multivar simple object merge algorithm, with one input data, either forecast or obs - int create_multivar_merge_objects(const StringArray & Argv, ModeDataType dtype, const Grid &verification_grid, - int field_index=-1, int n_files=1); // run the default single var mode interface (traditional mode) int run_traditional(const StringArray & Argv); - // run the multivar intensity algorithm, where one forecast and one obs are restricted to be within superobjects - // and the traditional mode algorithm compares them - int multivar_intensity_comparisons(const StringArray & Argv, const MultiVarData &mvdf, const MultiVarData &mvdo, - bool has_union_f, bool has_union_o, ShapeData &merge_f, - ShapeData &merge_o, int field_index_f, int field_index_o); - - // multivar superobject interface, with no intensities - int run_super(const StringArray & Argv, ShapeData &f_super, ShapeData &o_super, - ShapeData &f_merge, ShapeData &o_merge, - GrdFileType ftype, GrdFileType otype, const Grid &grid, bool has_union); - + void init(); + // so far only implemented for traditional mode void do_quilt (); - // MODE algorithm for traditional, multivar simple, or multivar merge cases + // MODE algorithm for traditional mode void do_straight (); - // MODE algorithm when doing multivar intensities - void do_straight_multivar_intensity (const MultiVarData &mvdf, - const MultiVarData &mvdo, ShapeData &mergef, - ShapeData &mergeo); - - // MODE algorithm when doing multivar super with no intensities - void do_straight_multivar_super (ShapeData &f_merge, ShapeData &o_merge); - - - MultiVarData *get_multivar_data(ModeDataType dtype); - - void add_multivar_merge_data(MultiVarData *mvdi, ModeDataType dtype); - - void init(ModeExecutive::Processing_t p); void do_straight_init(int &NCT, int &NCR) const; - void process_command_line_for_simple_objects(const StringArray &, ModeDataType dtype); - void process_command_line(const StringArray &, bool is_multivar); + void process_command_line(const StringArray &); static void set_config_merge_file (const StringArray &); static void set_outdir (const StringArray &); diff --git a/src/tools/core/mode/mode_superobject.cc b/src/tools/core/mode/mode_superobject.cc new file mode 100644 index 0000000000..27d1c3092f --- /dev/null +++ b/src/tools/core/mode/mode_superobject.cc @@ -0,0 +1,184 @@ +using namespace std; +#include "mode_superobject.h" +#include "multivar_data.h" + +//////////////////////////////////////////////////////////////////////// +static void _mask_super(const string &name, int nx, int ny, DataPlane &data) +{ + + if (nx != data.nx() || ny != data.ny()) { + mlog << Error << "\nModeSuperObject::mask_data_super() -> " << name + << " :dimensions don't match " << nx << " " << ny + << " " << data.nx() << " " << data.ny() << "\n\n"; + + exit( 1 ); + } + + int nmasked=0, nkeep=0; + + for (int x=0; x " << name + << " :dimensions don't match " << nx << " " << ny + << " " << data.nx() << " " << data.ny() << "\n\n"; + + exit( 1 ); + } + + int nmasked=0, nkeep=0; + + for (int x=0; x values; + vector count; + for (int x=0; x::iterator vi; + vi = find(values.begin(), values.end(), v); + if (vi == values.end()) { + values.push_back(v); + count.push_back(1); + } else { + int ii = vi - values.begin(); + count[ii] = count[ii] + 1; + } + } + } + for (size_t i=0; i &mvd, + BoolCalc &calc) +{ + _hasUnion = calc.has_union(); + + // + // set the BoolPlane values using the mvd content + // + + BoolPlane * simple_plane = new BoolPlane [n_files]; + BoolPlane * merge_plane = new BoolPlane [n_files]; + + for (int j=0; jobjects_from_arrays(do_clusters, true, simple_plane[j]); + mvd[j]->objects_from_arrays(do_clusters, false, merge_plane[j]); + } + + // + // combine the objects into super-objects + // + const int nx = simple_plane[0].nx(); + const int ny = simple_plane[0].ny(); + + BoolPlane merge_result; // local, not used + _simple_result.set_size(nx, ny); + merge_result.set_size(nx, ny); + + string simple_name, merge_name; + + if (isFcst) { + simple_name = "Fcst_Simple"; + merge_name = "Fcst_Merge"; + } else { + simple_name = "Obs_Simple"; + merge_name = "Obs_Merge"; + } + combine_boolplanes(simple_name, simple_plane, n_files, calc, _simple_result); + combine_boolplanes(merge_name, merge_plane, n_files, calc, merge_result); + + // create ShapeData objects using something from mvd as a template + // (shape data has 1's or bad) + + _simple_sd = ShapeData(*(mvd[0]->_simple->_sd)); + for (int x=0; x_simple->_sd)); + for (int x=0; x_sd->data); +} + + +void ModeSuperObject::mask_data_super(const string &name, const MultiVarData &mvd) +{ + int nx = mvd._nx; + int ny = mvd._ny; + _mask_super(name, nx, ny, _simple_sd.data); +} diff --git a/src/tools/core/mode/mode_superobject.h b/src/tools/core/mode/mode_superobject.h new file mode 100644 index 0000000000..83278b20a6 --- /dev/null +++ b/src/tools/core/mode/mode_superobject.h @@ -0,0 +1,51 @@ +// ** Copyright UCAR (c) 1992 - 2023 +// ** University Corporation for Atmospheric Research (UCAR) +// ** National Center for Atmospheric Research (NCAR) +// ** Research Applications Lab (RAL) +// ** P.O.Box 3000, Boulder, Colorado, 80307-3000, USA +// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* + + +//////////////////////////////////////////////////////////////////////// + + +#ifndef __MODE_SUPEROBJECT_H__ +#define __MODE_SUPEROBJECT_H__ + + +//////////////////////////////////////////////////////////////////////// + + +#include "combine_boolplanes.h" +#include "multivar_data.h" +#include "shapedata.h" +#include +#include + +class MultiVarData; + +class ModeSuperObject { + + private: + + public: + + ModeSuperObject(bool isFcst, int n_files, bool do_clusters, + const vector &mvd, + BoolCalc &calc); + inline ~ModeSuperObject() {} + + void mask_data_simple(const string &name, MultiVarData &mvd) const; + void mask_data_super(const string &name, const MultiVarData &mvd); + + bool _isFcst; + bool _hasUnion; + BoolPlane _simple_result; + ShapeData _simple_sd; + ShapeData _merge_sd_split; +}; + + +#endif /* __MODE_SUPEROBJECT_H__ */ + +///////////////////////////////////////////////////////////////////////// diff --git a/src/tools/core/mode/multivar_data.cc b/src/tools/core/mode/multivar_data.cc index 1d38d270ba..3cde9ccf83 100644 --- a/src/tools/core/mode/multivar_data.cc +++ b/src/tools/core/mode/multivar_data.cc @@ -142,8 +142,7 @@ MultiVarData::MultiVarData() : _merge(0), _name("notset"), _nx(0), _ny(0), - _grid(0), - _type(FileType_None) + _grid(0) { } @@ -152,23 +151,10 @@ MultiVarData::~MultiVarData() _clear(); } -void MultiVarData::checkFileTypeConsistency(const MultiVarData &mvdi, int j) -{ - bool err = false; - if (_type != mvdi._type) { - mlog << Error << "MultivarData::checkFileTypeConsistgency() -> " - << "inputs of different file types not supported " - << "Input 0:" << grdfiletype_to_string(_type).c_str() - << "Input " << j << ":" << grdfiletype_to_string(mvdi._type).c_str() - << "\n\n"; - exit ( 1 ); - } -} - void MultiVarData::init(ModeDataType dataType, const string &name, - const Grid &grid, GrdFileType type, - const string &units, + const Grid &grid, + const string &units, const string &level, double data_min, double data_max) { @@ -179,7 +165,6 @@ void MultiVarData::init(ModeDataType dataType, _nx = grid.nx(); _ny = grid.ny(); _grid = new Grid(grid); - _type = type; _units = units; _level = level; _data_min = data_min; @@ -286,7 +271,6 @@ void MultiVarData::_clear() _grid = 0; } _nx = _ny = 0; - _type = FileType_None; } diff --git a/src/tools/core/mode/multivar_data.h b/src/tools/core/mode/multivar_data.h index 142e1aa65b..f93b7f9ac5 100644 --- a/src/tools/core/mode/multivar_data.h +++ b/src/tools/core/mode/multivar_data.h @@ -85,13 +85,11 @@ class MultiVarData { MultiVarData(); ~MultiVarData(); - void checkFileTypeConsistency(const MultiVarData &mvdi, int j); - void init(ModeDataType dataType, const string &name, - const Grid &grid, GrdFileType type, + const Grid &grid, const string &units, - const string &level, + const string &level, double data_min, double data_max); void set_obj(ShapeData *sd, bool simple); @@ -109,7 +107,6 @@ class MultiVarData { string _name; int _nx, _ny; Grid *_grid; - GrdFileType _type; string _units; string _level; double _data_min, _data_max; diff --git a/src/tools/core/mode/multivar_frontend.cc b/src/tools/core/mode/multivar_frontend.cc index 2205fc4329..6bd3a5aa57 100644 --- a/src/tools/core/mode/multivar_frontend.cc +++ b/src/tools/core/mode/multivar_frontend.cc @@ -6,170 +6,88 @@ // ** P.O.Box 3000, Boulder, Colorado, 80307-3000, USA // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* - -//////////////////////////////////////////////////////////////////////// - - -// for multivar mode, this is the default file -static const char mode_default_config [] = "MET_BASE/config/MODEMultivarConfig_default"; - -static const int dir_creation_mode = 0755; - //////////////////////////////////////////////////////////////////////// using namespace std; -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "vx_util.h" -#include "file_exists.h" -#include "two_d_array.h" -#include "get_filenames.h" -#include "mode_conf_info.h" -#include "shapedata.h" -#include "interest.h" -#include "met_file.h" -#include "mode_usage.h" -#include "mode_exec.h" - -#include "combine_boolplanes.h" -#include "objects_from_netcdf.h" -#include "parse_file_list.h" -#include "mode_frontend.h" -#include "multivar_data.h" -#include "mode_data_type.h" +#include "multivar_frontend.h" -using namespace netCDF; +#include "mode_usage.h" +#ifdef WITH_PYTHON +#include "global_python.h" +#endif //////////////////////////////////////////////////////////////////////// extern const char * const program_name; - static const char sep [] = "===================================================="; -static const char tab [] = " "; - -// this is hardwired for the multivar case, at least for now - -static const bool do_clusters = false; +static string outdir; +static int compress_level = -1; -static string default_out_dir = "."; +// for multivar mode, this is the default file +static const char mode_default_config [] = "MET_BASE/config/MODEMultivarConfig_default"; -static ModeConfInfo config; +static const int dir_creation_mode = 0755; -static string mode_path; -static string fcst_fof; -static string obs_fof; -static string config_file; -static string outdir; +static ModeExecutive *mode_exec = 0; -static Grid verification_grid; //////////////////////////////////////////////////////////////////////// - -static void set_outdir (const StringArray &); -static void set_logfile (const StringArray &); -static void set_verbosity (const StringArray &); - -static void multivar_consistency_checks(StringArray &fcst_filenames, StringArray &obs_filenames, - BoolCalc &f_calc, BoolCalc &o_calc, int &n_fcst_files, - int &n_obs_files); - -static ConcatString set_multivar_dir(); - -static void create_verification_grid(const string &fcst_filename, - const string &obs_filename, - const ConcatString &dir); - -static MultiVarData *create_simple_objects(ModeDataType dtype, int j, int n_files, - const string &filename, - const ConcatString &dir); - -static void create_superobjects(int n_fcst_files, const vector &mvdFcst, - int n_obs_files, const vector &mvdObs, - BoolCalc &f_calc, BoolCalc &o_calc, - BoolPlane &f_simple_result, BoolPlane &o_simple_result, - ShapeData &f_simple_sd, ShapeData &o_simple_sd, - ShapeData &f_merge_sd_split, ShapeData &o_merge_sd_split); - -static void create_intensity_comparisons(int findex, int oindex, const BoolPlane &f_result, - BoolPlane &o_result, - const ConcatString &dir, - MultiVarData &mvdf, MultiVarData &mvdo, - bool has_union_f, bool has_union_o, - const string &fcst_filename, const string &obs_filename, - ShapeData &merge_f, ShapeData &merge_o); - -static void process_superobjects(ShapeData &f_result, ShapeData &o_result, - ShapeData &f_merge, ShapeData &o_merge, - int nx, int ny, const ConcatString &dir, - GrdFileType ftype, GrdFileType otype, const Grid &grid, bool has_union); - -static void mask_data(const string &name, int nx, int ny, const BoolPlane &mask, DataPlane &data); -static void mask_data_super(const string &name, int nx, int ny, DataPlane &data); - -static void read_config(const string & filename); - -static void process_command_line(const StringArray &); - -static int _mkdir(const char *dir); - -static void _debug_shape_examine(string &name, const ShapeData &sd, int nx, int ny); - +MultivarFrontEnd::MultivarFrontEnd() +{ + // this is hardwired for the multivar case, at least for now + do_clusters = false; + default_out_dir = "."; + compress_level = -1; + mode_exec = 0; +} //////////////////////////////////////////////////////////////////////// - -int multivar_frontend(const StringArray & Argv) +int MultivarFrontEnd::run(const StringArray & Argv) { - const int Argc = Argv.n(); + // initialize - if ( Argc < 4 ) multivar_usage(); - - int j, n_fcst_files, n_obs_files; - StringArray fcst_filenames; - StringArray obs_filenames; - BoolCalc f_calc, o_calc ; + init(Argv); - process_command_line(Argv); + mlog << Debug(2) << "\n" << sep << "\n"; - read_config(config_file); + // read in all the data - multivar_consistency_checks(fcst_filenames, obs_filenames, f_calc, o_calc, - n_fcst_files, n_obs_files); + // in the conf object, shift *can* be set independently for obs and fcst + int shift = config.shift_right; - bool f_has_union = f_calc.has_union(); - bool o_has_union = o_calc.has_union(); + for (int i=0; i mvdObs, mvdFcst; - - for (j=0; j 0) { - mvdFcst[0]->checkFileTypeConsistency(*mvdi, j); - } + << "\n" << sep << "\ncreating simple forecast objects from forecast " + << (j + 1) << " of " << n_fcst_files << "\n" << sep << "\n"; + MultiVarData *mvdi = create_simple_objects(ModeDataType_MvMode_Fcst, j, + n_fcst_files, fcst_filenames[j], + fcstInput[j]); mvdFcst.push_back(mvdi); mvdi->print(); } // for j - for (j=0; j 0) { - mvdObs[0]->checkFileTypeConsistency(*mvdi, j); - } + << "\n" << sep << "\ncreating simple obs objects from obs " + << (j + 1) << " of " << n_obs_files << "\n" << sep << "\n"; + MultiVarData *mvdi = create_simple_objects(ModeDataType_MvMode_Obs, j, + n_obs_files, obs_filenames[j], + obsInput[j]); mvdObs.push_back(mvdi); mvdi->print(); } // for j @@ -207,72 +123,86 @@ int multivar_frontend(const StringArray & Argv) // now create forecast and obs superobjects - BoolPlane f_simple_result, o_simple_result; - ShapeData f_simple_sd, o_simple_sd, f_merge_sd_split, o_merge_sd_split; - - create_superobjects(n_fcst_files, mvdFcst, n_obs_files, mvdObs, - f_calc, o_calc, f_simple_result, o_simple_result, - f_simple_sd, o_simple_sd, - f_merge_sd_split, o_merge_sd_split); - + ModeSuperObject fsuper(true, n_fcst_files, do_clusters, mvdFcst, f_calc); + ModeSuperObject osuper(true, n_obs_files, do_clusters, mvdObs, o_calc); + // - // Filter the data to within the superobjects only and do statistics by invoking mode algorithm again - // on the masked data pairs + // Filter the data to within the superobjects only and do statistics by invoking mode + // algorithm again on the masked data pairs // + for (int k=0; k_nx; - int ny = mvdFcst[0]->_ny; - GrdFileType ftype = mvdFcst[0]->_type; - GrdFileType otype = mvdObs[0]->_type; - Grid grid = *(mvdFcst[0]->_grid); + if (config.fcst_multivar_compare_index.n() <= 0) { - // here run one more time using superobjects as input + process_superobjects(fsuper, osuper, *mvdFcst[0], *mvdObs[0]); + } + + // + // done + // + return (0); +} - bool has_union = f_calc.has_union() || o_calc.has_union(); +//////////////////////////////////////////////////////////////////////// - process_superobjects(f_simple_sd, o_simple_sd, f_merge_sd_split, o_merge_sd_split, - nx, ny, dir, ftype, otype, grid, has_union); +MultivarFrontEnd::~MultivarFrontEnd() +{ + if ( mode_exec ) { + delete mode_exec; mode_exec = 0; } - - // free up memory - for (j=0; jgrid(); + GrdFileType ft = f->file_type(); + + //? + f->set_shift_right(shift); + + // update config now that we know file type (this sets Fcst to index i) + DataPlane dp; + + if (type == ModeDataType_MvMode_Fcst) { + config.process_config_field(ft, other_t, type, index); + f->data_plane(*(config.Fcst->var_info), dp); + fcstInput.push_back(ModeInputData(name, dp, g)); + } else { + config.process_config_field(other_t, ft, type, index); + f->data_plane(*(config.Obs->var_info), dp); + obsInput.push_back(ModeInputData(name, dp, g)); + } + + delete f; +} + +//////////////////////////////////////////////////////////////////////// - ConcatString path; +void MultivarFrontEnd::create_verif_grid() +{ + mlog << Debug(2) << "\n creating the verification grid \n" << sep << "\n"; - path = replace_path(mode_default_config); + _init_exec(ModeExecutive::TRADITIONAL, "None", "None"); + mode_exec->setup_verification_grid(fcstInput[0], obsInput[0], config); + verification_grid = mode_exec->grid; + delete mode_exec; mode_exec = 0; +} - config.read_config(path.c_str(), filename.c_str()); +//////////////////////////////////////////////////////////////////////// - return; +MultiVarData *MultivarFrontEnd::create_simple_objects(ModeDataType dtype, int j, + int n_files, + const string &filename, + const ModeInputData &input) +{ + // + // create simple non merged objects + // + _simple_objects(ModeExecutive::MULTIVAR_SIMPLE, dtype, j, n_files, + filename, input); + MultiVarData *mvdi = mode_exec->get_multivar_data(dtype); + delete mode_exec; mode_exec = 0; + // + // create simple merged objects + // + _simple_objects(ModeExecutive::MULTIVAR_SIMPLE_MERGE, dtype, j, n_files, + filename, input); + mode_exec->add_multivar_merge_data(mvdi, dtype); + delete mode_exec; mode_exec = 0; + return mvdi; } +//////////////////////////////////////////////////////////////////////// + +void +MultivarFrontEnd::create_intensity_comparisons(int findex, int oindex, + const ModeSuperObject &fsuper, + const ModeSuperObject &osuper, + MultiVarData &mvdf, MultiVarData &mvdo, + const string &fcst_filename, + const string &obs_filename) +{ + + // mask the input data to be valid only inside the simple super objects + fsuper.mask_data_simple("Fcst", mvdf); + osuper.mask_data_simple("Obs", mvdo); + + mlog << Debug(1) << "Running mvmode intensity comparisions \n\n"; + + _init_exec(ModeExecutive::MULTIVAR_INTENSITY, fcst_filename, obs_filename); + mode_exec->init_multivar_intensities(config); + + ModeConfInfo & conf = mode_exec->engine.conf_info; + conf.set_field_index(findex, oindex); + + // for multivar intensities, explicity set the level and units using stored values + // from pass1 + conf.Fcst->var_info->set_level_name(mvdf._level.c_str()); + conf.Fcst->var_info->set_units(mvdf._units.c_str()); + if (fsuper._hasUnion && conf.Fcst->merge_flag == MergeType_Thresh) { + mlog << Warning << "\nModeFrontEnd::multivar_intensity_comparisons() -> " + << "Logic includes union '||' along with 'merge_flag=THRESH' " + << ". This can lead to bad results\n\n"; + } + conf.Obs->var_info->set_level_name(mvdo._level.c_str()); + conf.Obs->var_info->set_units(mvdo._units.c_str()); + if (osuper._hasUnion && conf.Obs->merge_flag == MergeType_Thresh) { + mlog << Warning << "\nModeFrontEnd::multivar_intensity_comparisons() -> " + << "Logic includes union '||' along with 'merge_flag=THRESH' " + << ". This can lead to bad results\n\n"; + } + + // + // set up data access using inputs + // + mode_exec->setup_multivar_fcst_obs_data_intensities(mvdf, mvdo); + + // + // run the mode algorithm for multivar intensities + // + _intensity_compare_mode_algorithm(mvdf, mvdo, fsuper, osuper); + + delete mode_exec; mode_exec = 0; +} //////////////////////////////////////////////////////////////////////// +void MultivarFrontEnd::process_superobjects(ModeSuperObject &fsuper, + ModeSuperObject &osuper, + const MultiVarData &mvdf, + const MultiVarData &mvdo) +{ + mlog << Debug(1) << "Running superobject mode \n\n"; + + // set the data to 0 inside superobjects and missing everywhere else + + fsuper.mask_data_super("FcstSimple", mvdf); + osuper.mask_data_super("ObsSimple", mvdo); + + _init_exec(ModeExecutive::MULTIVAR_SUPER, "None", "None"); + mode_exec->init_multivar_intensities(config); + + ModeConfInfo & conf = mode_exec->engine.conf_info; + if ((fsuper._hasUnion || osuper._hasUnion) && + (conf.Fcst->merge_flag == MergeType_Thresh || + conf.Obs->merge_flag == MergeType_Thresh)) { + mlog << Warning << "\nModeFrontEnd::run_super() -> " + << "Logic includes union '||' along with 'merge_flag=THRESH' " + << ". This can lead to bad results\n\n"; + } + + // + // set up data access using inputs + // + mode_exec->setup_multivar_fcst_obs_data_super(fsuper._simple_sd, osuper._simple_sd, + *mvdf._grid); + + // run the mode algorithm + _superobject_mode_algorithm(fsuper, osuper); + + delete mode_exec; mode_exec = 0; +} + +//////////////////////////////////////////////////////////////////////// -void process_command_line(const StringArray & argv) +void MultivarFrontEnd::_process_command_line(const StringArray & argv) { @@ -357,6 +436,7 @@ void process_command_line(const StringArray & argv) cline.add(set_outdir, "-outdir", 1); cline.add(set_logfile, "-log", 1); cline.add(set_verbosity, "-v", 1); + cline.add(set_compress, "-compress", 1); cline.parse(); @@ -364,11 +444,37 @@ void process_command_line(const StringArray & argv) // should be 3 arguments left // - fcst_fof = cline[0]; obs_fof = cline[1]; config_file = cline[2]; + return; + +} + +//////////////////////////////////////////////////////////////////////// + + +void MultivarFrontEnd::_read_config(const string & filename) + +{ + + ConcatString path; + + path = replace_path(mode_default_config); + + config.read_config(path.c_str(), filename.c_str()); + + // process the config except for the fields + config.process_config_except_fields(); + + // done once here, used for all data + // what is this, command line overrides config? look deeper.. remove from exec + // except traditional mode + if (compress_level >= 0) config.nc_info.set_compress_level(compress_level); + // from within mode_exec: + // engine.conf_info.nc_info.compress_level = engine.conf_info.get_compression_level(); + return; @@ -376,9 +482,7 @@ void process_command_line(const StringArray & argv) //////////////////////////////////////////////////////////////////////// -void multivar_consistency_checks(StringArray &fcst_filenames, StringArray &obs_filenames, - BoolCalc &f_calc, BoolCalc &o_calc, int &n_fcst_files, - int &n_obs_files) +void MultivarFrontEnd::_setup_inputs() { // // make sure the multivar logic programs are in the config file @@ -464,450 +568,191 @@ void multivar_consistency_checks(StringArray &fcst_filenames, StringArray &obs_f //////////////////////////////////////////////////////////////////////// -ConcatString set_multivar_dir() +void MultivarFrontEnd::_set_output_path() { - ConcatString dir; int status; - dir.clear(); + output_path.clear(); // no longer want numbered subdirectories - if ( outdir.length() > 0 ) dir << outdir; + if ( outdir.length() > 0 ) output_path << outdir; // // test to see of the output directory for this // mode runs exists, and if not, create it // - if ( ! directory_exists(dir.c_str()) ) { + if ( ! directory_exists(output_path.c_str()) ) { mlog << Debug(2) << program_name << ": creating output directory \"" - << dir << "\"\n\n"; + << output_path << "\"\n\n"; - status = _mkdir(dir.c_str()); + status = _mkdir(output_path.c_str()); if ( status < 0 ) { mlog << Error << "\nset_multivar_dir() ->" << " unable to create output directory \"" - << dir << "\"\n\n"; + << output_path << "\"\n\n"; exit ( 1 ); } } - return dir; } //////////////////////////////////////////////////////////////////////// -void create_verification_grid(const string &fcst_filename, const string &obs_filename, - const ConcatString &dir) +int MultivarFrontEnd::_mkdir(const char *dir) { - ConcatString command; - StringArray a, mode_argv; - - // - // build the command for running mode frontend - // - - mode_argv.clear(); - mode_argv.add(mode_path); - mode_argv.add(fcst_filename); - mode_argv.add(obs_filename); - mode_argv.add(config_file); - - command << cs_erase - << mode_path << ' ' - << fcst_filename << ' ' - << obs_filename << ' ' - << config_file; - - mode_argv.add("-v"); - char junk [256]; - snprintf(junk, sizeof(junk), "%d", mlog.verbosity_level()); - mode_argv.add(junk); - - mode_argv.add("-outdir"); - mode_argv.add(dir); - - command << " -v " << mlog.verbosity_level(); - command << " -outdir " << dir; - - // - // run the pass1 portions of mode, which creates simple objects - // - - mlog << Debug(3) << "Running mode command: \"" << command << "\"\n\n"; - ModeFrontEnd *frontend = new ModeFrontEnd; - verification_grid = frontend->create_verification_grid(mode_argv); - delete frontend; + char tmp[256]; + char *p = NULL; + size_t len; + snprintf(tmp, sizeof(tmp),"%s",dir); + len = strlen(tmp); + if (tmp[len - 1] == '/') + tmp[len - 1] = 0; + for (p = tmp + 1; *p; p++) + if (*p == '/') { + *p = 0; + string s = tmp; + if (s != ".") { + if (mkdir(tmp, dir_creation_mode) < 0) { + mlog << Error << "\n_mkdir() -> Error making " << tmp << "\n"; + return -1; + } + } + *p = '/'; + } + return (mkdir(tmp, dir_creation_mode)); } //////////////////////////////////////////////////////////////////////// -MultiVarData *create_simple_objects(ModeDataType dtype, int j, int n_files, - const string &filename, - const ConcatString &dir) +void MultivarFrontEnd::_simple_objects(ModeExecutive::Processing_t p, + ModeDataType dtype, + int j, int n_files, const string &filename, + const ModeInputData &input) { - ConcatString command; - StringArray a, mode_argv; - - // - // build the command for running mode frontend - // - - mode_argv.clear(); - mode_argv.add(mode_path); - mode_argv.add(filename); - mode_argv.add(config_file); - - command << cs_erase - << mode_path << ' ' - << filename << ' ' - << config_file; - - mode_argv.add("-v"); - char junk [256]; - snprintf(junk, sizeof(junk), "%d", mlog.verbosity_level()); - mode_argv.add(junk); - - mode_argv.add("-outdir"); - mode_argv.add(dir); - - command << " -v " << mlog.verbosity_level(); - command << " -outdir " << dir; - - // - // create the simple objects, forecast or obs, from this input data file - // - - mlog << Debug(3) << "Running mode command: \"" << command << "\"\n\n"; - ModeFrontEnd *frontend = new ModeFrontEnd; - int status = frontend->create_multivar_simple_objects(mode_argv, dtype, verification_grid, j, n_files); - MultiVarData *mvdi = frontend->get_multivar_data(dtype); - delete frontend; - - // - // create simple merge objects - // - - frontend = new ModeFrontEnd; - status = frontend->create_multivar_merge_objects(mode_argv, dtype, verification_grid, j, n_files); - - // add the merge results to the mvdi object - frontend->add_multivar_merge_data(mvdi, dtype); - delete frontend; - - return mvdi; -} + if (dtype == ModeDataType_MvMode_Fcst) { + _init_exec(p, filename, "None"); + mode_exec->init_multivar_simple(j, n_files, dtype, config); + mode_exec->setup_multivar_fcst_data(verification_grid, input); + } else { + _init_exec(p, "None", filename); + mode_exec->init_multivar_simple(j, n_files, dtype, config); + mode_exec->setup_multivar_obs_data(verification_grid, input); + } + + _simple_mode_algorithm(p); +} //////////////////////////////////////////////////////////////////////// -void create_superobjects(int n_fcst_files, const vector &mvdFcst, - int n_obs_files, const vector &mvdObs, - BoolCalc &f_calc, BoolCalc &o_calc, - BoolPlane &f_simple_result, BoolPlane &o_simple_result, - ShapeData &f_simple_sd, ShapeData &o_simple_sd, - ShapeData &f_merge_sd_split, ShapeData &o_merge_sd_split) +void MultivarFrontEnd::_init_exec(ModeExecutive::Processing_t p, + const string &ffile, + const string &ofile) { - // - // set the BoolPlane values using the mvd content - // + mlog << Debug(1) << "Running multivar front end for " << ModeExecutive::stype(p) << "\n"; - BoolPlane * f_simple_plane = new BoolPlane [n_fcst_files]; - BoolPlane * o_simple_plane = new BoolPlane [n_obs_files]; - BoolPlane * f_merge_plane = new BoolPlane [n_fcst_files]; - BoolPlane * o_merge_plane = new BoolPlane [n_obs_files]; + if ( mode_exec ) { delete mode_exec; mode_exec = 0; } - for (int j=0; jobjects_from_arrays(do_clusters, true, f_simple_plane[j]); - mvdFcst[j]->objects_from_arrays(do_clusters, false, f_merge_plane[j]); - } - - for (int j=0; jobjects_from_arrays(do_clusters, true, o_simple_plane[j]); - mvdObs[j]->objects_from_arrays(do_clusters, false, o_merge_plane[j]); - } + mode_exec = new ModeExecutive(); + // compress_level = -1; + mode_exec->fcst_file = ffile; + mode_exec->obs_file = ofile; - // - // combine the objects into super-objects - // - const int nx = f_simple_plane[0].nx(); - const int ny = f_simple_plane[0].ny(); - - BoolPlane f_merge_result, o_merge_result; - f_simple_result.set_size(nx, ny); - o_simple_result.set_size(nx, ny); - f_merge_result.set_size(nx, ny); - o_merge_result.set_size(nx, ny); - - combine_boolplanes("Fcst_Simple", f_simple_plane, n_fcst_files, f_calc, f_simple_result); - combine_boolplanes("Obs_Simple", o_simple_plane, n_obs_files, o_calc, o_simple_result); - combine_boolplanes("Fcst_Merge", f_merge_plane, n_fcst_files, f_calc, f_merge_result); - combine_boolplanes("Obs_Merge", o_merge_plane, n_obs_files, o_calc, o_merge_result); - - - // create ShapeData objects using something from mvd as a template - // (shape data has 1's or bad) - - f_simple_sd = ShapeData(*(mvdFcst[0]->_simple->_sd)); - for (int x=0; x_simple->_sd)); - for (int x=0; x_simple->_sd)); - for (int x=0; x_simple->_sd)); - for (int x=0; xmatch_config_file = config_file; // this is never used + mode_exec->out_dir = output_path; } - //////////////////////////////////////////////////////////////////////// void -create_intensity_comparisons(int findex, int oindex, const BoolPlane &f_result, - BoolPlane &o_result, const ConcatString &dir, - MultiVarData &mvdf, MultiVarData &mvdo, - bool has_union_f, bool has_union_o, - const string &fcst_filename, const string &obs_filename, - ShapeData &merge_f, ShapeData &merge_o) +MultivarFrontEnd::_superobject_mode_algorithm(const ModeSuperObject &fsuper, + const ModeSuperObject &osuper) { - - // mask the input data to be valid only inside the simple super objects - int nx = mvdf._nx; - int ny = mvdf._ny; - - mask_data("Fcst", nx, ny, f_result, mvdf._simple->_sd->data); - mask_data("Obs", nx, ny, o_result, mvdo._simple->_sd->data); - - // - // build the command for running mode frontend - // - StringArray mode_argv; - char junk [256]; - - mode_argv.clear(); - mode_argv.add(mode_path); - mode_argv.add(fcst_filename); - mode_argv.add(obs_filename); - mode_argv.add(config_file); - mode_argv.add("-v"); - snprintf(junk, sizeof(junk), "%d", mlog.verbosity_level()); - mode_argv.add(junk); - mode_argv.add("-outdir"); - mode_argv.add(dir); - - mlog << Debug(1) << "Running mvmode intensity comparisions \n\n"; - - ModeFrontEnd *frontend = new ModeFrontEnd; - int status = frontend->multivar_intensity_comparisons(mode_argv, mvdf, mvdo, has_union_f, - has_union_o, merge_f, merge_o, findex, - oindex); - delete frontend; + _mode_algorithm_init(); + mode_exec->clear_internal_r_index(); + mode_exec->do_conv_thresh_multivar_super(); + mode_exec->do_match_merge_multivar(fsuper._merge_sd_split, osuper._merge_sd_split, + ModeExecutive::MULTIVAR_SUPER); + mode_exec->process_output_multivar_super(); + mode_exec->clear_internal_r_index(); +#ifdef WITH_PYTHON + GP.finalize(); + #endif } //////////////////////////////////////////////////////////////////////// -void process_superobjects(ShapeData &f_result, ShapeData &o_result, - ShapeData &f_merge, ShapeData &o_merge, - int nx, int ny, const ConcatString &dir, - GrdFileType ftype, GrdFileType otype, const Grid &grid, - bool has_union) +void +MultivarFrontEnd::_intensity_compare_mode_algorithm(const MultiVarData &mvdf, + const MultiVarData &mvdo, + const ModeSuperObject &fsuper, + const ModeSuperObject &osuper) { - StringArray mode_argv; - char junk [256]; + _mode_algorithm_init(); + mode_exec->do_conv_thresh_multivar_intensity_compare(); + mode_exec->do_match_merge_multivar(fsuper._merge_sd_split, osuper._merge_sd_split, + ModeExecutive::MULTIVAR_INTENSITY); + // here replace raw data and min/max for plotting + mode_exec->process_output_multivar_intensity_compare(&mvdf, &mvdo); + mode_exec->clear_internal_r_index(); +#ifdef WITH_PYTHON + GP.finalize(); + #endif +} - // - // build the command for running mode frontend - // - mode_argv.clear(); - mode_argv.add(mode_path); - mode_argv.add(config_file); - mode_argv.add("-v"); - snprintf(junk, sizeof(junk), "%d", mlog.verbosity_level()); - mode_argv.add(junk); - mode_argv.add("-outdir"); - mode_argv.add(dir); - - mlog << Debug(1) << "Running superobject mode \n\n"; - - // set the data to 0 inside superobjects and missing everywhere else - mask_data_super("FcstSimple", nx, ny, f_result.data); - mask_data_super("ObsSimple", nx, ny, o_result.data); - - - ModeFrontEnd *frontend = new ModeFrontEnd; - int status = frontend->run_super(mode_argv, f_result, o_result, - f_merge, o_merge, ftype, otype, grid, has_union); - delete frontend; -} - //////////////////////////////////////////////////////////////////////// -void mask_data(const string &name, int nx, int ny, const BoolPlane &bp, DataPlane &data) +void MultivarFrontEnd::_simple_mode_algorithm(ModeExecutive::Processing_t p) { - - if (nx != data.nx() || ny != data.ny()) { - mlog << Error << "\nmask_data() -> " << name - << " :dimensions don't match " << nx << " " << ny - << " " << data.nx() << " " << data.ny() << "\n\n"; - - exit( 1 ); - } - - int nmasked=0, nkeep=0; + _mode_algorithm_init(); + mode_exec->clear_internal_r_index(); + mode_exec->do_conv_thresh_multivar_simple(p); + mode_exec->clear_internal_r_index(); - for (int x=0; xengine.conf_info; + if ( conf.quilt ) { + mlog << Error << "\nMultiVarFontend::mode_algorithm() -> " + << "quilting not yet implemented for multivar mode \n\n"; + exit ( 1 ); } - - mlog << Debug(1) << name << " superobject masking.." - << nkeep << " points of " - << nmasked + nkeep << " in superobjects\n"; -} + int NCT = conf.n_conv_threshs(); + int NCR = conf.n_conv_radii(); + if ( NCT != NCR ) { -//////////////////////////////////////////////////////////////////////// -void mask_data_super(const string &name, int nx, int ny, DataPlane &data) -{ + mlog << Error << "\nMultivarFrontEnd::_mode_algorithm_init() ->" + << "all convolution radius and threshold arrays must have the same number of elements\n\n"; - if (nx != data.nx() || ny != data.ny()) { - mlog << Error << "\nmask_data_super() -> " << name - << " :dimensions don't match " << nx << " " << ny - << " " << data.nx() << " " << data.ny() << "\n\n"; + exit ( 1 ); - exit( 1 ); } - int nmasked=0, nkeep=0; - - for (int x=0; x 1) { - for (int y=0; y" + << ": multiple convolution radii and thresholds not implemented in multivar mode\n\n"; - if(is_bad_data(data.get(x,y))) { - nmasked ++; - } else { - data.set(0.0, x, y); - nkeep ++; - } - } + exit ( 1 ); } - - mlog << Debug(1) << name << " superobject masking.." - << nkeep << " points of " - << nmasked + nkeep << " in superobjects\n"; } -//////////////////////////////////////////////////////////////////////// - -int _mkdir(const char *dir) -{ - char tmp[256]; - char *p = NULL; - size_t len; - - snprintf(tmp, sizeof(tmp),"%s",dir); - len = strlen(tmp); - if (tmp[len - 1] == '/') - tmp[len - 1] = 0; - for (p = tmp + 1; *p; p++) - if (*p == '/') { - *p = 0; - string s = tmp; - if (s != ".") { - if (mkdir(tmp, dir_creation_mode) < 0) { - mlog << Error << "\n_mkdir() -> Error making " << tmp << "\n"; - return -1; - } - } - *p = '/'; - } - - return (mkdir(tmp, dir_creation_mode)); -} -//////////////////////////////////////////////////////////////////////// -void _debug_shape_examine(string &name, const ShapeData &sd, int nx, int ny) -{ - vector values; - vector count; - for (int x=0; x::iterator vi; - vi = find(values.begin(), values.end(), v); - if (vi == values.end()) { - values.push_back(v); - count.push_back(1); - } else { - int ii = vi - values.begin(); - count[ii] = count[ii] + 1; - } - } - } - for (size_t i=0; i +#include "mode_conf_info.h" +#include "two_d_array.h" +#include "bool_calc.h" +#include "multivar_data.h" +#include "mode_superobject.h" +#include "mode_input_data.h" +#include "mode_exec.h" + +class MultivarFrontEnd { + +private: + + int n_fcst_files, n_obs_files; + StringArray fcst_filenames; + StringArray obs_filenames; + BoolCalc f_calc, o_calc ; + vector fcstInput, obsInput; + vector mvdFcst, mvdObs; + string fcst_fof; + string obs_fof; + + void _process_command_line(const StringArray &); + void _read_config(const string & filename); + void _setup_inputs(); + void _set_output_path(); + int _mkdir(const char *dir); + void _simple_objects(ModeExecutive::Processing_t p, ModeDataType dtype, + int j, int n_files, const string &filename, + const ModeInputData &input); + void _init_exec(ModeExecutive::Processing_t p, const string &ffile, const string &ofile); + void _superobject_mode_algorithm(const ModeSuperObject &fsuper, const ModeSuperObject &osuper); + void _intensity_compare_mode_algorithm(const MultiVarData &mvdf, const MultiVarData &mvdo, + const ModeSuperObject &fsuper, const ModeSuperObject &osuper); + void _simple_mode_algorithm(ModeExecutive::Processing_t p); + void _mode_algorithm_init() const; + +public: + + bool do_clusters; + string default_out_dir; + ModeConfInfo config; + ConcatString output_path; + string mode_path; + string config_file; + Grid verification_grid; + + MultivarFrontEnd(); + + ~MultivarFrontEnd(); + + + int run(const StringArray & Argv); + void init(const StringArray & Argv); + + static void set_outdir (const StringArray &); + static void set_logfile (const StringArray &); + static void set_verbosity (const StringArray &); + static void set_compress (const StringArray &); + + void read_input(const string &name, int index, ModeDataType type, + GrdFileType f_t, GrdFileType other_t, int shift); + + + void create_verif_grid(void); + + MultiVarData *create_simple_objects(ModeDataType dtype, int j, int n_files, + const string &filename, + const ModeInputData &input); + + void create_intensity_comparisons(int findex, int oindex, + const ModeSuperObject &fsuper, + const ModeSuperObject &osuper, + MultiVarData &mvdf, MultiVarData &mvdo, + const string &fcst_filename, + const string &obs_filename); + + void process_superobjects(ModeSuperObject &fsuper, + ModeSuperObject &osuper, + const MultiVarData &mvdf, + const MultiVarData &mvdo); + +}; + + +#endif /* __MULTIVAR_FRONT_END_H__ */ + + +///////////////////////////////////////////////////////////////////////// diff --git a/src/tools/core/pcp_combine/pcp_combine.cc b/src/tools/core/pcp_combine/pcp_combine.cc index 8993c2891a..183dc3825f 100644 --- a/src/tools/core/pcp_combine/pcp_combine.cc +++ b/src/tools/core/pcp_combine/pcp_combine.cc @@ -77,6 +77,7 @@ // the -pcpdir option. // 024 07/06/22 Howard Soh METplus-Internal #19 Rename main to met_main // 025 09/29/22 Prestopnik MET #2227 Remove namespace netCDF from header files +// 026 01/29/24 Halley Gotway MET #2801 Configure time difference warnings // //////////////////////////////////////////////////////////////////////// @@ -755,9 +756,9 @@ int search_pcp_dir(const char *cur_dir, const unixtime cur_ut, cur_file << cs_erase << cur_dir << '/' << dirp->d_name; Met2dDataFileFactory factory; - Met2dDataFile * mtddf = (Met2dDataFile *) nullptr; + Met2dDataFile * mtddf; VarInfoFactory var_fac; - VarInfo * cur_var = (VarInfo *) nullptr; + VarInfo * cur_var; // // Create a data file object. @@ -869,15 +870,25 @@ void do_sub_command() { nc_valid_time = plus.valid(); // - // Output initialization time - // Warning if init_time1 != init_time2. + // Check that the initialization times match // if(plus.init() != minus.init()) { - mlog << Warning << "\ndo_sub_command() -> " - << "the initialization times do not match (" - << unix_to_yyyymmdd_hhmmss(plus.init()) << " != " - << unix_to_yyyymmdd_hhmmss(minus.init()) - << ") for subtraction. Using the first value.\n\n"; + + ConcatString cs; + cs << cs_erase + << "The initialization times do not match (" + << unix_to_yyyymmdd_hhmmss(plus.init()) << " != " + << unix_to_yyyymmdd_hhmmss(minus.init()) + << ") for subtraction. Using the first value."; + + if(config.time_offset_warning( + (int) (plus.init() - minus.init()))) { + mlog << Warning << "\ndo_sub_command() -> " + << cs << "\n\n"; + } + else { + mlog << Debug(3) << cs << "\n"; + } } nc_init_time = plus.init(); @@ -1145,13 +1156,13 @@ void do_derive_command() { for(j=0; j " - << "Forecast and observation valid times do not match " - << unix_to_yyyymmdd_hhmmss(fcst_dp.valid()) << " != " - << unix_to_yyyymmdd_hhmmss(obs_dp.valid()) << " for " - << fcst_info->magic_str() << " versus " - << obs_info->magic_str() << ".\n\n"; + + ConcatString cs; + cs << cs_erase + << "Forecast and observation valid times do not match (" + << unix_to_yyyymmdd_hhmmss(fcst_dp.valid()) << " != " + << unix_to_yyyymmdd_hhmmss(obs_dp.valid()) << ") for " + << fcst_info->magic_str() << " versus " + << obs_info->magic_str() << "."; + + if(conf_info.conf.time_offset_warning( + (int) (fcst_dp.valid() - obs_dp.valid()))) { + mlog << Warning << "\nget_series_data() -> " + << cs << "\n\n"; + } + else { + mlog << Debug(3) << cs << "\n"; + } } return; diff --git a/src/tools/core/wavelet_stat/wavelet_stat.cc b/src/tools/core/wavelet_stat/wavelet_stat.cc index beba507332..744b80f6c9 100644 --- a/src/tools/core/wavelet_stat/wavelet_stat.cc +++ b/src/tools/core/wavelet_stat/wavelet_stat.cc @@ -39,6 +39,7 @@ // 014 07/09/21 Linden MET #1746 Skip thresholding. // 015 07/06/22 Howard Soh METplus-Internal #19 Rename main to met_main // 016 10/03/22 Prestopnik MET #2227 Remove using namespace netCDF from header files +// 017 01/29/24 Halley Gotway MET #2801 Configure time difference warnings // //////////////////////////////////////////////////////////////////////// @@ -338,24 +339,34 @@ void process_scores() { // Check that the valid times match if(fcst_dp.valid() != obs_dp.valid()) { - mlog << Warning << "\nprocess_scores() -> " - << "Forecast and observation valid times do not match " - << unix_to_yyyymmdd_hhmmss(fcst_dp.valid()) << " != " << - unix_to_yyyymmdd_hhmmss(obs_dp.valid()) << " for " - << conf_info.fcst_info[i]->magic_str() << " versus " - << conf_info.obs_info[i]->magic_str() << ".\n"; + ConcatString cs; + cs << cs_erase + << "Forecast and observation valid times do not match (" + << unix_to_yyyymmdd_hhmmss(fcst_dp.valid()) << " != " + << unix_to_yyyymmdd_hhmmss(obs_dp.valid()) << ") for " + << conf_info.fcst_info[i]->magic_str() << " versus " + << conf_info.obs_info[i]->magic_str() << "."; + + if(conf_info.conf.time_offset_warning( + (int) (fcst_dp.valid() - obs_dp.valid()))) { + mlog << Warning << "\nprocess_scores() -> " + << cs << "\n\n"; + } + else { + mlog << Debug(3) << cs << "\n"; + } } // Check that the accumulation intervals match if(conf_info.fcst_info[i]->level().type() == LevelType_Accum && conf_info.obs_info[i]->level().type() == LevelType_Accum && - fcst_dp.accum() != obs_dp.accum()) { + fcst_dp.accum() != obs_dp.accum()) { mlog << Warning << "\nprocess_scores() -> " << "Forecast and observation accumulation times " - << "do not match " << sec_to_hhmmss(fcst_dp.accum()) + << "do not match (" << sec_to_hhmmss(fcst_dp.accum()) << " != " << sec_to_hhmmss(obs_dp.accum()) - << " for " << conf_info.fcst_info[i]->magic_str() << " versus " + << ") for " << conf_info.fcst_info[i]->magic_str() << " versus " << conf_info.obs_info[i]->magic_str() << ".\n"; } diff --git a/src/tools/other/ascii2nc/aeronet_handler.cc b/src/tools/other/ascii2nc/aeronet_handler.cc index e6ca0963d8..74a727ef45 100644 --- a/src/tools/other/ascii2nc/aeronet_handler.cc +++ b/src/tools/other/ascii2nc/aeronet_handler.cc @@ -15,9 +15,6 @@ //////////////////////////////////////////////////////////////////////// -using namespace std; - - #include #include #include @@ -29,6 +26,9 @@ using namespace std; #include "aeronet_handler.h" +using namespace std; + + static bool test_AOD_550 = false; static const char *AERONET_NA_STR = "N/A"; static const char *AERONET_V3_STR = "AERONET Version 3"; diff --git a/src/tools/other/ascii2nc/aeronet_handler.h b/src/tools/other/ascii2nc/aeronet_handler.h index e835a91602..a602ba41ce 100644 --- a/src/tools/other/ascii2nc/aeronet_handler.h +++ b/src/tools/other/ascii2nc/aeronet_handler.h @@ -31,24 +31,24 @@ class AeronetHandler : public FileHandler public: - AeronetHandler(const string &program_name); + AeronetHandler(const std::string &program_name); virtual ~AeronetHandler(); virtual bool isFileType(LineDataFile &ascii_file) const; void setFormatVersion(int version); - static string getFormatString() + static std::string getFormatString() { return "aeronet"; } - static string getFormatString_v2() + static std::string getFormatString_v2() { return "aeronetv2"; } - static string getFormatString_v3() + static std::string getFormatString_v3() { return "aeronetv3"; } @@ -69,7 +69,7 @@ class AeronetHandler : public FileHandler // The header type for these observations - static const string HEADER_TYPE; + static const std::string HEADER_TYPE; // Grib codes for the different fields @@ -83,7 +83,7 @@ class AeronetHandler : public FileHandler // Unchanging header information - string _stationId; + std::string _stationId; double _stationLat; double _stationLon; double _stationAlt; @@ -111,13 +111,13 @@ class AeronetHandler : public FileHandler virtual bool _readObservations(LineDataFile &ascii_file); // Extract the height from the field name - double extract_height(string hdr_field); + double extract_height(std::string hdr_field); // Get the number of headers int get_header_count_v3(StringArray hdr_tokens); // Make the variable name from header (field name) - string make_var_name_from_header(string hdr_field); + std::string make_var_name_from_header(std::string hdr_field); }; diff --git a/src/tools/other/ascii2nc/airnow_handler.cc b/src/tools/other/ascii2nc/airnow_handler.cc index 74525c6139..033d2419d3 100644 --- a/src/tools/other/ascii2nc/airnow_handler.cc +++ b/src/tools/other/ascii2nc/airnow_handler.cc @@ -10,9 +10,6 @@ //////////////////////////////////////////////////////////////////////// -using namespace std; - - #include #include #include @@ -25,6 +22,9 @@ using namespace std; #include "airnow_handler.h" +using namespace std; + + static const char *AIRNOW_NA_STR = "N/A"; static const char *airnow_stations_env = "MET_AIRNOW_STATIONS"; @@ -421,16 +421,12 @@ bool AirnowHandler::_parseObservationLineAqobs(const string &data_line, } // fill in expected things - double lat, lon, elev; + double elev = 0.0; string stationId = tokens[stationIdPtr]; - string col; - - lat = atof(tokens[latPtr].c_str()); - lon = atof(tokens[lonPtr].c_str()); + double lat = atof(tokens[latPtr].c_str()); + double lon = atof(tokens[lonPtr].c_str()); if (elevPtr >= 0) { elev = atof(tokens[elevPtr].c_str()); - } else { - elev = 0.0; } _addHourlyAqobsObs(tokens, header_type, stationId, valid_time, lat, lon, elev, @@ -496,20 +492,15 @@ void AirnowHandler::_addHourlyAqobsObs(const vector &data_line, const st int measuredPtr, int aqiPtr, int valuePtr, int unitPtr, const string &varname) { - string col; - int status; - int aqi; - double value; - string units; - // averging period is 1-hour int avgPeriodSec = 3600; - status = atoi(data_line[measuredPtr].c_str()); + int status = atoi(data_line[measuredPtr].c_str()); if (status == 1) { - aqi = atoi(data_line[aqiPtr].c_str()); + double value; + int aqi = atoi(data_line[aqiPtr].c_str()); if (doubleOrMissing(data_line[valuePtr], value)) { - units = data_line[unitPtr]; + string units = data_line[unitPtr]; // add the observation _addObservations(Observation(header_type, stationId, valid_time, @@ -528,15 +519,13 @@ void AirnowHandler::_addHourlyAqobsObs(const vector &data_line, const st double lat, double lon, double elev, int valuePtr, int unitPtr, const string &varname) { - string col; double value; - string units; // averging period is 1-hour int avgPeriodSec = 3600; if (doubleOrMissing(data_line[valuePtr], value)) { - units = data_line[unitPtr]; + string units = data_line[unitPtr]; // add the observation _addObservations(Observation(header_type, stationId, valid_time, diff --git a/src/tools/other/ascii2nc/airnow_handler.h b/src/tools/other/ascii2nc/airnow_handler.h index beef7bf0d5..94c7007807 100644 --- a/src/tools/other/ascii2nc/airnow_handler.h +++ b/src/tools/other/ascii2nc/airnow_handler.h @@ -32,24 +32,24 @@ class AirnowHandler : public FileHandler public: - AirnowHandler(const string &program_name); + AirnowHandler(const std::string &program_name); virtual ~AirnowHandler(); virtual bool isFileType(LineDataFile &ascii_file) const; void setFormatVersion(int version); - static string getFormatStringDailyV2() + static std::string getFormatStringDailyV2() { return "airnowdaily_v2"; } - static string getFormatStringHourlyAqObs() + static std::string getFormatStringHourlyAqObs() { return "airnowhourlyaqobs"; } - static string getFormatStringHourly() + static std::string getFormatStringHourly() { return "airnowhourly"; } @@ -82,7 +82,7 @@ class AirnowHandler : public FileHandler // Unchanging header information - string _stationId; + std::string _stationId; double _stationLat; double _stationLon; double _stationAlt; @@ -132,7 +132,7 @@ class AirnowHandler : public FileHandler int so2Ptr; int so2UnitPtr; - string monitoringSiteFileName; + std::string monitoringSiteFileName; AirnowLocations locations; @@ -154,43 +154,43 @@ class AirnowHandler : public FileHandler bool _determineFileType(LineDataFile &ascii_file); - void _addHourlyAqobsObs(const vector &data_line, const string &header_type, - const string &stationId, const time_t &valid_time, + void _addHourlyAqobsObs(const std::vector &data_line, const std::string &header_type, + const std::string &stationId, const time_t &valid_time, double lat, double lon, double elev, int measuredPtr, int aqiPtr, int valuePtr, - int unitPtr, const string &varname); - void _addHourlyAqobsObs(const vector &data_line, const string &header_type, - const string &stationId, const time_t &valid_time, + int unitPtr, const std::string &varname); + void _addHourlyAqobsObs(const std::vector &data_line, const std::string &header_type, + const std::string &stationId, const time_t &valid_time, double lat, double lon, double elev, - int valuePtr, int unitPtr, const string &varname); + int valuePtr, int unitPtr, const std::string &varname); // Get the observation valid time from the given observation line - time_t _getValidTime(const vector &data_line) const; + time_t _getValidTime(const std::vector &data_line) const; time_t _getValidTime(const DataLine &data_line) const; - time_t _getValidTime(const string &dateStr, const string &timeStr) const; + time_t _getValidTime(const std::string &dateStr, const std::string &timeStr) const; // Read the observations from the given file and add them to the // _observations vector. virtual bool _readObservations(LineDataFile &ascii_file); - bool _readObservationsHourlyAqobs(LineDataFile &ascii_file, int column_cnt, const string &delimiter, - const string &header_type); - bool _readObservationsStandard(LineDataFile &ascii_file, int column_cnt, const string &delimiter, - const string &header_type); + bool _readObservationsHourlyAqobs(LineDataFile &ascii_file, int column_cnt, const std::string &delimiter, + const std::string &header_type); + bool _readObservationsStandard(LineDataFile &ascii_file, int column_cnt, const std::string &delimiter, + const std::string &header_type); bool _parseObservationLineStandard(DataLine &data_line, - const string &filename, + const std::string &filename, int column_cnt, - const string &header_type); - bool _parseObservationLineAqobs(const string &data_line, int column_cnt, - const string &header_type, int lineNumber, - const string &filename); + const std::string &header_type); + bool _parseObservationLineAqobs(const std::string &data_line, int column_cnt, + const std::string &header_type, int lineNumber, + const std::string &filename); void _initializeColumnPointers(); - string _extractColumn(const DataLine &data_line, int ptr) const; - int _getVarIndex(const string &, const string &); + std::string _extractColumn(const DataLine &data_line, int ptr) const; + int _getVarIndex(const std::string &, const std::string &); }; diff --git a/src/tools/other/ascii2nc/airnow_locations.cc b/src/tools/other/ascii2nc/airnow_locations.cc index 5e66fa9018..5063cf59c0 100644 --- a/src/tools/other/ascii2nc/airnow_locations.cc +++ b/src/tools/other/ascii2nc/airnow_locations.cc @@ -8,8 +8,6 @@ //////////////////////////////////////////////////////////////////////// -using namespace std; - #include #include #include @@ -22,6 +20,9 @@ using namespace std; #include "airnow_locations.h" +using namespace std; + + //////////////////////////////////////////////////////////////////////// // diff --git a/src/tools/other/ascii2nc/airnow_locations.h b/src/tools/other/ascii2nc/airnow_locations.h index 3ad1908622..74dbb76e0c 100644 --- a/src/tools/other/ascii2nc/airnow_locations.h +++ b/src/tools/other/ascii2nc/airnow_locations.h @@ -28,25 +28,25 @@ class AirnowLocations AirnowLocations(); virtual ~AirnowLocations(); - bool initialize(const string &locationsFile); - bool lookupLatLonElev(const string aqsid, double &lat, double &lon, double &elev) const; + bool initialize(const std::string &locationsFile); + bool lookupLatLonElev(const std::string aqsid, double &lat, double &lon, double &elev) const; private: - bool _setPtr(DataLine &data_line, const string &headerName, int &ptr) const; + bool _setPtr(DataLine &data_line, const std::string &headerName, int &ptr) const; - string monitoringSiteFileName; + std::string monitoringSiteFileName; // all the AQSID's from the monitoring site file, set only for Hourly format. // and all the associated location information from the monitoring site file, in the same order // 3 different AQSID's are found in the lookup file: stationId, Aqsid, FullAqsid // - vector monitoringSiteStationId; - vector monitoringSiteAqsid; - vector monitoringSiteFullAqsid; - vector monitoringSiteLat; - vector monitoringSiteLon; - vector monitoringSiteElev; + std::vector monitoringSiteStationId; + std::vector monitoringSiteAqsid; + std::vector monitoringSiteFullAqsid; + std::vector monitoringSiteLat; + std::vector monitoringSiteLon; + std::vector monitoringSiteElev; }; diff --git a/src/tools/other/ascii2nc/file_handler.cc b/src/tools/other/ascii2nc/file_handler.cc index 74020804f0..5afa7b5dc8 100644 --- a/src/tools/other/ascii2nc/file_handler.cc +++ b/src/tools/other/ascii2nc/file_handler.cc @@ -10,13 +10,10 @@ //////////////////////////////////////////////////////////////////////// -using namespace std; - #include #include #include -using namespace netCDF; #include "vx_math.h" #include "vx_nc_util.h" @@ -31,6 +28,10 @@ using namespace netCDF; #include "summary_calc_range.h" #include "summary_calc_stdev.h" +using namespace std; +using namespace netCDF; + + const float FileHandler::FILL_VALUE = -9999.f; const int DEF_DEFALTE_LEVEL = 2; @@ -263,7 +264,6 @@ bool FileHandler::_openNetcdf(const string &nc_filename) bool FileHandler::_addObservations(const Observation &obs) { - double grid_x, grid_y; // // Apply the grid mask, the area mask, and the polyline mask diff --git a/src/tools/other/ascii2nc/file_handler.h b/src/tools/other/ascii2nc/file_handler.h index 8358411f36..8994afb4e5 100644 --- a/src/tools/other/ascii2nc/file_handler.h +++ b/src/tools/other/ascii2nc/file_handler.h @@ -47,7 +47,7 @@ class FileHandler public: - FileHandler(const string &program_name); + FileHandler(const std::string &program_name); virtual ~FileHandler(); virtual bool isFileType(LineDataFile &ascii_file) const = 0; @@ -56,10 +56,10 @@ class FileHandler void setAreaMask(MaskPlane &a); void setPolyMask(MaskPoly &p); void setSIDMask (StringArray &s); - void setMessageTypeMap(map m); + void setMessageTypeMap(std::map m); - virtual bool readAsciiFiles(const vector< ConcatString > &ascii_filename_list); - bool writeNetcdfFile(const string &nc_filename); + virtual bool readAsciiFiles(const std::vector< ConcatString > &ascii_filename_list); + bool writeNetcdfFile(const std::string &nc_filename); bool summarizeObs(const TimeSummaryInfo &summary_info); @@ -81,7 +81,7 @@ class FileHandler // Protected members // /////////////////////// - string _programName; + std::string _programName; // Variables for writing output NetCDF file @@ -95,11 +95,11 @@ class FileHandler MaskFilters filters; - map _messageTypeMap; + std::map _messageTypeMap; // List of observations read from the ascii files - vector< Observation > _observations; + std::vector< Observation > _observations; bool use_var_id; StringArray obs_names; StringArray obs_units; @@ -124,7 +124,7 @@ class FileHandler void _countHeaders(); - time_t _getValidTime(const string &time_string) const; + time_t _getValidTime(const std::string &time_string) const; // Read the observations from the given file. @@ -141,8 +141,8 @@ class FileHandler bool _writeObservations(); void _closeNetcdf(); - bool _openNetcdf(const string &nc_filename); - void debug_print_observations(vector< Observation >, string); + bool _openNetcdf(const std::string &nc_filename); + void debug_print_observations(std::vector< Observation >, std::string); }; inline void FileHandler::setCompressionLevel(int compressoion_level) { deflate_level = compressoion_level; } @@ -150,7 +150,7 @@ inline void FileHandler::setGridMask(Grid &g) { filters.set_grid_mask(&g) inline void FileHandler::setAreaMask(MaskPlane &a) { filters.set_area_mask(&a); } inline void FileHandler::setPolyMask(MaskPoly &p) { filters.set_poly_mask(&p); } inline void FileHandler::setSIDMask (StringArray &s) { filters.set_sid_mask(&s); } -inline void FileHandler::setMessageTypeMap(map m) { +inline void FileHandler::setMessageTypeMap(std::map m) { _messageTypeMap = m; } diff --git a/src/tools/other/ascii2nc/python_handler.cc b/src/tools/other/ascii2nc/python_handler.cc index a5a55e0f94..ae91a80200 100644 --- a/src/tools/other/ascii2nc/python_handler.cc +++ b/src/tools/other/ascii2nc/python_handler.cc @@ -68,14 +68,13 @@ PythonHandler::PythonHandler(const char * program_name, const char * ascii_filen { -int j; ConcatString s = ascii_filename; StringArray a = s.split(" "); user_script_filename = a[0]; -for (j=1; j<(a.n()); ++j) { // j starts at one here, not zero +for (int j=1; j<(a.n()); ++j) { // j starts at one here, not zero user_script_args.add(a[j]); @@ -117,7 +116,7 @@ bool PythonHandler::isFileType(LineDataFile &ascii_file) const { -return ( false ); +return false; } @@ -153,8 +152,7 @@ if ( ! PyList_Check(obj) ) { } -int j; -PyObject * a = 0; +PyObject * a = nullptr; Python3_List list(obj); Observation obs; @@ -164,7 +162,7 @@ Observation obs; use_var_id = false; -for (j=0; j<(list.size()); ++j) { +for (int j=0; j<(list.size()); ++j) { a = list[j]; diff --git a/src/tools/other/madis2nc/madis2nc.cc b/src/tools/other/madis2nc/madis2nc.cc index 0035ee5012..f23272a533 100644 --- a/src/tools/other/madis2nc/madis2nc.cc +++ b/src/tools/other/madis2nc/madis2nc.cc @@ -697,12 +697,12 @@ void convert_wind_wdir_to_u_v(float wind, float wdir, //////////////////////////////////////////////////////////////////////// bool check_masks(double lat, double lon, const char *sid) { - double grid_x, grid_y; // // Check grid masking. // if(mask_grid.nx() > 0 || mask_grid.ny() > 0) { + double grid_x, grid_y; mask_grid.latlon_to_xy(lat, -1.0*lon, grid_x, grid_y); if(grid_x < 0 || grid_x >= mask_grid.nx() || grid_y < 0 || grid_y >= mask_grid.ny()) { @@ -3330,7 +3330,6 @@ void process_madis_acarsProfiles(NcFile *&f_in) { // // Loop through each record and get the header data. // - int data_cnt; for(i_hdr_s=rec_beg; i_hdr_s BUFFER_SIZE) ? BUFFER_SIZE: (my_rec_end - i_hdr_s); @@ -3355,7 +3354,7 @@ void process_madis_acarsProfiles(NcFile *&f_in) { cur[0] = i_hdr_s; dim[0] = buf_size; dim[1] = maxLevels; - data_cnt = buf_size * maxLevels; + int data_cnt = buf_size * maxLevels; get_nc_data(&in_hdr_vld_var, tmp_dbl_arr, dim, cur); get_nc_data(&in_hdr_lat_var, (float *)hdr_lat_arr, dim, cur); diff --git a/src/tools/other/pb2nc/pb2nc.cc b/src/tools/other/pb2nc/pb2nc.cc index c4eecb213f..f46fce3bb0 100644 --- a/src/tools/other/pb2nc/pb2nc.cc +++ b/src/tools/other/pb2nc/pb2nc.cc @@ -117,33 +117,34 @@ static const char * default_config_filename = "MET_BASE/config/PB2NCConfig_defau static const char *program_name = "pb2nc"; static const char *not_assigned = "not_assigned"; +static const char *tmp_pb2nc_base = "tmp_pb2nc_blk"; -constexpr static float fill_value = -9999.f; -constexpr static int missing_cycle_minute = -1; +constexpr float fill_value = -9999.f; +constexpr int missing_cycle_minute = -1; // Constants used to interface to Fortran subroutines // Missing value for BUFR data -static const double r8bfms = 1.0E10; +constexpr double r8bfms = 1.0E10; // Maximum number of BUFR parameters -static const int mxr8pm = 10; +constexpr int mxr8pm = 10; // Maximum number of BUFR levels -static const int mxr8lv_small = 255; -static const int mxr8lv = 1023; // was 255; +constexpr int mxr8lv_small = 255; +constexpr int mxr8lv = 1023; // was 255; // Maximum number of BUFR event sequences -static const int mxr8vn = 10; +constexpr int mxr8vn = 10; // Maximum number of BUFR variable types -static const int mxr8vt = 6; +constexpr int mxr8vt = 6; // Maximum length of BUFR variable name -static const int mxr8nm = 8; +constexpr int mxr8nm = 8; // Maximum number of BUFR variable types -static const int COUNT_THRESHOLD = 16; +constexpr int COUNT_THRESHOLD = 16; // File unit number for opening the PrepBufr file -static const int file_unit = 11; +constexpr int file_unit = 11; // 2nd file unit number for opening the PrepBufr file -static const int dump_unit = 22; +constexpr int dump_unit = 22; // Grib codes corresponding to the variable types const std::array var_gc = { @@ -187,9 +188,9 @@ static float pbl_data_ugrd[MAX_PBL_LEVEL]; static float pbl_data_vgrd[MAX_PBL_LEVEL]; // PREPBUFR VIRTMP program code -static const double virtmp_prog_code = 8.0; -static const int MIN_FORTRAN_FILE_ID = 1; -static const int MAX_FORTRAN_FILE_ID = 99; +constexpr double virtmp_prog_code = 8.0; +constexpr int MIN_FORTRAN_FILE_ID = 1; +constexpr int MAX_FORTRAN_FILE_ID = 99; //////////////////////////////////////////////////////////////////////// @@ -238,15 +239,15 @@ static double hdr[mxr8pm]; static double evns[mxr8vt][mxr8vn][mxr8lv][mxr8pm]; static int nlev; -static const int BUFR_NUMBER_START = 13; -static const int BUFR_NAME_START = 2; -static const int BUFR_NAME_LEN = 8; -static const int BUFR_DESCRIPTION_START = 22; -static const int BUFR_DESCRIPTION_LEN = 56; -static const int BUFR_UNIT_START = 40; -static const int BUFR_UNIT_LEN = 25; -static const int BUFR_SEQUENCE_START = 13; -static const int BUFR_SEQUENCE_LEN = 66; +constexpr int BUFR_NUMBER_START = 13; +constexpr int BUFR_NAME_START = 2; +constexpr int BUFR_NAME_LEN = 8; +constexpr int BUFR_DESCRIPTION_START = 22; +constexpr int BUFR_DESCRIPTION_LEN = 56; +constexpr int BUFR_UNIT_START = 40; +constexpr int BUFR_UNIT_LEN = 25; +constexpr int BUFR_SEQUENCE_START = 13; +constexpr int BUFR_SEQUENCE_LEN = 66; static const char *prepbufr_p_event = "POB PQM PPC PRC PFC PAN CAT"; static const char *prepbufr_q_event = "QOB QQM QPC QRC QFC QAN CAT"; @@ -271,7 +272,7 @@ static const char *bufr_avail_latlon_names = "XOB CLON CLONH YOB CLAT CLATH"; static const char *derived_mlcape = "D_MLCAPE"; static const char *derived_cape = "D_CAPE"; static const char *derived_pbl = "D_PBL"; -static const float MLCAPE_INTERVAL = 3000.; +constexpr float MLCAPE_INTERVAL = 3000.; static double bufr_obs[mxr8lv][mxr8pm]; static double bufr_obs_extra[mxr8lv][mxr8pm]; @@ -308,7 +309,7 @@ static vector< Observation > observations; // // Output NetCDF file, dimensions, and variables // -static NcFile *f_out = (NcFile *) 0; +static NcFile *f_out = (NcFile *) nullptr; //////////////////////////////////////////////////////////////////////// @@ -460,8 +461,6 @@ derive_var_cfg &derive_var_cfg::operator=(const derive_var_cfg &a) noexcept { int met_main(int argc, char *argv[]) { - int i; - // Initialize static variables initialize(); @@ -470,7 +469,7 @@ int met_main(int argc, char *argv[]) { if (collect_metadata) { // Process each PrepBufr file - for(i=0; i MAX_FORTRAN_FILE_ID || _file_id < MIN_FORTRAN_FILE_ID) { @@ -673,9 +672,7 @@ ConcatString save_bufr_table_to_file(const char *blk_file, int _file_id) { openpb_(blk_file, &_file_id); dump_tbl_(blk_file, &_file_id, tbl_filename.c_str(), &len); closepb_(&_file_id); - - // Delete the temporary blocked file - remove_temp_file((string)blk_file); + return tbl_filename; } @@ -689,7 +686,7 @@ bool is_prepbufr_file(const StringArray *events) { //////////////////////////////////////////////////////////////////////// -void get_variable_info(const char* tbl_filename) { +void get_variable_info(ConcatString blk_file, int unit) { static const char *method_name = " get_variable_info()"; FILE * fp; @@ -704,8 +701,9 @@ void get_variable_info(const char* tbl_filename) { tableB_vars.clear(); tableB_descs.clear(); - fp = fopen(tbl_filename, "r"); - ConcatString input_data; + ConcatString tbl_filename = save_bufr_table_to_file(blk_file.c_str(), unit); + + fp = fopen(tbl_filename.c_str(), "r"); if (fp != nullptr) { char var_name[BUFR_NAME_LEN+1]; char var_desc[max(BUFR_DESCRIPTION_LEN,BUFR_SEQUENCE_LEN)+1]; @@ -822,6 +820,8 @@ void get_variable_info(const char* tbl_filename) { if (line) free(line); } + + remove_temp_file(tbl_filename); return; } @@ -839,7 +839,7 @@ void open_netcdf() { << "trouble opening output file: " << ncfile << "\n\n"; delete f_out; - f_out = (NcFile *) 0; + f_out = (NcFile *) nullptr; exit(1); } @@ -858,21 +858,22 @@ void open_netcdf() { void process_pbfile(int i_pb) { int npbmsg, npbmsg_total, unit, yr, mon, day, hr, min, sec; - int i, i_msg, i_read, n_file_obs, i_ret, i_date, n_hdr_obs; + int i, i_msg, n_file_obs, i_ret, i_date, n_hdr_obs; int rej_typ, rej_sid, rej_vld, rej_grid, rej_poly; int rej_elv, rej_pb_rpt, rej_in_rpt, rej_itp, rej_nobs; - int lv, ev, ev_temp, kk, len1, len2; + int lv, ev, ev_temp, kk; int n_derived_obs; - double x, y; + double x; + double y; int cycle_minute; - unixtime file_ut = (unixtime) 0; + unixtime file_ut; unixtime adjusted_file_ut; unixtime msg_ut, beg_ut, end_ut; unixtime min_msg_ut, max_msg_ut; - beg_ut = end_ut = (unixtime) 0; + file_ut = beg_ut = end_ut = (unixtime) 0; ConcatString file_name, blk_prefix, blk_file, log_message; ConcatString prefix; @@ -894,7 +895,7 @@ void process_pbfile(int i_pb) { float pqtzuv[mxr8vt], pqtzuv_qty[mxr8vt]; const int debug_level_for_performance = 3; - int start_t, end_t, method_start, method_end; + clock_t start_t, end_t, method_start, method_end; start_t = end_t = method_start = method_end = clock(); IntArray diff_file_times; @@ -922,7 +923,7 @@ void process_pbfile(int i_pb) { file_name << pbfile[i_pb]; // Build the temporary block file name - blk_prefix << conf_info.tmp_dir << "/" << "tmp_pb2nc_blk"; + blk_prefix << conf_info.tmp_dir << "/" << tmp_pb2nc_base; blk_file = make_temp_file_name(blk_prefix.c_str(), nullptr); mlog << Debug(1) << "Blocking Bufr file to:\t" << blk_file << "\n"; @@ -1007,11 +1008,12 @@ void process_pbfile(int i_pb) { int nlev_max_req = mxr8lv; if (0 < conf_info.end_level && conf_info.end_level < mxr8lv) { - nlev_max_req = conf_info.end_level; + nlev_max_req = (int)conf_info.end_level; mlog << Debug(4) << "Request up to " << nlev_max_req << " vertical levels\n"; } - int grib_code, bufr_var_index; + int grib_code; + int bufr_var_index; map message_type_map = conf_info.getMessageTypeMap(); int bin_count = nint(npbmsg/20.0); @@ -1036,7 +1038,6 @@ void process_pbfile(int i_pb) { float cape_qm = bad_data_float; // To compute PBL - int pbl_level = 0; int pbl_code = -1; float pbl_p, pbl_h; float pbl_qm = bad_data_float; @@ -1048,7 +1049,7 @@ void process_pbfile(int i_pb) { bool cal_mlcape = bufr_obs_name_arr.has(derived_mlcape, mlcape_code, false); bool is_same_header; - unixtime prev_hdr_vld_ut = (unixtime) 0; + auto prev_hdr_vld_ut = (unixtime) 0; char prev_hdr_typ[max_str_len], prev_hdr_sid[max_str_len]; double prev_hdr_lat, prev_hdr_lon, prev_hdr_elv; map pqtzuv_map_tq; @@ -1063,14 +1064,13 @@ void process_pbfile(int i_pb) { mlcape_cnt_missing_values = mlcape_cnt_zero_values = 0; if (cal_pbl) { - is_same_header = false; prev_hdr_vld_ut = -1; m_strncpy(prev_hdr_typ, not_assigned, m_strlen(not_assigned), method_name_s, "prev_hdr_typ"); m_strncpy(prev_hdr_sid, not_assigned, m_strlen(not_assigned), method_name_s, "prev_hdr_sid"); } IMM = JMM =1; - p1d = t1d = q1d = r8bfms * 10; + p1d = t1d = q1d = (float)r8bfms * 10; cape_h = pbl_h = 0; cape_p = pbl_p = bad_data_float; @@ -1088,7 +1088,7 @@ void process_pbfile(int i_pb) { for (int idx=0; idx 0) { if(bin_count > 0 && (i_read+1)%bin_count == 0) { @@ -1167,7 +1167,8 @@ void process_pbfile(int i_pb) { } if (!is_prepbufr) { - int index, req_hdr_level = 1; + int index; + int req_hdr_level = 1; ConcatString tmp_str; //Read header (station id, lat, lon, ele, time) @@ -1286,11 +1287,9 @@ void process_pbfile(int i_pb) { // Include the area mask rejection counts with the polyline since // it is specified using the mask.poly config option. - if(apply_area_mask) { - if(!conf_info.area_mask.s_is_on(nint(x), nint(y))) { - rej_poly++; - continue; - } + if(apply_area_mask && !conf_info.area_mask.s_is_on(nint(x), nint(y))) { + rej_poly++; + continue; } } @@ -1385,9 +1384,6 @@ void process_pbfile(int i_pb) { } do_pbl = cal_pbl && 0 == strcmp("ADPUPA", hdr_typ); - if (do_pbl) { - pbl_level = 0; - } // Search through the vertical levels for(lv=0, n_hdr_obs = 0; lv MAX_FORTRAN_FILE_ID || unit < MIN_FORTRAN_FILE_ID) { + mlog << Error << "\n" << method_name << " -> " + << "Invalid file ID [" << unit << "] between 1 and 99 for BUFR table.\n\n"; + } + get_variable_info(blk_file, unit); - // Assume that the input PrepBufr file is unblocked. - // Block the PrepBufr file and open it for reading. + // The input PrepBufr file is blocked already. unit = dump_unit + i_pb; - blk_file = make_temp_file_name(blk_prefix.c_str(), nullptr); - pblock(file_name.c_str(), blk_file.c_str(), block); if (unit > MAX_FORTRAN_FILE_ID || unit < MIN_FORTRAN_FILE_ID) { mlog << Error << "\n" << method_name << " -> " << "Invalid file ID [" << unit << "] between 1 and 99.\n\n"; @@ -2169,9 +2161,6 @@ void process_pbfile_metadata(int i_pb) { mlog << Debug(1) << method_name << " -> " << "the number of records: " << npbmsg << "\n"; - // Open the blocked temp PrepBufr file for reading - openpb_(blk_file.c_str(), &unit); - // Use the number of records requested by the user if there // are enough present. if(nmsg >= 0 && nmsg <= npbmsg) { @@ -2189,16 +2178,17 @@ void process_pbfile_metadata(int i_pb) { << "No Bufr messages to process in file: " << pbfile[i_pb] << "\n\n"; - closepb_(&unit); - // Delete the temporary blocked file remove_temp_file(blk_file); return; } + // Open the blocked temp PrepBufr file for reading + openpb_(blk_file.c_str(), &unit); + // Initialize counts - i_ret = i_msg = 0; + i_ret = 0; StringArray headers; StringArray tmp_hdr_array; headers.add(prepbufr_hdrs); diff --git a/src/tools/other/wwmca_tool/nc_output.cc b/src/tools/other/wwmca_tool/nc_output.cc index 9a03e9d0b0..5819533cba 100644 --- a/src/tools/other/wwmca_tool/nc_output.cc +++ b/src/tools/other/wwmca_tool/nc_output.cc @@ -56,7 +56,7 @@ void WwmcaRegridder::do_output(const char * output_filename) int accum_time, x, y; float f; double v; - ConcatString s; + ConcatString s, cs; const int Nx = ToGrid->nx(); const int Ny = ToGrid->ny(); @@ -129,16 +129,23 @@ void WwmcaRegridder::do_output(const char * output_filename) if ( cp_nh->valid() != (unixtime) 0 && cp_nh->valid() != valid_time ) { - - mlog << Warning << "\nWwmcaRegridder::do_output(const char * output_filename) -> " - << "the config file valid time (" - << unix_to_yyyymmdd_hhmmss(valid_time) - << ") and data file valid time (" - << unix_to_yyyymmdd_hhmmss(cp_nh->valid()) - << ") do not match, using config file time.\n\n"; - + + cs << cs_erase + << "the config file valid time (" + << unix_to_yyyymmdd_hhmmss(valid_time) + << ") and data file valid time (" + << unix_to_yyyymmdd_hhmmss(cp_nh->valid()) + << ") do not match, using config file time."; + + if(Config->time_offset_warning( + (int) (cp_nh->valid() != valid_time))) { + mlog << Warning << "\nWwmcaRegridder::do_output(const char * output_filename) -> " + << cs << "\n\n"; + } + else { + mlog << Debug(3) << cs << "\n"; + } } - } else { valid_time = cp_nh->valid(); @@ -164,16 +171,23 @@ void WwmcaRegridder::do_output(const char * output_filename) if ( cp_nh->init() != (unixtime) 0 && cp_nh->init() != init_time ) { - - mlog << Warning << "\nWwmcaRegridder::do_output(const char * output_filename) -> " - << "the config file initialization time (" - << unix_to_yyyymmdd_hhmmss(init_time) - << ") and data file initialization time (" - << unix_to_yyyymmdd_hhmmss(cp_nh->init()) - << ") do not match, using config file time.\n\n"; - + + cs << cs_erase + << "the config file initialization time (" + << unix_to_yyyymmdd_hhmmss(init_time) + << ") and data file initialization time (" + << unix_to_yyyymmdd_hhmmss(cp_nh->init()) + << ") do not match, using config file time."; + + if(Config->time_offset_warning( + (int) (cp_nh->init() != init_time))) { + mlog << Warning << "\nWwmcaRegridder::do_output(const char * output_filename) -> " + << cs << "\n\n"; + } + else { + mlog << Debug(3) << cs << "\n"; + } } - } else { init_time = cp_nh->init();