diff --git a/.github/jobs/set_job_controls.sh b/.github/jobs/set_job_controls.sh index 8e3fb07da7..818c62c4f0 100755 --- a/.github/jobs/set_job_controls.sh +++ b/.github/jobs/set_job_controls.sh @@ -6,6 +6,7 @@ run_unit_tests=false run_diff=false run_update_truth=false met_base_repo=met-base +# TODO: Change 3.4 back to 3.5 after solving TC-Diag failure met_base_tag=3.4-latest input_data_version=develop truth_data_version=develop diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index caa120a0ce..b60635be02 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -148,7 +148,7 @@ jobs: - jobid: 'job1' tests: 'ascii2nc' - jobid: 'job2' - tests: 'pb2nc madis2nc pcp_combine gen_ens_prod regrid' + tests: 'pb2nc madis2nc pcp_combine gen_ens_prod regrid winds' fail-fast: false steps: - uses: actions/checkout@v6 diff --git a/data/config/ConfigConstants b/data/config/ConfigConstants index acf34b788a..c8d5c432c7 100644 --- a/data/config/ConfigConstants +++ b/data/config/ConfigConstants @@ -22,6 +22,18 @@ output_precision = 5; // MET_TMP_DIR. tmp_dir = "/tmp"; +//////////////////////////////////////////////////////////////////////////////// +// +// Settings to define wind vector metadata. +// +//////////////////////////////////////////////////////////////////////////////// + +// Comma-separated lists of wind variable names for rotating and deriving +u_wind_field_name = "UGRD,U"; +v_wind_field_name = "VGRD,V"; +wind_speed_field_name = "WIND,SP"; +wind_direction_field_name = "WDIR,DD"; + //////////////////////////////////////////////////////////////////////////////// // // Standard unit conversion functions. @@ -202,3 +214,7 @@ CLIMO_STD_ANOM = 3; FCST_ANOM = 4; FCST_STD_ANOM = 5; +// Power spectrum missing flag types +NONE = 1; +MEAN = 2; +VALUE = 3; diff --git a/data/config/GridDiagConfig_default b/data/config/GridDiagConfig_default index a732974c27..5cb4c79c22 100644 --- a/data/config/GridDiagConfig_default +++ b/data/config/GridDiagConfig_default @@ -32,6 +32,15 @@ regrid = { censor_thresh = []; censor_val = []; +// +// Specify the handling of missing data for power spectra +// +power_spectrum = { + missing_flag = NONE; + missing_value = 0.0; + vld_thresh = 0.5; +} + // // Data fields // @@ -68,9 +77,10 @@ mask = { // Diagnostic output types // output_flag = { - histogram_1d = TRUE; - histogram_2d = TRUE; - info_theory = FALSE; + histogram_1d = TRUE; + histogram_2d = TRUE; + info_theory = FALSE; + power_spectrum = FALSE; } //////////////////////////////////////////////////////////////////////////////// diff --git a/docs/Users_Guide/config_options.rst b/docs/Users_Guide/config_options.rst index d02d43eb7e..c849c93749 100644 --- a/docs/Users_Guide/config_options.rst +++ b/docs/Users_Guide/config_options.rst @@ -661,6 +661,41 @@ Some tools override the temporary directory by the command line argument A description of the use of temporary files in MET can be found in :numref:`Contributor's Guide Section %s `. +.. _config_wind_field_names: + +wind_field_names +---------------- + +As described in :numref:`PS_wind_rotation_derivation`, MET can automatically +derive wind variables and also rotate them from being grid-relative to earth-relative. +Logic is provided for each supported input file type to identify input wind components +and determine whether they are defined as being grid-relative or earth-relative. + +The following configuration options can be set to refine the metadata, when needed: + +.. code-block:: none + + is_u_wind = boolean; + is_v_wind = boolean; + is_grid_relative = boolean; + is_wind_speed = boolean; + is_wind_direction = boolean; + is_kinetic_energy = boolean; + +When deriving and rotating winds, MET searches the input file for matching wind +components. That search is driven by the following configuration options: + +.. code-block:: none + + u_wind_field_name = "UGRD,U"; + v_wind_field_name = "VGRD,V"; + wind_speed_field_name = "WIND,SP"; + wind_direction_field_name = "WDIR,DD"; + +Each is a comma-separated list of wind variable names to be searched. Users can +explicity set these options to configure what data should be used in the wind +derivation and rotation logic. + message_type_group_map ---------------------- @@ -1163,6 +1198,7 @@ below: is_grid_relative = boolean; is_wind_speed = boolean; is_wind_direction = boolean; + is_kinetic_energy = boolean; is_prob = boolean; diff --git a/docs/Users_Guide/grid-diag.rst b/docs/Users_Guide/grid-diag.rst index cf11fcb44e..00e8e4a492 100644 --- a/docs/Users_Guide/grid-diag.rst +++ b/docs/Users_Guide/grid-diag.rst @@ -7,9 +7,13 @@ Grid-Diag Tool Introduction ============ -The Grid-Diag tool creates histograms (probability distributions when normalized) for an arbitrary collection of data fields and levels. Joint histograms are created for all possible pairs of variables. If no masking region is specified to subset the data fields spatially, then all points in the input domain are used. However, an arbitrary number of masking regions can be specified and output is created for each one. The histograms are accumulated over all of the input data files. Typically this tool is run with a time series of input data files, similar to Series-Analysis. +Unlike the Grid-Stat and Point-Stat tools, which compute verification statistics for matched pairs of forecast and observation values, the Grid-Diag tool provides diagnostic information based on the distribution of values found in gridded input files. -The Grid-Diag tool also uses the histograms to derive information theory statistics. Entropy is derived from each 1-dimensional histogram, and joint entropy and mutual information are derived from each 2-dimensional joint histogram. These statistics are defined using log base 2, rather than the natural logarithm which is also commonly used. +Grid-Diag creates histograms (probability distributions when normalized) for an arbitrary collection of data fields and levels. Joint histograms are created for all possible pairs of variables. If no masking region is specified to subset the data fields spatially, then all points in the input domain are used. However, an arbitrary number of masking regions can be specified and output is created for each one. The histograms are accumulated over all of the input data files. Typically this tool is run with a time series of input data files, similar to Series-Analysis. + +Grid-Diag also uses the histograms to derive information theory statistics. Entropy is derived from each 1-dimensional histogram, and joint entropy and mutual information are derived from each 2-dimensional joint histogram. These statistics are defined using log base 2, rather than the natural logarithm which is also commonly used. + +Finally, Grid-Diag also derives power spectra for each input field. However, power spectra are computed over the full model domain rather than being subset by spatial masking regions. Note that special logic is applied when processing kinetic energy. Grid-Diag reads the corresponding U and V-wind vector components and applies a discrete cosine transform to them separately prior to copmuting the power spectrum. Practical Information ===================== @@ -77,6 +81,20 @@ The configuration options listed above are common to many MET tools and are desc _____________________ +.. code-block:: none + + power_spectrum = { + missing_flag = NONE; + missing_value = 0.0; + vld_thresh = 0.5; + } + +The **power_spectrum** dictionary defines options for computing power spectra and can be specified separately for each **data.field** entry below. + +The **missing_flag** and **missing_value** entries define how bad data values should be handled. For all other output types, bad data values are ignored but they are problematic for power spectra. Set **missing_flag** to **NONE** (default) to skip power spectrum when bad data is present, to **MEAN** to replace bad data with the mean of each input field, or to **VALUE** to replace bad data with the constant numeric value specified by **missing_value**. Set **vld_thresh** to a number beween 0 and 1 to define the required ratio of valid data to be present to compute power spectra output for that field. + +_____________________ + .. code-block:: none data = { @@ -105,9 +123,10 @@ _____________________ .. code-block:: none output_flag = { - histogram_1d = TRUE; - histogram_2d = TRUE; - info_theory = FALSE; + histogram_1d = TRUE; + histogram_2d = TRUE; + info_theory = FALSE; + power_spectrum = FALSE; } The **output_flag** dictionary controls the type of output that the Grid-Diag tool generates. Each flag should be set to **TRUE** or **FALSE** to enable the computation and writing of one or more variables to the output NetCDF file, as described below: @@ -118,6 +137,8 @@ The **output_flag** dictionary controls the type of output that the Grid-Diag to 3. **info_theory** for information theory metrics, including entropy for each **data.field** entry and mutual information and joint entropy for each pair of entries. +4. **power_spectrum** for power spectrum output. + grid_diag Output File --------------------- diff --git a/docs/Users_Guide/point-stat.rst b/docs/Users_Guide/point-stat.rst index fcfefeae85..084b8c7945 100644 --- a/docs/Users_Guide/point-stat.rst +++ b/docs/Users_Guide/point-stat.rst @@ -117,6 +117,33 @@ _______________________ The forecast value at P is chosen as the grid point inside the interpolation area whose value most closely matches the observation value. +.. _PS_wind_rotation_derivation: + +Wind Rotation and Derivation +---------------------------- + +Numerical weather prediction model output often defines winds relative to the orientation of the model grid rather than true north-south and east-west directions on the earth. +However point observations typically define winds relative to true earth directions. Prior to comparing them, the model wind data must be rotated from grid-relative to +to earth-relative. While the degree of grid-to-earth rotation varies based on the projection type and location, failing to rotate the winds has a significant impact on +the verification results. + +For simplicity, the MET library code attempts to rotate all wind components from being grid-relative to earth-relative regardless of whether they are being compared to point +observations or gridded analyses. Running the MET tools at verbosity level 3 (-v 3) prints log messages to describing the wind rotation process. + +Wind fields which must be rotated include wind direction and both the U and V components of the wind vector. While wind direction can be rotated by itself, both U and V are +required to rotate either component. When processing U-wind data, MET attempts to read corresponding V-wind data and vice-versa. + +The configuration options for wind rotation and derivation in MET are described in section :numref:`config_wind_field_names`. + +When reading V-wind data to rotate U-wind or U-wind data to rotate V-wind, MET first searches using the same field name, but with both upper and lowercase U's and V's +swapped (e.g. for "U_PL" search for "V_PL"). If the result is unsuccesful, it searches other common field names specified by the **u_wind_field_name** and **v_wind_field_name** +configuration options. If needed, users should set these configuration options to indicate how the U-wind and V-wind data should be paired. + +In addition to rotating winds, MET can also derive them. If U-wind and V-wind are present in the input file, request field names of **WDIR**, **WIND**, or **KENG** +to derive wind direction, wind speed, and kinetic engery from the U and V components, respectively. If wind speed and direction are present in the input file, request field +names of **UGRD** or **VGRD** for MET to derive the U and V components from them. Depending on the wind field naming conventions, the configuration options described in +:numref:`config_wind_field_names` may be required to configure this derivation logic. + .. _PS_HiRA_framework: HiRA Framework diff --git a/docs/Users_Guide/release-notes.rst b/docs/Users_Guide/release-notes.rst index c51d86da46..067a5c93bd 100644 --- a/docs/Users_Guide/release-notes.rst +++ b/docs/Users_Guide/release-notes.rst @@ -182,6 +182,14 @@ MET Version 13.0.0 Upgrade Instructions * The "ensemble_flag.eas" and "ensemble_flag.eas_width" entries are added to enable the writing of EAS output fields. + * ConfigConstanst configuration file + + * The "u_wind_field_name", "v_wind_field_name", "wind_speed_field_name", and "wind_direction_field_name" + entries are comma-separated lists of field names to search when read wind data needed to derive winds + or rotate them from being grid-relative to earth-relative. See :numref:`PS_wind_rotation_derivation` + for a description of wind rotation and derivation and :numref:`config_wind_field_names` for the + corresponding configuration options. + .. dropdown:: Output format changes - NONE .. dropdown:: Output format changes - NONE diff --git a/internal/test_unit/bin/unit_test.sh b/internal/test_unit/bin/unit_test.sh index ebca453449..8efc70ca2c 100755 --- a/internal/test_unit/bin/unit_test.sh +++ b/internal/test_unit/bin/unit_test.sh @@ -40,6 +40,7 @@ UNIT_XML="unit_ascii2nc.xml \ unit_gen_ens_prod.xml \ unit_pcp_combine.xml \ unit_regrid.xml \ + unit_winds.xml \ unit_wwmca_regrid.xml \ unit_point_stat.xml \ unit_stat_analysis_ps.xml \ diff --git a/internal/test_unit/config/GridDiagConfig b/internal/test_unit/config/GridDiagConfig index aa6553e03d..d0f83ce170 100644 --- a/internal/test_unit/config/GridDiagConfig +++ b/internal/test_unit/config/GridDiagConfig @@ -32,6 +32,15 @@ regrid = { censor_thresh = []; censor_val = []; +// +// Specify the handling of missing data for power spectra +// +power_spectrum = { + missing_flag = NONE; + missing_value = 0.0; + vld_thresh = 0.5; +} + // // Data fields // diff --git a/internal/test_unit/config/GridDiagConfig_APCP_06_FCST_OBS b/internal/test_unit/config/GridDiagConfig_FCST_OBS similarity index 87% rename from internal/test_unit/config/GridDiagConfig_APCP_06_FCST_OBS rename to internal/test_unit/config/GridDiagConfig_FCST_OBS index f8d68d04ba..c65f376124 100644 --- a/internal/test_unit/config/GridDiagConfig_APCP_06_FCST_OBS +++ b/internal/test_unit/config/GridDiagConfig_FCST_OBS @@ -32,15 +32,19 @@ regrid = { censor_thresh = []; censor_val = []; +// +// Specify the handling of missing data for power spectra +// +power_spectrum = { + missing_flag = VALUE; + missing_value = 0.0; + vld_thresh = 0.5; +} + // // Data fields // data = { - name = "APCP"; - level = ["A06"]; - n_bins = 50; - range = [0,25]; - field = [ { ${FIELD1} }, { ${FIELD2} } @@ -62,10 +66,6 @@ mask = { // // Diagnostic output types // -output_flag = { - histogram_1d = TRUE; - histogram_2d = TRUE; - info_theory = FALSE; -} +output_flag = TRUE; //////////////////////////////////////////////////////////////////////////////// diff --git a/internal/test_unit/config/GridDiagConfig_TMP b/internal/test_unit/config/GridDiagConfig_TMP index da7c02302e..f253d6c25a 100644 --- a/internal/test_unit/config/GridDiagConfig_TMP +++ b/internal/test_unit/config/GridDiagConfig_TMP @@ -32,6 +32,15 @@ regrid = { censor_thresh = []; censor_val = []; +// +// Specify the handling of missing data for power spectra +// +power_spectrum = { + missing_flag = NONE; + missing_value = 0.0; + vld_thresh = 0.5; +} + // // Data fields // @@ -74,9 +83,10 @@ mask = { // Diagnostic output types // output_flag = { - histogram_1d = TRUE; - histogram_2d = TRUE; - info_theory = FALSE; + histogram_1d = TRUE; + histogram_2d = TRUE; + info_theory = FALSE; + power_spectrum = FALSE; } //////////////////////////////////////////////////////////////////////////////// diff --git a/internal/test_unit/xml/unit_grib_tables.xml b/internal/test_unit/xml/unit_grib_tables.xml index 88e8370e23..5430a1ea12 100644 --- a/internal/test_unit/xml/unit_grib_tables.xml +++ b/internal/test_unit/xml/unit_grib_tables.xml @@ -36,8 +36,8 @@ \ &DATA_DIR_MODEL;/grib2/um_raw/PS.557WW_SC.U_DI.F_GP.GALWEM-GD_GR.C17KM_AR.GLOBAL_DD.20160424_CY.00_FH.000_DF.GR2 \ &OUTPUT_DIR;/grib_tables/um_raw/PS.557WW_SC.U_DI.F_GP.GALWEM-GD_GR.C17KM_AR.GLOBAL_DD.20160424_CY.00_FH.000_DF.ps \ - 'name = "TCDC"; level = "L0";' \ - -title "GRIB2 um_raw L0 TCDC" \ + 'name = "TCC"; level = "L0";' \ + -title "GRIB2 um_raw L0 TCC" \ -v 1 diff --git a/internal/test_unit/xml/unit_grid_diag.xml b/internal/test_unit/xml/unit_grid_diag.xml index 81c720a2b6..df6b62471e 100644 --- a/internal/test_unit/xml/unit_grid_diag.xml +++ b/internal/test_unit/xml/unit_grid_diag.xml @@ -73,13 +73,13 @@ > &OUTPUT_DIR;/grid_diag/obs_file_list; \ &MET_BIN;/grid_diag - FIELD1 set_attr_name = "FCST_APCP"; - FIELD2 set_attr_name = "OBS_APCP"; + FIELD1 set_attr_name = "FCST_APCP"; name = "APCP"; level = "A06"; n_bins = 50; range = [0,25]; + FIELD2 set_attr_name = "OBS_APCP"; name = "APCP"; level = "A06"; n_bins = 50; range = [0,25]; \ -data &OUTPUT_DIR;/grid_diag/fcst_file_list \ -data &OUTPUT_DIR;/grid_diag/obs_file_list \ - -config &CONFIG_DIR;/GridDiagConfig_APCP_06_FCST_OBS \ + -config &CONFIG_DIR;/GridDiagConfig_FCST_OBS \ -out &OUTPUT_DIR;/grid_diag/grid_diag_APCP_06_FCST_OBS.nc \ -v 3 @@ -109,13 +109,13 @@ > &OUTPUT_DIR;/grid_diag/obs_file_list; \ &MET_BIN;/grid_diag - FIELD1 name = "APCP"; - FIELD2 name = "APCP"; + FIELD1 name = "APCP"; level = "A06"; n_bins = 50; range = [0,25]; + FIELD2 name = "APCP"; level = "A06"; n_bins = 50; range = [0,25]; \ -data &OUTPUT_DIR;/grid_diag/fcst_file_list \ -data &OUTPUT_DIR;/grid_diag/obs_file_list \ - -config &CONFIG_DIR;/GridDiagConfig_APCP_06_FCST_OBS \ + -config &CONFIG_DIR;/GridDiagConfig_FCST_OBS \ -out &OUTPUT_DIR;/grid_diag/grid_diag_APCP_06_VARN.nc \ -v 3 @@ -124,4 +124,60 @@ + + echo "&DATA_DIR_MODEL;/grib2/gfs/gfs_2012040900_F006.grib2 \ + &DATA_DIR_MODEL;/grib2/gfs/gfs_2012040900_F012.grib2 \ + &DATA_DIR_MODEL;/grib2/gfs/gfs_2012040900_F018.grib2 \ + &DATA_DIR_MODEL;/grib2/gfs/gfs_2012040900_F024.grib2" \ + > &OUTPUT_DIR;/grid_diag/uv_wind_fcst_file_list; \ + echo "&DATA_DIR_MODEL;/grib2/gfsanl/gfsanl_4_20120409_0600_000.grb2 \ + &DATA_DIR_MODEL;/grib2/gfsanl/gfsanl_4_20120409_1200_000.grb2 \ + &DATA_DIR_MODEL;/grib2/gfsanl/gfsanl_4_20120409_1800_000.grb2 \ + &DATA_DIR_MODEL;/grib2/gfsanl/gfsanl_4_20120410_0000_000.grb2" \ + > &OUTPUT_DIR;/grid_diag/uv_wind_anly_file_list; \ + &MET_BIN;/grid_diag + + FIELD1 name = "WIND"; level = "Z10"; n_bins = 200; range = [0,20]; + FIELD2 name = "WIND"; level = "Z10"; n_bins = 200; range = [0,20]; + + \ + -data &OUTPUT_DIR;/grid_diag/uv_wind_fcst_file_list \ + -data &OUTPUT_DIR;/grid_diag/uv_wind_anly_file_list \ + -config &CONFIG_DIR;/GridDiagConfig_FCST_OBS \ + -out &OUTPUT_DIR;/grid_diag/grid_diag_WIND_Z10.nc \ + -v 3 + + + &OUTPUT_DIR;/grid_diag/grid_diag_WIND_Z10.nc + + + + + echo "&DATA_DIR_MODEL;/grib2/gfs/gfs_2012040900_F006.grib2 \ + &DATA_DIR_MODEL;/grib2/gfs/gfs_2012040900_F012.grib2 \ + &DATA_DIR_MODEL;/grib2/gfs/gfs_2012040900_F018.grib2 \ + &DATA_DIR_MODEL;/grib2/gfs/gfs_2012040900_F024.grib2" \ + > &OUTPUT_DIR;/grid_diag/keng_fcst_file_list; \ + echo "&DATA_DIR_MODEL;/grib2/gfsanl/gfsanl_4_20120409_0600_000.grb2 \ + &DATA_DIR_MODEL;/grib2/gfsanl/gfsanl_4_20120409_1200_000.grb2 \ + &DATA_DIR_MODEL;/grib2/gfsanl/gfsanl_4_20120409_1800_000.grb2 \ + &DATA_DIR_MODEL;/grib2/gfsanl/gfsanl_4_20120410_0000_000.grb2" \ + > &OUTPUT_DIR;/grid_diag/keng_anly_file_list; \ + &MET_BIN;/grid_diag + + FIELD1 name = "KENG"; level = "Z10"; n_bins = 140; range = [0,140]; + FIELD2 name = "KENG"; level = "Z10"; n_bins = 140; range = [0,140]; + + \ + -data &OUTPUT_DIR;/grid_diag/keng_fcst_file_list \ + -data &OUTPUT_DIR;/grid_diag/keng_anly_file_list \ + -config &CONFIG_DIR;/GridDiagConfig_FCST_OBS \ + -out &OUTPUT_DIR;/grid_diag/grid_diag_KENG_Z10.nc \ + -v 3 + + + &OUTPUT_DIR;/grid_diag/grid_diag_KENG_Z10.nc + + + diff --git a/internal/test_unit/xml/unit_plot_data_plane.xml b/internal/test_unit/xml/unit_plot_data_plane.xml index e42b275e41..999773a57d 100644 --- a/internal/test_unit/xml/unit_plot_data_plane.xml +++ b/internal/test_unit/xml/unit_plot_data_plane.xml @@ -615,7 +615,7 @@ \ &DATA_DIR_MODEL;/wrf/wrfout_solarwind_d02_2019-08-31_12:00:00 \ &OUTPUT_DIR;/plot_data_plane/wrf_west_east_stag.ps \ - 'name="U"; level="(0,0,*,*)";' \ + 'name="U"; level="(0,0,*,*)"; is_grid_relative=false;' \ -title "X-wind component (staggered)" \ -v 1 @@ -629,7 +629,7 @@ \ &DATA_DIR_MODEL;/wrf/wrfout_solarwind_d02_2019-08-31_12:00:00 \ &OUTPUT_DIR;/plot_data_plane/wrf_south_north_stag.ps \ - 'name="V"; level="(0,0,*,*)";' \ + 'name="V"; level="(0,0,*,*)"; is_grid_relative=false;' \ -title "Y-wind component (staggered)" \ -v 1 diff --git a/internal/test_unit/xml/unit_winds.xml b/internal/test_unit/xml/unit_winds.xml new file mode 100644 index 0000000000..6638db36b9 --- /dev/null +++ b/internal/test_unit/xml/unit_winds.xml @@ -0,0 +1,90 @@ + + + + + + + + + +]> + + + + + + &TEST_DIR; + true + + + + + + + + &MET_BIN;/pcp_combine + \ + -add &DATA_DIR_MODEL;/grib1/nam/nam_2012040900_F012.grib \ + -field 'name="UGRD"; level="Z10"; set_attr_name="UWIND_GRID"; is_grid_relative=false;' \ + -field 'name="UGRD"; level="Z10"; set_attr_name="UWIND_EARTH";' \ + -field 'name="VGRD"; level="Z10"; set_attr_name="VWIND_GRID"; is_grid_relative=false;' \ + -field 'name="VGRD"; level="Z10"; set_attr_name="VWIND_EARTH";' \ + -field 'name="WDIR"; level="Z10"; set_attr_name="D_WDIR_GRID"; is_grid_relative=false;' \ + -field 'name="WDIR"; level="Z10"; set_attr_name="D_WDIR_EARTH";' \ + -field 'name="WIND"; level="Z10"; set_attr_name="D_WIND";' \ + -field 'name="KENG"; level="Z10"; set_attr_name="D_KENG";' \ + &OUTPUT_DIR;/winds/pcp_combine_nam_winds_GRIB1.nc \ + -v 3 + + + &OUTPUT_DIR;/winds/pcp_combine_nam_winds_GRIB1.nc + + + + + + + + + + &MET_BIN;/pcp_combine + \ + -add &DATA_DIR_MODEL;/grib2/nbm/blend.t13z.core.f119.co.grib2 \ + -field 'name="UGRD"; level="Z10"; set_attr_name="D_UWIND";' \ + -field 'name="VGRD"; level="Z10"; set_attr_name="D_VWIND";' \ + -field 'name="WDIR"; level="Z10"; set_attr_name="WDIR";' \ + -field 'name="WDIR"; level="Z10"; set_attr_name="WDIR_ROTATE"; is_grid_relative=true;' \ + -field 'name="WIND"; level="Z10"; set_attr_name="WIND";' \ + &OUTPUT_DIR;/winds/pcp_combine_nbm_winds_GRIB2.nc \ + -v 3 + + + &OUTPUT_DIR;/winds/pcp_combine_nbm_winds_GRIB2.nc + + + + + + + + + &MET_BIN;/pcp_combine + \ + -add &DATA_DIR_MODEL;/wrf/wrfout_solarwind_d02_2019-08-31_12:00:00 \ + -field 'name="U10"; level="(0,*,*)"; set_attr_name="U10_GRID"; is_grid_relative=false;' \ + -field 'name="U10"; level="(0,*,*)"; set_attr_name="U10_EARTH";' \ + -field 'name="V10"; level="(0,*,*)"; set_attr_name="V10_GRID"; is_grid_relative=false;' \ + -field 'name="V10"; level="(0,*,*)"; set_attr_name="V10_EARTH";' \ + -field 'name="WDIR10"; level="(0,*,*)"; set_attr_name="WDIR10_GRID"; is_wind_direction=true; u_wind_field_name="U10"; v_wind_field_name="V10"; is_grid_relative=false;' \ + -field 'name="WDIR10"; level="(0,*,*)"; set_attr_name="WDIR10_EARTH"; is_wind_direction=true; u_wind_field_name="U10"; v_wind_field_name="V10";' \ + -field 'name="WIND10"; level="(0,*,*)"; is_wind_speed=true; u_wind_field_name="U10"; v_wind_field_name="V10";' \ + &OUTPUT_DIR;/winds/pcp_combine_winds_NETCDF_WRF.nc \ + -v 3 + + + &OUTPUT_DIR;/winds/pcp_combine_winds_NETCDF_WRF.nc + + + + diff --git a/src/basic/vx_config/config_constants.h b/src/basic/vx_config/config_constants.h index 75e0629123..4a3d442a1c 100644 --- a/src/basic/vx_config/config_constants.h +++ b/src/basic/vx_config/config_constants.h @@ -294,9 +294,17 @@ struct InterpInfo { ~InterpInfo() { clear(); } InterpInfo(InterpInfo const &i) { *this = i; } InterpInfo &operator=(const InterpInfo &a) noexcept; // SonarQube findings - bool operator==(const InterpInfo &) const; void clear(); void validate(); // Ensure that width and method are accordant + + friend bool operator==(const InterpInfo &lhs, const InterpInfo &rhs) { + return(lhs.field == rhs.field && + lhs.vld_thresh == rhs.vld_thresh && + lhs.n_interp == rhs.n_interp && + lhs.method == rhs.method && + lhs.width == rhs.width && + lhs.shape == rhs.shape); + } }; //////////////////////////////////////////////////////////////////////// @@ -450,8 +458,48 @@ struct MaskLatLon { ~MaskLatLon() { clear(); } MaskLatLon(MaskLatLon const &i) { *this = i; } MaskLatLon &operator=(const MaskLatLon &a) noexcept; - bool operator==(const MaskLatLon &) const; void clear(); + + friend bool operator==(const MaskLatLon &lhs, const MaskLatLon &rhs) { + return(lhs.name == rhs.name && + lhs.lat_thresh == rhs.lat_thresh && + lhs.lon_thresh == rhs.lon_thresh); + } +}; + +//////////////////////////////////////////////////////////////////////// + +// +// Struct to store wind vector metadata +// + +struct WindMetadata { + + StringArray u_wind; // U-wind field names + StringArray v_wind; // V-wind field names + StringArray wind_speed; // Wind speed field names + StringArray wind_direction; // Wind direction field names + StringArray kinetic_energy; // Kinetic energy field names + + WindMetadata() { clear(); } + ~WindMetadata() { clear(); } + WindMetadata(WindMetadata const &i) { *this = i; } + WindMetadata &operator=(const WindMetadata &a) noexcept; + void clear(); + + bool is_u_wind(const std::string &s) const { return u_wind.has(s); } + bool is_v_wind(const std::string &s) const { return v_wind.has(s); } + bool is_wind_speed(const std::string &s) const { return wind_speed.has(s); } + bool is_wind_direction(const std::string &s) const { return wind_direction.has(s); } + bool is_kinetic_energy(const std::string &s) const { return kinetic_energy.has(s); } + + friend bool operator==(const WindMetadata &lhs, const WindMetadata &rhs) { + return(lhs.u_wind == rhs.u_wind && + lhs.v_wind == rhs.v_wind && + lhs.wind_speed == rhs.wind_speed && + lhs.wind_direction == rhs.wind_direction && + lhs.kinetic_energy == rhs.kinetic_energy); + } }; //////////////////////////////////////////////////////////////////////// @@ -560,6 +608,35 @@ enum class MatchType { NoMerge // Match with no additional merging }; +//////////////////////////////////////////////////////////////////////// + +// +// Enumeration for Grid-Diag Power Spectrum missing flag options +// + +enum class MissingDataType { + None, // No missing data type + Mean, // Replace missing data with the mean of the field + Value // Replace missing data with a constant value +}; + +// +// Struct to store power spectrum information +// + +struct PowerSpectrumInfo { + MissingDataType missing_flag; + double missing_value; + double vld_thresh; + bool skip; + + PowerSpectrumInfo() { clear(); } + ~PowerSpectrumInfo() { clear(); } + PowerSpectrumInfo(PowerSpectrumInfo const &i) { *this = i; } + PowerSpectrumInfo &operator=(const PowerSpectrumInfo &a) noexcept; + void clear(); +}; + //////////////////////////////////////////////////////////////////////// // // Constants used in configuartion files @@ -751,6 +828,15 @@ static const char conf_key_ugrid_map_config[] = "ugrid_map_config"; static const char conf_key_ugrid_max_distance_km[] = "ugrid_max_distance_km"; static const char conf_key_ugrid_metadata_map[] = "ugrid_metadata_map"; +// +// Entries to define wind vector metadata +// + +static const char conf_key_u_wind_field_name[] = "u_wind_field_name"; +static const char conf_key_v_wind_field_name[] = "v_wind_field_name"; +static const char conf_key_wind_speed_field_name[] = "wind_speed_field_name"; +static const char conf_key_wind_direction_field_name[] = "wind_direction_field_name"; + // // Entries to override file metadata // @@ -771,6 +857,7 @@ static const char conf_key_is_v_wind[] = "is_v_wind"; static const char conf_key_is_grid_relative[] = "is_grid_relative"; static const char conf_key_is_wind_speed[] = "is_wind_speed"; static const char conf_key_is_wind_direction[] = "is_wind_direction"; +static const char conf_key_is_kinetic_energy[] = "is_kinetic_energy"; static const char conf_key_is_prob[] = "is_prob"; // @@ -924,9 +1011,13 @@ static const char conf_val_beta[] = "BETA"; // Grid-Diag specific parameter key names // -static const char conf_key_hist1d_flag[] = "histogram_1d"; -static const char conf_key_hist2d_flag[] = "histogram_2d"; -static const char conf_key_info_theory_flag[] = "info_theory"; +static const char conf_key_hist1d_flag[] = "histogram_1d"; +static const char conf_key_hist2d_flag[] = "histogram_2d"; +static const char conf_key_info_theory_flag[] = "info_theory"; +static const char conf_key_power_spectrum_flag[] = "power_spectrum"; +static const char conf_key_power_spectrum[] = "power_spectrum"; +static const char conf_key_missing_flag[] = "missing_flag"; +static const char conf_key_missing_value[] = "missing_value"; // // STAT-Analysis and Pair-Stat specific parameter key names @@ -1329,8 +1420,6 @@ static const char conf_key_n_azimuth[] = "n_azimuth"; static const char conf_key_delta_range[] = "delta_range_km"; static const char conf_key_rmw_scale[] = "rmw_scale"; static const char conf_key_compute_tangential_and_radial_winds[] = "compute_tangential_and_radial_winds"; -static const char conf_key_u_wind_field_name[] = "u_wind_field_name"; -static const char conf_key_v_wind_field_name[] = "v_wind_field_name"; static const char conf_key_radial_velocity_field_name[] = "radial_velocity_field_name"; static const char conf_key_tangential_velocity_field_name[] = "tangential_velocity_field_name"; static const char conf_key_radial_velocity_long_field_name[] = "radial_velocity_long_field_name"; @@ -1446,6 +1535,14 @@ static const char conf_val_merge_both[] = "MERGE_BOTH"; static const char conf_val_merge_fcst[] = "MERGE_FCST"; static const char conf_val_no_merge[] = "NO_MERGE"; +// +// Grid-Diag specific parameter value names +// + +// Power spectrum missing flag values +static const char conf_val_mean[] = "MEAN"; +static const char conf_val_value[] = "VALUE"; + //////////////////////////////////////////////////////////////////////// // Default GRIB version 1 parameters diff --git a/src/basic/vx_config/config_util.cc b/src/basic/vx_config/config_util.cc index d9ec914550..2203b74843 100644 --- a/src/basic/vx_config/config_util.cc +++ b/src/basic/vx_config/config_util.cc @@ -743,20 +743,6 @@ void MaskLatLon::clear() { /////////////////////////////////////////////////////////////////////////////// -bool MaskLatLon::operator==(const MaskLatLon &v) const { - bool match = true; - - if(!(name == v.name ) || - !(lat_thresh == v.lat_thresh) || - !(lon_thresh == v.lon_thresh)) { - match = false; - } - - return match; -} - -/////////////////////////////////////////////////////////////////////////////// - MaskLatLon &MaskLatLon::operator=(const MaskLatLon &a) noexcept { if(this != &a) { name = a.name; @@ -817,6 +803,66 @@ vector parse_conf_llpnt_mask(Dictionary *dict) { return v; } +/////////////////////////////////////////////////////////////////////////////// +// +// Code for WindMetadata struct +// +/////////////////////////////////////////////////////////////////////////////// + +void WindMetadata::clear() { + u_wind.clear(); + v_wind.clear(); + wind_speed.clear(); + wind_direction.clear(); + kinetic_energy.clear(); +} + +/////////////////////////////////////////////////////////////////////////////// + +WindMetadata &WindMetadata::operator=(const WindMetadata &a) noexcept { + if(this != &a) { + u_wind = a.u_wind; + v_wind = a.v_wind; + wind_speed = a.wind_speed; + wind_direction = a.wind_direction; + kinetic_energy = a.kinetic_energy; + } + return *this; +} + +/////////////////////////////////////////////////////////////////////////////// + +WindMetadata parse_conf_wind_metadata(Dictionary *dict) { + WindMetadata info; + + if(!dict) { + mlog << Error << "\nparse_conf_wind_metadata() -> " + << "empty dictionary!\n\n"; + exit(1); + } + + // Conf: u_wind_field_name + info.u_wind.parse_css(dict->lookup_string(conf_key_u_wind_field_name)); + + // Conf: v_wind_field_name + info.v_wind.parse_css(dict->lookup_string(conf_key_v_wind_field_name)); + + // Conf: wind_speed_field_name + info.wind_speed.parse_css(dict->lookup_string(conf_key_wind_speed_field_name)); + + // Conf: wind_direction_field_name + info.wind_direction.parse_css(dict->lookup_string(conf_key_wind_direction_field_name)); + + // Kinetic energy names are hard-coded + info.kinetic_energy.parse_css("KENG"); + + return info; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Utility parsing functions +// /////////////////////////////////////////////////////////////////////////////// SingleThresh parse_conf_quality_mark_thresh(Dictionary *dict) { @@ -1732,23 +1778,6 @@ void InterpInfo::validate() { /////////////////////////////////////////////////////////////////////////////// -bool InterpInfo::operator==(const InterpInfo &v) const { - bool match = true; - - if(!(field == v.field ) || - !(vld_thresh == v.vld_thresh) || - !(n_interp == v.n_interp ) || - !(method == v.method ) || - !(width == v.width ) || - !(shape == v.shape )) { - match = false; - } - - return match; -} - -/////////////////////////////////////////////////////////////////////////////// - InterpInfo & InterpInfo::operator=(const InterpInfo &a) noexcept { if(this != &a) { field = a.field; @@ -3885,6 +3914,96 @@ NormalizeType parse_conf_normalize(Dictionary *dict) { return t; } +/////////////////////////////////////////////////////////////////////////////// + +MissingDataType int_to_missingdatatype(int v) { + MissingDataType t = MissingDataType::None; + + // Convert integer to enumerated MissingDataType + if(v == conf_const.lookup_int(conf_val_none)) t = MissingDataType::None; + else if(v == conf_const.lookup_int(conf_val_mean)) t = MissingDataType::Mean; + else if(v == conf_const.lookup_int(conf_val_value)) t = MissingDataType::Value; + else { + mlog << Error << "\nint_to_missingdatatype() -> " + << "Unexpected value of " << v << ".\n\n"; + exit(1); + } + + return t; +} + +/////////////////////////////////////////////////////////////////////////////// + +ConcatString missingdatatype_to_string(MissingDataType type) { + ConcatString s; + + // Convert enumerated MissingDataType to string + switch(type) { + case MissingDataType::None: s = conf_val_none; break; + case MissingDataType::Mean: s = conf_val_mean; break; + case MissingDataType::Value: s = conf_val_value; break; + default: + mlog << Error << "\nmissingdatatype_to_string() -> " + << "Unexpected MissingDataType value of " + << enum_class_as_int(type) << ".\n\n"; + exit(1); + } + + return s; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Code for PowerSpectrumInfo struct +// +/////////////////////////////////////////////////////////////////////////////// + +void PowerSpectrumInfo::clear() { + missing_flag = MissingDataType::None; + missing_value = bad_data_double; + vld_thresh = bad_data_double; + skip = false; +} + +/////////////////////////////////////////////////////////////////////////////// + +PowerSpectrumInfo &PowerSpectrumInfo::operator=(const PowerSpectrumInfo &a) noexcept { + if(this != &a) { + missing_flag = a.missing_flag; + missing_value = a.missing_value; + vld_thresh = a.vld_thresh; + skip = a.skip; + } + return *this; +} + +/////////////////////////////////////////////////////////////////////////////// + +PowerSpectrumInfo parse_conf_power_spectrum(Dictionary *dict) { + Dictionary *ps_dict = (Dictionary *) nullptr; + PowerSpectrumInfo info; + + if(!dict) { + mlog << Error << "\nparse_conf_power_spectrum() -> " + << "empty dictionary!\n\n"; + exit(1); + } + + // Conf: power_spectrum + ps_dict = dict->lookup_dictionary(conf_key_power_spectrum); + + // Conf: missing_flag + info.missing_flag = int_to_missingdatatype(ps_dict->lookup_int(conf_key_missing_flag)); + + // Conf: missing_value + info.missing_value = ps_dict->lookup_double(conf_key_missing_value); + + // Conf: vld_thresh + info.vld_thresh = ps_dict->lookup_double(conf_key_vld_thresh); + + return info; +} + /////////////////////////////////////////////////////////////////////////////// // // Print consistent error message and exit diff --git a/src/basic/vx_config/config_util.h b/src/basic/vx_config/config_util.h index 3ed2fe9e7a..a1ee1b4c60 100644 --- a/src/basic/vx_config/config_util.h +++ b/src/basic/vx_config/config_util.h @@ -143,6 +143,7 @@ extern void parse_add_conf_ugrid_metadata_map( std::map *m); extern SurfaceInfo parse_conf_surface_info(Dictionary *dict); extern void parse_conf_topo_mask_interp(Dictionary *dict, SurfaceInfo &); +extern WindMetadata parse_conf_wind_metadata(Dictionary *dict); extern void check_full_grid_mask(StringArray &, const StringArray *, const StringArray *, @@ -201,7 +202,11 @@ extern ConcatString dist_to_string(DistType, const NumArray &); extern ConcatString griddecomptype_to_string(GridDecompType); extern ConcatString wavelettype_to_string(WaveletType); +extern MissingDataType int_to_missingdatatype(int); +extern ConcatString missingdatatype_to_string(MissingDataType); + extern int parse_conf_percentile(Dictionary *dict); +extern PowerSpectrumInfo parse_conf_power_spectrum(Dictionary *dict); extern void python_compile_error(const char *caller=nullptr); extern void ugrid_compile_error(const char *caller=nullptr); diff --git a/src/basic/vx_util/data_plane.cc b/src/basic/vx_util/data_plane.cc index 7809864905..f828c41ead 100644 --- a/src/basic/vx_util/data_plane.cc +++ b/src/basic/vx_util/data_plane.cc @@ -458,6 +458,16 @@ void DataPlane::set_all(float *data, int nx, int ny) { /////////////////////////////////////////////////////////////////////////////// +bool DataPlane::has_bad_data() const { + + // Search for bad data + auto it = find(Data.begin(), Data.end(), bad_data_double); + + return it != Data.end(); +} + +/////////////////////////////////////////////////////////////////////////////// + bool DataPlane::is_all_bad_data() const { bool status = true; @@ -494,6 +504,27 @@ int DataPlane::n_good_data() const { /////////////////////////////////////////////////////////////////////////////// +double DataPlane::mean() const { + double sum = 0.0; + int n = 0; + +#pragma omp parallel default(none) \ + shared(sum, n) + { + + // Accumulate sum +#pragma omp for schedule(static) \ + reduction(+: sum, n) + for(int j=0; j 0 ? sum / n : bad_data_double); +} + +/////////////////////////////////////////////////////////////////////////////// + double DataPlane::get(int x, int y) const { int n; @@ -1332,6 +1363,24 @@ void DataPlaneArray::levels(int n, double & _low, double & _up) const { /////////////////////////////////////////////////////////////////////////////// +bool DataPlaneArray::levels_match(const DataPlaneArray &d) const { + + // Check for matching number of levels + if(Nplanes != d.Nplanes) return false; + + for(int i=0; i radial_energy(const DataPlane &dp) { + + // Check for empty input + if(dp.is_empty()) { + mlog << Error << "\nradial_energy() -> " + << "empty input!\n\n"; + exit(1); + } + + // Define the number of bins as the smaller dimension + int nx = dp.nx(); + int ny = dp.ny(); + int n_bins = min(nx, ny); + vector re(n_bins, 0.0); + + // Maximum euclidean distance from (0,0) + double max_dist = sqrt((nx-1)*(nx-1) + (ny-1)*(ny-1)); + + // Accumulate the radial energy for each bin + for(int x=0; x radial_energy(const DataPlane &); + //////////////////////////////////////////////////////////////////////// #endif // __DATA_PLANE_UTIL__ diff --git a/src/libcode/vx_data2d/data2d_utils.cc b/src/libcode/vx_data2d/data2d_utils.cc index 69992e4955..efecb12efa 100644 --- a/src/libcode/vx_data2d/data2d_utils.cc +++ b/src/libcode/vx_data2d/data2d_utils.cc @@ -24,8 +24,21 @@ using namespace std; //////////////////////////////////////////////////////////////////////// -bool build_grid_by_grid_string(const char *grid_str, Grid &grid, - const char *caller_name, bool do_warning) { +static bool derive_wind_speed_and_direction( + const DataPlane &u2d, const DataPlane &v2d, + bool want_wspd, + DataPlane &dp); + +static bool derive_u_and_v_wind( + const DataPlane &spd, const DataPlane &dir, + bool want_uwnd, + DataPlane &dp); + +//////////////////////////////////////////////////////////////////////// + +bool build_grid_by_grid_string( + const char *grid_str, Grid &grid, + const char *caller_name, bool do_warning) { bool status = false; if (nullptr != grid_str && m_strlen(grid_str) > 0) { @@ -58,8 +71,9 @@ bool build_grid_by_grid_string(const char *grid_str, Grid &grid, //////////////////////////////////////////////////////////////////////// -bool build_grid_by_grid_string(const ConcatString &grid_str, Grid &grid, - const char *caller_name, bool do_warning) { +bool build_grid_by_grid_string( + const ConcatString &grid_str, Grid &grid, + const char *caller_name, bool do_warning) { bool status = false; if(grid_str.nonempty()) { @@ -72,38 +86,59 @@ bool build_grid_by_grid_string(const ConcatString &grid_str, Grid &grid, //////////////////////////////////////////////////////////////////////// -bool derive_wdir(const DataPlane &u2d, const DataPlane &v2d, - DataPlane &wdir2d) { - const int nx = u2d.nx(); - const int ny = u2d.ny(); +bool derive_wind_speed( + const DataPlane &uwnd, const DataPlane &vwnd, + DataPlane &wspd) { + + mlog << Debug(3) + << "Deriving wind speed from U and V wind components.\n"; + + return derive_wind_speed_and_direction(uwnd, vwnd, true, wspd); +} + +//////////////////////////////////////////////////////////////////////// + +bool derive_wind_direction( + const DataPlane &uwnd, const DataPlane &vwnd, + DataPlane &wdir) { mlog << Debug(3) << "Deriving wind direction from U and V wind components.\n"; + return derive_wind_speed_and_direction(uwnd, vwnd, false, wdir); +} + +//////////////////////////////////////////////////////////////////////// + +static bool derive_wind_speed_and_direction( + const DataPlane &uwnd, const DataPlane &vwnd, + bool want_wspd, + DataPlane &dp) { + // // Check that the dimensions match // - if(u2d.nx() != v2d.nx() || u2d.ny() != v2d.ny()) { - mlog << Warning << "\nderive_wdir() -> " + if(uwnd.nx() != vwnd.nx() || uwnd.ny() != vwnd.ny()) { + mlog << Warning << "\nderive_wind_speed_and_direction() -> " << "the dimensions for U and V do not match: (" - << u2d.nx() << ", " << u2d.ny() << ") != (" - << v2d.nx() << ", " << v2d.ny() << ")\n\n"; + << uwnd.nx() << ", " << uwnd.ny() << ") != (" + << vwnd.nx() << ", " << vwnd.ny() << ")\n\n"; return false; } // - // Initialize by setting to u2d + // Initialize output // - wdir2d = u2d; - wdir2d.set_constant(bad_data_double); + dp = uwnd; + dp.set_constant(bad_data_double); + + const int nx = uwnd.nx(); + const int ny = uwnd.ny(); #pragma omp parallel default(shared) \ - shared(u2d, v2d, wdir2d) + shared(uwnd, vwnd, dp) { - // - // Compute the wind direction - // #pragma omp for schedule(static) \ collapse(2) for(int x=0; x " + if(uwnd.nx() != vwnd.nx() || uwnd.ny() != vwnd.ny()) { + mlog << Warning << "\nderive_kinetic_energy() -> " << "the dimensions for U and V do not match: (" - << u2d.nx() << ", " << u2d.ny() << ") != (" - << v2d.nx() << ", " << v2d.ny() << ")\n\n"; + << uwnd.nx() << ", " << uwnd.ny() << ") != (" + << vwnd.nx() << ", " << vwnd.ny() << ")\n\n"; return false; } // - // Initialize by setting to u2d + // Initialize output // - wind2d = u2d; - wind2d.set_constant(bad_data_double); + keng = uwnd; + keng.set_constant(bad_data_double); + + const int nx = uwnd.nx(); + const int ny = uwnd.ny(); #pragma omp parallel default(shared) \ - shared(u2d, v2d, wind2d) + shared(uwnd, vwnd, keng) { - // - // Compute the wind direction - // #pragma omp for schedule(static) \ collapse(2) for(int x=0; x " + << "the dimensions for wind speed and direction do not match: (" + << wspd.nx() << ", " << wspd.ny() << ") != (" + << wdir.nx() << ", " << wdir.ny() << ")\n\n"; + return false; + } + + // + // Initialize output + // + dp = wspd; + dp.set_constant(bad_data_double); + + const int nx = wspd.nx(); + const int ny = wspd.ny(); + +#pragma omp parallel default(shared) \ + shared(wspd, wdir, dp) + { + +#pragma omp for schedule(static) \ + collapse(2) + for(int x=0; x " - << "When resetting the grid definition to \"" - << grid.serialize() << "\", the grid dimensions " - << "are changed (" << grid.nx() << ", " << grid.ny() - << ") != (" << raw_nx() << ", " << raw_ny() << ").\n\n"; - } - else if ( raw_nx() != grid.nx() || raw_ny() != grid.ny() ) { + if ( raw_nx() > 0 && raw_ny() > 0 && + ( raw_nx() != grid.nx() || raw_ny() != grid.ny() ) ) { mlog << Error << "\nMet2dDataFile::set_grid() -> " << "When resetting the grid definition to \"" << grid.serialize() << "\", the grid dimensions " - << "cannot change to (" << grid.nx() << ", " << grid.ny() - << ") from (" << raw_nx() << ", " << raw_ny() << ").\n\n"; + << "cannot change from (" << raw_nx() << ", " << raw_ny() + << ") to (" << grid.nx() << ", " << grid.ny() << ").\n\n"; exit ( 1 ); @@ -359,6 +360,599 @@ return n_valid; } +//////////////////////////////////////////////////////////////////////// + +bool Met2dDataFile::derive_winds(VarInfo *vinfo, DataPlane &dp) + +{ + +if(!vinfo) return false; + +bool status = false; + + // + // Derive wind fields from U and V + // + +if(vinfo->need_uv_wind()) { + + // Create local copies + auto vinfo_uwnd = vinfo->clone(); + auto vinfo_vwnd = vinfo->clone(); + + DataPlane uwnd_dp; + DataPlane vwnd_dp; + status = read_wind_data(vinfo_uwnd.get(), + conf_key_u_wind_field_name, + vinfo->wind_info().u_wind, + uwnd_dp) && + read_wind_data(vinfo_vwnd.get(), + conf_key_v_wind_field_name, + vinfo->wind_info().v_wind, + vwnd_dp); + + if(status) { + + // Rotate U/V winds, if needed + if(vinfo->is_wind_rotation() && + vinfo_uwnd->need_rotation() && + vinfo_vwnd->need_rotation()) { + DataPlane uwnd_dp_orig(uwnd_dp); + DataPlane vwnd_dp_orig(vwnd_dp); + mlog << Debug(3) << "Rotating U and V wind fields " + << "from grid-relative to earth-relative.\n"; + rotate_uv_grid_to_earth(uwnd_dp_orig, vwnd_dp_orig, + *Raw_Grid, uwnd_dp, vwnd_dp); + } + + // Derive wind speed + if(vinfo->is_wind_speed()) { + status = derive_wind_speed(uwnd_dp, vwnd_dp, dp); + vinfo->set_long_name("Wind Speed"); + vinfo->set_units(vinfo_uwnd->units()); + } + // Derive wind direction + else if(vinfo->is_wind_direction()) { + status = derive_wind_direction(uwnd_dp, vwnd_dp, dp); + vinfo->set_earth_relative(); + vinfo->set_long_name("Wind Direction"); + vinfo->set_units("deg"); + } + // Derive kinetic energy + else if(vinfo->is_kinetic_energy()) { + status = derive_kinetic_energy(uwnd_dp, vwnd_dp, dp); + vinfo->set_long_name("Kinetic Energy"); + vinfo->set_units("J/kg"); + } + } +} + + // + // Derive U and V from wind speed and direction + // + +else if(vinfo->is_u_wind() || vinfo->is_v_wind()) { + + // Create local copies + auto vinfo_wspd = vinfo->clone(); + auto vinfo_wdir = vinfo->clone(); + + DataPlane wspd_dp; + DataPlane wdir_dp; + status = read_wind_data(vinfo_wspd.get(), + conf_key_wind_speed_field_name, + vinfo->wind_info().wind_speed, + wspd_dp) && + read_wind_data(vinfo_wdir.get(), + conf_key_wind_direction_field_name, + vinfo->wind_info().wind_direction, + wdir_dp); + + // Rotate wind direction, if needed + if(vinfo_wdir->need_rotation()) { + DataPlane wdir_dp_orig(wdir_dp); + mlog << Debug(3) << "Rotating wind direction field " + << "from grid-relative to earth-relative.\n"; + rotate_wind_direction_grid_to_earth(wdir_dp_orig, + *Raw_Grid, wdir_dp); + vinfo->set_earth_relative(); + } + + if(status) { + if(vinfo->is_u_wind()) { + status = derive_u_wind(wspd_dp, wdir_dp, dp); + vinfo->set_long_name("U-Component of Wind"); + vinfo->set_units(vinfo_wspd->units()); + } + else { + status = derive_v_wind(wspd_dp, wdir_dp, dp); + vinfo->set_long_name("V-Component of Wind"); + vinfo->set_units(vinfo_wspd->units()); + } + } +} + +return status; + +} + + +//////////////////////////////////////////////////////////////////////// + + +bool Met2dDataFile::derive_winds(VarInfo *vinfo, DataPlaneArray &dpa) + +{ + +static const char *method_name = "Met2dDataFile::derive_winds() -> "; + +if(!vinfo) return false; + +bool status = false; + + // + // Derive wind fields from U and V + // + +if(vinfo->need_uv_wind()) { + + // Create local copies + auto vinfo_uwnd = vinfo->clone(); + auto vinfo_vwnd = vinfo->clone(); + + DataPlaneArray uwnd_dpa; + DataPlaneArray vwnd_dpa; + status = read_wind_data(vinfo_uwnd.get(), + conf_key_u_wind_field_name, + vinfo->wind_info().u_wind, + uwnd_dpa) && + read_wind_data(vinfo_vwnd.get(), + conf_key_v_wind_field_name, + vinfo->wind_info().v_wind, + vwnd_dpa); + + if(!status) return status; + + // Rotate U/V winds, if needed + if(vinfo->is_wind_rotation() && + vinfo_uwnd->need_rotation() && + vinfo_vwnd->need_rotation()) { + mlog << Debug(3) << "Rotating " << uwnd_dpa.n_planes() + << " U and V wind fields from grid-relative " + << "to earth-relative.\n"; + for(int i=0; iis_wind_speed()) { + vinfo->set_long_name("Wind Speed"); + vinfo->set_units(vinfo_uwnd->units()); + } + else if(vinfo->is_wind_direction()) { + vinfo->set_earth_relative(); + vinfo->set_long_name("Wind Direction"); + vinfo->set_units("deg"); + } + else if(vinfo->is_kinetic_energy()) { + vinfo->set_long_name("Kinetic Energy"); + vinfo->set_units("J/kg"); + } + + // Check for matching dimensions + if(uwnd_dpa.n_planes() != vwnd_dpa.n_planes()) { + mlog << Warning << "\n" << method_name + << "when deriving winds, the number of U-wind records (" + << uwnd_dpa.n_planes() + << ") does not match the number of V-wind records (" + << vwnd_dpa.n_planes() + << ") for file '" << filename() << "'\n\n"; + return false; + } + + // + // Loop through each of the data planes + // + for(int i=0; iis_wind_speed()) { + status = derive_wind_speed(uwnd_dpa[i], vwnd_dpa[i], dp); + } + // Derive wind direction + else if(vinfo->is_wind_direction()) { + status = derive_wind_direction(uwnd_dpa[i], vwnd_dpa[i], dp); + } + // Derive kinetic energy + else if(vinfo->is_kinetic_energy()) { + status = derive_kinetic_energy(uwnd_dpa[i], vwnd_dpa[i], dp); + } + + // Store the result + dpa.add(dp, uwnd_dpa.lower(i), uwnd_dpa.upper(i)); + } +} + + // + // Derive U and V from wind speed and direction + // + +else if(vinfo->is_u_wind() || vinfo->is_v_wind()) { + + // Create local copies + auto vinfo_wspd = vinfo->clone(); + auto vinfo_wdir = vinfo->clone(); + + DataPlaneArray wspd_dpa; + DataPlaneArray wdir_dpa; + status = read_wind_data(vinfo_wspd.get(), + conf_key_wind_speed_field_name, + vinfo->wind_info().wind_speed, + wspd_dpa) && + read_wind_data(vinfo_wdir.get(), + conf_key_wind_direction_field_name, + vinfo->wind_info().wind_direction, + wdir_dpa); + + if(!status) return status; + + // Rotate wind direction, if needed + if(vinfo_wdir->need_rotation()) { + mlog << Debug(3) << "Rotating " << wdir_dpa.n_planes() + << " wind direction field(s) from grid-relative " + << "to earth-relative.\n"; + for(int i=0; iset_earth_relative(); + } + + // Store the long name and units + if(vinfo->is_u_wind()) { + vinfo->set_long_name("U-Component of Wind"); + vinfo->set_units(vinfo_wspd->units()); + } + else { + vinfo->set_long_name("V-Component of Wind"); + vinfo->set_units(vinfo_wspd->units()); + } + + if(wspd_dpa.n_planes() != wdir_dpa.n_planes()) { + mlog << Warning << "\n" << method_name + << "when deriving winds, the number of wind speed records (" + << wspd_dpa.n_planes() + << ") does not match the number of wind direction records (" + << wdir_dpa.n_planes() + << ") for file '" << filename() << "'\n\n"; + return false; + } + + // + // Loop through each of the data planes + // + for(int i=0; iis_u_wind()) { + status = derive_u_wind(wspd_dpa[i], wdir_dpa[i], dp); + } + else { + status = derive_v_wind(wspd_dpa[i], wdir_dpa[i], dp); + } + + // Store the result + dpa.add(dp, wspd_dpa.lower(i), wspd_dpa.upper(i)); + } +} + +return status; + +} + + +//////////////////////////////////////////////////////////////////////// + + +bool Met2dDataFile::rotate_winds(const VarInfo *vinfo, DataPlane &dp) + +{ + +static const char *method_name = "Met2dDataFile::rotate_winds(DataPlane) -> "; + +if(!vinfo) return false; + +if(!vinfo->is_wind_rotation()) return true; + +if(vinfo->is_grid_relative()) { + mlog << Debug(3) << "Rotating wind field \"" + << vinfo->magic_str() + << "\" from grid-relative to earth-relative.\n"; +} +else { + mlog << Debug(3) << "Identified wind field \"" + << vinfo->magic_str() + << "\" as being earth-relative.\n"; + return true; +} + +bool status = false; + +// Create local copy +auto vinfo_wind = vinfo->clone(); + +DataPlane uwnd_dp; +DataPlane vwnd_dp; +DataPlane tmp_dp; + +// Rotate U-Wind +if(vinfo->is_u_wind()) { + uwnd_dp = dp; + status = read_wind_data(vinfo_wind.get(), + conf_key_v_wind_field_name, + vinfo->wind_info().v_wind, + vwnd_dp); + if(status) status = rotate_uv_grid_to_earth(uwnd_dp, vwnd_dp, + *Raw_Grid, + dp, tmp_dp); +} +// Rotate V-Wind +else if(vinfo->is_v_wind()) { + vwnd_dp = dp; + status = read_wind_data(vinfo_wind.get(), + conf_key_u_wind_field_name, + vinfo->wind_info().u_wind, + uwnd_dp); + if(status) status = rotate_uv_grid_to_earth(uwnd_dp, vwnd_dp, + *Raw_Grid, + tmp_dp, dp); +} +// Rotate Wind Direction +else if(vinfo->is_wind_direction()) { + tmp_dp = dp; + rotate_wind_direction_grid_to_earth(tmp_dp, *Raw_Grid, dp); + status = true; +} + +if(!status) { + mlog << Warning << "\n" << method_name + << "Trouble rotating wind field (" << vinfo->magic_str() + << ") from grid to earth relative.\n\n"; +} + +return status; + +} + + +//////////////////////////////////////////////////////////////////////// + + +bool Met2dDataFile::rotate_winds(const VarInfo *vinfo, DataPlaneArray &dpa) + +{ + +static const char *method_name = "Met2dDataFile::rotate_winds(DataPlaneArray) -> "; + +if(!vinfo) return false; + +if(!vinfo->is_wind_rotation() || dpa.n_planes() == 0) return true; + +if(vinfo->is_grid_relative()) { + mlog << Debug(3) << "Rotating " << dpa.n_planes() + << " wind field(s) for \"" << vinfo->magic_str() + << "\" from grid-relative to earth-relative.\n"; +} +else { + mlog << Debug(3) << "Identified " << dpa.n_planes() + << " wind field(s) for \"" << vinfo->magic_str() + << "\" as being earth-relative.\n"; + return true; +} + +bool status = false; + +// Create local copy +auto vinfo_wind = vinfo->clone(); + +DataPlaneArray uwnd_dpa; +DataPlaneArray vwnd_dpa; +DataPlaneArray tmp_dpa(dpa); +DataPlaneArray *uwnd_out; +DataPlaneArray *vwnd_out; + +// Rotate U-Wind and V-Wind +if(vinfo->is_u_wind() || vinfo->is_v_wind()) { + + if(vinfo->is_u_wind()) { + uwnd_dpa = dpa; + uwnd_out = &dpa; + vwnd_out = &tmp_dpa; + status = read_wind_data(vinfo_wind.get(), + conf_key_v_wind_field_name, + vinfo->wind_info().v_wind, + vwnd_dpa); + } + else { + vwnd_dpa = dpa; + uwnd_out = &tmp_dpa; + vwnd_out = &dpa; + status = read_wind_data(vinfo_wind.get(), + conf_key_u_wind_field_name, + vinfo->wind_info().u_wind, + uwnd_dpa); + } + + // Check for matching levels + if(!uwnd_dpa.levels_match(vwnd_dpa)) { + mlog << Warning << "\n" << method_name + << "The U-wind and V-wind levels do not match.\n\n"; + status = false; + } + + // Rotate each plane + if(status) { + for(int i=0; iat(i), vwnd_out->at(i)); + if(!status) break; + } + } +} +// Rotate Wind Direction +else if(vinfo->is_wind_direction()) { + tmp_dpa = dpa; + + // Rotate each plane + for(int i=0; imagic_str() + << ") from grid to earth relative.\n\n"; +} + +return status; + +} + + +//////////////////////////////////////////////////////////////////////// + + +bool Met2dDataFile::read_wind_data(VarInfo *vinfo, + const char *conf_key, + const StringArray &names, + DataPlane &dp) + +{ + +static const char *method_name = "Met2dDataFile::read_wind_data(DataPlane) -> "; + +if(!vinfo) return false; + +bool status = false; + + // Update search names, if needed + +StringArray search_names(swap_uv_name(vinfo, conf_key, names)); + + // Try each of the possible names + +for(int i=0; ireset_dict_with_name(search_names[i].c_str()) && + data_plane(*vinfo, dp, false)) { + status = true; + mlog << Debug(3) << "Found matching wind field \"" + << vinfo->magic_str() << "\".\n"; + break; + } +} + +if(!status) { + mlog << Warning << "\n" << method_name + << "No matching wind field found for name(s) \"" + << write_css(search_names) << "\".\n" + << "Set \"" << conf_key + << "\" to specify the matching variable name.\n\n"; +} + +return status; + +} + + +//////////////////////////////////////////////////////////////////////// + + +bool Met2dDataFile::read_wind_data(VarInfo *vinfo, + const char *conf_key, + const StringArray &names, + DataPlaneArray &dpa) + +{ + +static const char *method_name = "Met2dDataFile::read_wind_data(DataPlaneArray) -> "; + +if(!vinfo) return false; + +bool status = false; + + // Update search names, if needed + +StringArray search_names(swap_uv_name(vinfo, conf_key, names)); + + // Try each of the possible names + +for(int i=0; ireset_dict_with_name(search_names[i].c_str()) && + data_plane_array(*vinfo, dpa, false)) { + status = true; + mlog << Debug(3) << "Found matching wind field(s) \"" + << vinfo->magic_str() << "\".\n"; + break; + } +} + +if(!status) { + mlog << Warning << "\n" << method_name + << "No matching wind field(s) found for name(s) \"" + << write_css(search_names) << "\".\n" + << "Set \"" << conf_key + << "\" to specify the matching variable name.\n\n"; +} + +return status; + +} + + //////////////////////////////////////////////////////////////////////// @@ -455,4 +1049,47 @@ return true; } +//////////////////////////////////////////////////////////////////////// + + +static StringArray swap_uv_name(const VarInfo *vinfo, + const ConcatString &conf_key, + const StringArray &names) +{ + + // Try swapping U and V in the VarInfo name when: + // - Converting between U-wind and V-wind + // - The default setting has not be modified + // - The substitution produces an actual change + + // Lookup default setting from ConfigConstants + StringArray default_names; + MetConfig conf_const(replace_path(config_const_filename).c_str()); + default_names.parse_css(conf_const.lookup_string(conf_key.c_str())); + + StringArray sa(names); + ConcatString cs(vinfo->name()); + + // Replace U with V + if(vinfo->is_u_wind() && + conf_key == conf_key_v_wind_field_name && + names == default_names) { + cs.replace("U", "V", false); + cs.replace("u", "v", false); + if(cs != vinfo->name()) sa.insert(0, cs.c_str()); + } + // Replace V with U + else if(vinfo->is_v_wind() && + conf_key == conf_key_u_wind_field_name && + names == default_names) { + cs.replace("V", "U", false); + cs.replace("v", "u", false); + if(cs != vinfo->name()) sa.insert(0, cs.c_str()); + } + + return sa; + +} + + //////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_data2d/data_class.h b/src/libcode/vx_data2d/data_class.h index 1b8cd6493d..de36548069 100644 --- a/src/libcode/vx_data2d/data_class.h +++ b/src/libcode/vx_data2d/data_class.h @@ -83,6 +83,13 @@ class Met2dDataFile : public Met2dData { void mtddf_init_from_scratch(); + bool read_wind_data(VarInfo *, + const char *, const StringArray &, + DataPlane &); + bool read_wind_data(VarInfo *, + const char *, const StringArray &, + DataPlaneArray &); + bool GridShifted; protected: @@ -141,11 +148,11 @@ class Met2dDataFile : public Met2dData { // retrieve the first matching data plane - virtual bool data_plane(VarInfo &, DataPlane &) = 0; + virtual bool data_plane(VarInfo &, DataPlane &, bool do_winds = true) = 0; // retrieve all matching data planes - virtual int data_plane_array(VarInfo &, DataPlaneArray &) = 0; + virtual int data_plane_array(VarInfo &, DataPlaneArray &, bool do_winds = true) = 0; // retrieve the indexes of the first matching data plane @@ -156,6 +163,13 @@ class Met2dDataFile : public Met2dData { int data_planes(std::vector&, std::vector&); + // derive and rotate wind fields + + bool derive_winds(VarInfo *, DataPlane &); + bool derive_winds(VarInfo *, DataPlaneArray &); + bool rotate_winds(const VarInfo *, DataPlane &); + bool rotate_winds(const VarInfo *, DataPlaneArray &); + // post-process data after reading it bool process_data_plane(VarInfo *, DataPlane &); diff --git a/src/libcode/vx_data2d/table_lookup.cc b/src/libcode/vx_data2d/table_lookup.cc index b51bb40610..1a87c4cd0a 100644 --- a/src/libcode/vx_data2d/table_lookup.cc +++ b/src/libcode/vx_data2d/table_lookup.cc @@ -333,7 +333,7 @@ void Grib2TableEntry::clear() { -index_a = index_b = index_c = mtab_set = mtab_low = mtab_high = cntr = ltab = -1; +disc = pcat = pnum = mtab_set = mtab_low = mtab_high = cntr = ltab = -1; parm_name.clear(); @@ -355,9 +355,9 @@ void Grib2TableEntry::assign(const Grib2TableEntry & e) clear(); -index_a = e.index_a; -index_b = e.index_b; -index_c = e.index_c; +disc = e.disc; +pcat = e.pcat; +pnum = e.pnum; mtab_high = e.mtab_high; mtab_low = e.mtab_low; mtab_set = e.mtab_set; @@ -385,14 +385,14 @@ void Grib2TableEntry::dump(ostream & out, int depth) const Indent prefix(depth); out << prefix << "Index values = (" - << index_a << ", " + << disc << ", " << mtab_set << ", " << mtab_low << ", " << mtab_high << ", " << cntr << ", " << ltab << ", " - << index_b << ", " - << index_c << ")\n"; + << pcat << ", " + << pnum << ")\n"; out << prefix << "parm_name = " << parm_name.contents() << "\n"; @@ -410,9 +410,9 @@ return; bool Grib2TableEntry::is_eq(const Grib2TableEntry &e) const { - return (index_a == e.index_a) && - (index_b == e.index_b) && - (index_c == e.index_c) && + return (disc == e.disc) && + (pcat == e.pcat) && + (pnum == e.pnum) && (parm_name == e.parm_name) && (mtab_set == e.mtab_set) && (mtab_low == e.mtab_low) && @@ -468,14 +468,14 @@ for (int j=0; j<8; ++j) { if(!is_number(tok[j].c_str())) return false; } -index_a = atoi(tok[0].c_str()); +disc = atoi(tok[0].c_str()); mtab_set = atoi(tok[1].c_str()); mtab_low = atoi(tok[2].c_str()); mtab_high = atoi(tok[3].c_str()); cntr = atoi(tok[4].c_str()); ltab = atoi(tok[5].c_str()); -index_b = atoi(tok[6].c_str()); -index_c = atoi(tok[7].c_str()); +pcat = atoi(tok[6].c_str()); +pnum = atoi(tok[7].c_str()); // // grab the 3 strings separated by double quotes @@ -807,19 +807,20 @@ return status; //////////////////////////////////////////////////////////////////////// + ConcatString TableFlatFile::log_arguments(const char * parm_name, - int a, int b, int c, + int disc, int pcat, int pnum, int mtab, int cntr, int ltab) const { ConcatString msg; msg << "parm_name = " << parm_name; -if( bad_data_int != a ) msg << ", index_a = " << a; +if( bad_data_int != disc ) msg << ", disc = " << disc; if( bad_data_int != mtab ) msg << ", grib2_mtab = " << mtab; if( bad_data_int != cntr ) msg << ", grib2_cntr = " << cntr; if( bad_data_int != ltab ) msg << ", grib2_ltab = " << ltab; -if( bad_data_int != b ) msg << ", index_b = " << b; -if( bad_data_int != c ) msg << ", index_c = " << c; +if( bad_data_int != pcat ) msg << ", pcat = " << pcat; +if( bad_data_int != pnum ) msg << ", pnum = " << pnum; return msg; @@ -1043,435 +1044,211 @@ return true; } - //////////////////////////////////////////////////////////////////////// +int TableFlatFile::lookup_grib1(int code, int table_number, + vector &matches) const { + matches.clear(); -bool TableFlatFile::lookup_grib1(int code, int table_number, Grib1TableEntry & e) - -{ - -e.clear(); - -for (int j=0; j &matches) const { + matches.clear(); + + for(const auto &e : g1e) { + if((e.code == code ) && + (e.table_number == table_number) && + (e.center == center ) && + (e.subcenter == -1 || + e.subcenter == subcenter)) { + matches.emplace_back(e); } } - return false; -} + return (int) matches.size(); +} //////////////////////////////////////////////////////////////////////// +int TableFlatFile::lookup_grib1(int code, + vector &matches) const { -bool TableFlatFile::lookup_grib1(int code, Grib1TableEntry & e) // assumes table_number = 2; - -{ - -const int table_number = 2; -bool status = false; - -status = lookup_grib1(code, table_number, e); - -return status; + // Assume default table_number = 2 + return lookup_grib1(code, 2, matches); } - //////////////////////////////////////////////////////////////////////// +int TableFlatFile::lookup_grib1(const char * parm_name, int table_number, int code, + vector &matches) const { + matches.clear(); -bool TableFlatFile::lookup_grib1(const char * parm_name, int table_number, int code, - Grib1TableEntry & e, int & n_matches) - -{ - - // clear the by-reference arguments - e.clear(); - n_matches = 0; - - // build a list of matches - vector matches; - for(int j=0; j < N_grib1_elements; j++){ - - if( g1e[j].parm_name != parm_name || - (bad_data_int != table_number && g1e[j].table_number != table_number) || - (bad_data_int != code && g1e[j].code != code) ) - continue; - - if( n_matches++ == 0 ) e = g1e[j]; - matches.emplace_back( g1e[j] ); - - } - - // if there are multiple matches, print a descriptive message - if( 1 < n_matches ){ - - ConcatString msg; - msg << "Multiple GRIB1 table entries match lookup criteria (" - << "parm_name = " << parm_name; - if( bad_data_int != table_number ) msg << ", table_number = " << table_number; - if( bad_data_int != code ) msg << ", code = " << code; - msg << "):\n"; - mlog << Debug(3) << "\n" << msg; - - for(auto it = matches.begin(); it < matches.end(); it++) - mlog << Debug(3) << " parm_name: " << it->parm_name - << ", table_number = " << it->table_number - << ", code = " << it->code << "\n"; - - mlog << Debug(3) << "Using the first match found: " - << " parm_name: " << e.parm_name - << ", table_number = " << e.table_number - << ", code = " << e.code << "\n\n"; - + for(const auto &e : g1e) { + if((e.parm_name == parm_name ) && + (is_bad_data(table_number) || e.table_number == table_number) && + (is_bad_data(code) || e.code == code )) { + matches.emplace_back(e); + } } - return (n_matches > 0); - + return (int) matches.size(); } - //////////////////////////////////////////////////////////////////////// - -bool TableFlatFile::lookup_grib1(const char * parm_name, int table_number, int code,int center, int subcenter, - Grib1TableEntry & e, int & n_matches) - -{ - - // clear the by-reference arguments - e.clear(); - n_matches = 0; - - // build a list of matches - vector matches; - int matching_subsenter; - for(int j=0; j < N_grib1_elements; j++){ - matching_subsenter = subcenter; - if( g1e[j].subcenter == -1){ - matching_subsenter = -1; - } - - if( g1e[j].parm_name != parm_name || - (bad_data_int != table_number && g1e[j].table_number != table_number) || - (bad_data_int != code && g1e[j].code != code) || - (bad_data_int != center && g1e[j].center != center) || - (bad_data_int != matching_subsenter && g1e[j].subcenter != matching_subsenter) ) - continue; - - if( n_matches++ == 0 ) e = g1e[j]; - matches.emplace_back( g1e[j] ); - - } - - // if there are multiple matches, print a descriptive message - if( 1 < n_matches ){ - - ConcatString msg; - msg << "Multiple GRIB1 table entries match lookup criteria (" - << "parm_name = " << parm_name; - if( bad_data_int != table_number ) msg << ", table_number = " << table_number; - if( bad_data_int != code ) msg << ", code = " << code; - msg << "):\n"; - mlog << Debug(3) << "\n" << msg; - - for(auto it = matches.begin(); it < matches.end(); it++) - { - mlog << Debug(3) << " parm_name: " << it->parm_name - << ", table_number = " << it->table_number - << ", code = " << it->code - << ", center = " << it->center - << ", subcenter = " << it->subcenter << "\n"; +int TableFlatFile::lookup_grib1(const char *parm_name, int table_number, + int code,int center, int subcenter, + vector &matches) const { + matches.clear(); + + for(const auto &e : g1e) { + if((e.parm_name == parm_name ) && + (is_bad_data(table_number) || e.table_number == table_number) && + (is_bad_data(code) || e.code == code ) && + (is_bad_data(center) || e.center == center ) && + (e.subcenter == -1 || + is_bad_data(subcenter) || e.subcenter == subcenter )) { + matches.emplace_back(e); } - - mlog << Debug(3) << "Using the first match found: " - << " parm_name: " << e.parm_name - << ", table_number = " << e.table_number - << ", code = " << e.code << "\n\n"; - } - return (n_matches > 0); - + return (int) matches.size(); } - //////////////////////////////////////////////////////////////////////// +int TableFlatFile::lookup_grib1(const char * parm_name, + vector &matches) const { -bool TableFlatFile::lookup_grib1(const char * parm_name, Grib1TableEntry & e) // assumes table number is 2 - -{ - - int n_matches = -1; - return lookup_grib1(parm_name, 2, bad_data_int, e, n_matches); - + // Assume default table_number = 2 + return lookup_grib1(parm_name, 2, bad_data_int, matches); } - //////////////////////////////////////////////////////////////////////// +int TableFlatFile::lookup_grib2(int disc, int pcat, int pnum, + vector &matches) const { + matches.clear(); -bool TableFlatFile::lookup_grib2(int a, int b, int c, Grib2TableEntry & e) - -{ - -e.clear(); - -for (int j=0; j &matches) const { + vector exact_matches; + vector partial_matches; -bool TableFlatFile::lookup_grib2(int a, int b, int c, - int mtab, int cntr, int ltab, - Grib2TableEntry & e) - -{ - - bool has_def_entry = false; - Grib2TableEntry e_def_match; + for(const auto &e : g2e) { - e.clear(); + // Check discipline, parm_cat, and cat + if((e.disc != disc) || + (e.pcat != pcat) || + (e.pnum != pnum)) continue; - for (int j=0; j &matches) const { + matches.clear(); -bool TableFlatFile::lookup_grib2(const char * parm_name, int a, int b, int c, - Grib2TableEntry & e, int & n_matches) - -{ + for(const auto &e : g2e) { - // clear the by-reference arguments - e.clear(); - n_matches = 0; - - // build a list of matches - vector matches; - for(int j=0; jparm_name - << ", index_a = " << it->index_a - << ", index_b = " << it->index_b - << ", index_c = " << it->index_c << "\n"; - - mlog << Debug(3) << "Using the first match found: " - << " parm_name: " << e.parm_name - << ", index_a = " << e.index_a - << ", index_b = " << e.index_b - << ", index_c = " << e.index_c << "\n\n"; - - } - - return (n_matches > 0); - + return (int) matches.size(); } - //////////////////////////////////////////////////////////////////////// +int TableFlatFile::lookup_grib2(const char * parm_name, + int disc, int pcat, int pnum, + int mtab, int cntr, int ltab, + vector &matches) const { + vector exact_matches; + vector partial_matches; -bool TableFlatFile::lookup_grib2(const char * parm_name, - int a, int b, int c, - int mtab, int cntr, int ltab, - Grib2TableEntry & e, int & n_matches) + for(const auto &e : g2e) { -{ - // clear the by-reference arguments - e.clear(); - n_matches = 0; + if((e.parm_name == parm_name ) && + (is_bad_data(disc) || e.disc == disc) && + (is_bad_data(pcat) || e.pcat == pcat) && + (is_bad_data(pnum) || e.pnum == pnum)) { - // build a list of matches - vector matches; - vector partial_matches; - for(int j=0; jparm_name - << ", index_a = " << it->index_a - << ", grib2_mtab = " << it->mtab_set - << ", grib2_cntr = " << it->cntr - << ", grib2_ltab = " << it->ltab - << ", index_b = " << it->index_b - << ", index_c = " << it->index_c - << "\n"; - - mlog << Debug(3) << "Using the first match found: " - << " parm_name: " << e.parm_name - << ", index_a = " << e.index_a - << ", grib2_mtab = " << e.mtab_set - << ", grib2_cntr = " << e.cntr - << ", grib2_ltab = " << e.ltab - << ", index_b = " << e.index_b - << ", index_c = " << e.index_c - << "\n\n"; - - } - else if( 0 == n_matches ){ - mlog << Debug(3) << "No match, lookup criteria (" - << log_arguments(parm_name, a, b, c, mtab, cntr, ltab) - << ")\n"; - } + // Prefer exact matches + if(!exact_matches.empty()) matches = exact_matches; + else matches = partial_matches; - return (n_matches > 0); + return (int) matches.size(); } - //////////////////////////////////////////////////////////////////////// - -bool TableFlatFile::lookup_grib2(const char * parm_name, Grib2TableEntry & e, int & n_matches) - -{ - - return lookup_grib2(parm_name, bad_data_int, bad_data_int, bad_data_int, e, n_matches); - +int TableFlatFile::lookup_grib2(const char * parm_name, + vector &matches) const { + return lookup_grib2(parm_name, bad_data_int, bad_data_int, bad_data_int, + matches); } - //////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_data2d/table_lookup.h b/src/libcode/vx_data2d/table_lookup.h index ae330ee1eb..5d95002849 100644 --- a/src/libcode/vx_data2d/table_lookup.h +++ b/src/libcode/vx_data2d/table_lookup.h @@ -109,14 +109,14 @@ class Grib2TableEntry { bool is_eq(const Grib2TableEntry &) const; GribEntryMatch match(const int &mtab, const int &_cntr, const int &_ltab) const; - int index_a; // Section 0 Discipline + int disc; // Section 0 Discipline int mtab_set; // Section 1 Master Tables Version Number used by set_var int mtab_low; // Section 1 Master Tables Version Number low range of tables int mtab_high; // Section 1 Master Tables Version Number high range of tables - int cntr; // Section 1 originating centre, used for local tables + int cntr; // Section 1 originating center, used for local tables int ltab; // Section 1 Local Tables Version Number - int index_b; // Section 4 Template 4.0 Parameter category - int index_c; // Section 4 Template 4.0 Parameter number + int pcat; // Section 4 Template 4.0 Parameter category + int pnum; // Section 4 Template 4.0 Parameter number ConcatString parm_name; @@ -201,22 +201,32 @@ class TableFlatFile { bool read(const char * filename); - bool lookup_grib1(int code, int table_number, Grib1TableEntry &); - bool lookup_grib1(int code, int table_number, int center, int subcenter, Grib1TableEntry &); - - bool lookup_grib1(int code, Grib1TableEntry &); // assumes table_number is 2 - bool lookup_grib1(const char * parm_name, Grib1TableEntry &); // assumes table_number is 2 - - bool lookup_grib1(const char * parm_name, int table_number, int code, - Grib1TableEntry &, int & n_matches); - bool lookup_grib1(const char * parm_name, int table_number, int code,int center, int subcenter, - Grib1TableEntry &, int & n_matches); - - bool lookup_grib2(int a, int b, int c, Grib2TableEntry &); - bool lookup_grib2(int a, int b, int c, int mtab, int cntr, int ltab, Grib2TableEntry &); - bool lookup_grib2(const char * parm_name, Grib2TableEntry &, int & n_matches); - bool lookup_grib2(const char * parm_name, int a, int b, int c, Grib2TableEntry &, int & n_matches); - bool lookup_grib2(const char * parm_name, int a, int b, int c, int mtab, int cntr, int ltab, Grib2TableEntry &, int & n_matches); + int lookup_grib1(int code, int table_number, + std::vector &) const; + int lookup_grib1(int code, int table_number, int center, int subcenter, + std::vector &) const; + + // Assumes table_number = 2 by default + int lookup_grib1(int code, + std::vector &) const; + int lookup_grib1(const char * parm_name, + std::vector &) const; + + int lookup_grib1(const char * parm_name, int table_number, int code, + std::vector &) const; + int lookup_grib1(const char * parm_name, int table_number, int code,int center, int subcenter, + std::vector &) const; + + int lookup_grib2(int disc, int pcat, int pnum, + std::vector &) const; + int lookup_grib2(int disc, int pcat, int pnum, int mtab, int cntr, int ltab, + std::vector &) const; + int lookup_grib2(const char * parm_name, + std::vector &) const; + int lookup_grib2(const char * parm_name, int disc, int pcat, int pnum, + std::vector &) const; + int lookup_grib2(const char * parm_name, int disc, int pcat, int pnum, int mtab, int cntr, int ltab, + std::vector &) const; void readUserGribTables(const char * table_type); diff --git a/src/libcode/vx_data2d/var_info.cc b/src/libcode/vx_data2d/var_info.cc index 5e6aa8dd6a..e3fb37fd7d 100644 --- a/src/libcode/vx_data2d/var_info.cc +++ b/src/libcode/vx_data2d/var_info.cc @@ -87,6 +87,7 @@ void VarInfo::init_from_scratch() { void VarInfo::assign(const VarInfo &v) { // Copy + Dict = v.Dict; MagicStr = v.MagicStr; ReqName = v.ReqName; Name = v.Name; @@ -119,6 +120,9 @@ void VarInfo::assign(const VarInfo &v) { DefaultRegrid = v.DefaultRegrid; Regrid = v.Regrid; + GridRelativeFlag = v.GridRelativeFlag; + WindInfo = v.WindInfo; + SetAttrName = v.SetAttrName; SetAttrUnits = v.SetAttrUnits; SetAttrLevel = v.SetAttrLevel; @@ -138,6 +142,7 @@ void VarInfo::assign(const VarInfo &v) { SetAttrIsGridRelative = v.SetAttrIsGridRelative; SetAttrIsWindSpeed = v.SetAttrIsWindSpeed; SetAttrIsWindDirection = v.SetAttrIsWindDirection; + SetAttrIsKineticEnergy = v.SetAttrIsKineticEnergy; SetAttrIsProb = v.SetAttrIsProb; return; @@ -148,6 +153,7 @@ void VarInfo::assign(const VarInfo &v) { void VarInfo::clear() { // Initialize + Dict.clear(); MagicStr.clear(); ReqName.clear(); Name.clear(); @@ -180,6 +186,9 @@ void VarInfo::clear() { DefaultRegrid.clear(); Regrid.clear(); + GridRelativeFlag = false; + WindInfo.clear(); + SetAttrName.clear(); SetAttrUnits.clear(); SetAttrLevel.clear(); @@ -199,6 +208,7 @@ void VarInfo::clear() { SetAttrIsGridRelative = bad_data_int; SetAttrIsWindSpeed = bad_data_int; SetAttrIsWindDirection = bad_data_int; + SetAttrIsKineticEnergy = bad_data_int; SetAttrIsProb = bad_data_int; return; @@ -289,6 +299,13 @@ void VarInfo::set_req_name(const char *str) { /////////////////////////////////////////////////////////////////////////////// +void VarInfo::set_req_name(const string &str) { + set_req_name(str.c_str()); + return; +} + +/////////////////////////////////////////////////////////////////////////////// + void VarInfo::set_name(const char *str) { Name = str; return; @@ -296,8 +313,8 @@ void VarInfo::set_name(const char *str) { /////////////////////////////////////////////////////////////////////////////// -void VarInfo::set_name(const string str) { - Name = str; +void VarInfo::set_name(const string &str) { + set_name(str.c_str()); return; } @@ -324,6 +341,13 @@ void VarInfo::set_units(const char *str) { /////////////////////////////////////////////////////////////////////////////// +void VarInfo::set_units(const string &str) { + set_units(str.c_str()); + return; +} + +/////////////////////////////////////////////////////////////////////////////// + void VarInfo::set_level_info(const LevelInfo &l) { Level = l; return; @@ -457,6 +481,21 @@ void VarInfo::set_regrid(const RegridInfo &ri) { /////////////////////////////////////////////////////////////////////////////// +void VarInfo::set_grid_relative_flag(bool f) { + GridRelativeFlag = f; + return; +} + +/////////////////////////////////////////////////////////////////////////////// + +void VarInfo::set_earth_relative() { + GridRelativeFlag = false; + SetAttrIsGridRelative = 0; + return; +} + +/////////////////////////////////////////////////////////////////////////////// + void VarInfo::set_magic(const ConcatString &nstr, const ConcatString &lstr) { // Check for embedded whitespace @@ -502,6 +541,9 @@ bool VarInfo::set_dict(Dictionary &dict, bool do_exit) { int n; const char *method_name = "VarInfo::set_dict(Dictionary &dict) -> "; + // Store the dictionary + Dict = dict; + // Set init time, if present s = dict.lookup_string(conf_key_init_time, false); if(dict.last_lookup_status()) set_init(timestring_to_unix(s.c_str())); @@ -549,6 +591,9 @@ bool VarInfo::set_dict(Dictionary &dict, bool do_exit) { // Parse regrid, if present Regrid = parse_conf_regrid(&dict, &DefaultRegrid, false); + // Parse wind metadata + WindInfo = parse_conf_wind_metadata(&dict); + // Parse set_attr strings SetAttrName = parse_set_attr_string(dict, conf_key_set_attr_name, true); @@ -582,12 +627,14 @@ bool VarInfo::set_dict(Dictionary &dict, bool do_exit) { parse_set_attr_flag(dict, conf_key_is_u_wind); SetAttrIsVWind = parse_set_attr_flag(dict, conf_key_is_v_wind); + SetAttrIsGridRelative = + parse_set_attr_flag(dict, conf_key_is_grid_relative); SetAttrIsWindSpeed = parse_set_attr_flag(dict, conf_key_is_wind_speed); SetAttrIsWindDirection = parse_set_attr_flag(dict, conf_key_is_wind_direction); - SetAttrIsGridRelative = - parse_set_attr_flag(dict, conf_key_is_grid_relative); + SetAttrIsKineticEnergy = + parse_set_attr_flag(dict, conf_key_is_kinetic_energy); SetAttrIsProb = parse_set_attr_flag(dict, conf_key_is_prob); @@ -599,8 +646,16 @@ bool VarInfo::set_dict(Dictionary &dict, bool do_exit) { /////////////////////////////////////////////////////////////////////////////// -void VarInfo::set_level_info_grib(Dictionary & dict){ +bool VarInfo::reset_dict_with_name(const char *str) { + DictionaryEntry e; + e.set_string(conf_key_name, str); + Dict.store(e); + return set_dict(Dict); +} +/////////////////////////////////////////////////////////////////////////////// + +void VarInfo::set_level_info_grib(Dictionary & dict){ ConcatString field_level = dict.lookup_string(conf_key_level, false); LevelType lt; string lvl_type, lvl_val1, lvl_val2; @@ -774,6 +829,21 @@ bool VarInfo::is_flag_set(int flag) const { /////////////////////////////////////////////////////////////////////////////// +int VarInfo::get_wind_flag(const int set_attr_val, + const StringArray &field_names) const { + int flag = bad_data_int; + + // Use explicit boolean definition, if provided + if(!is_bad_data(set_attr_val)) flag = set_attr_val != 0; + + // Otherwise, check the list of field names + if(is_bad_data(flag) && field_names.has(Name)) flag = 1; + + return flag; +} + +/////////////////////////////////////////////////////////////////////////////// + bool VarInfo::is_precipitation() const { return is_flag_set(SetAttrIsPrecipitation); } @@ -787,25 +857,67 @@ bool VarInfo::is_specific_humidity() const { /////////////////////////////////////////////////////////////////////////////// bool VarInfo::is_u_wind() const { - return is_flag_set(SetAttrIsUWind); + return is_flag_set(get_wind_flag(SetAttrIsUWind, + WindInfo.u_wind)); } /////////////////////////////////////////////////////////////////////////////// bool VarInfo::is_v_wind() const { - return is_flag_set(SetAttrIsVWind); + return is_flag_set(get_wind_flag(SetAttrIsVWind, + WindInfo.v_wind)); } /////////////////////////////////////////////////////////////////////////////// bool VarInfo::is_wind_speed() const { - return is_flag_set(SetAttrIsWindSpeed); + return is_flag_set(get_wind_flag(SetAttrIsWindSpeed, + WindInfo.wind_speed)); } /////////////////////////////////////////////////////////////////////////////// bool VarInfo::is_wind_direction() const { - return is_flag_set(SetAttrIsWindDirection); + return is_flag_set(get_wind_flag(SetAttrIsWindDirection, + WindInfo.wind_direction)); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool VarInfo::is_kinetic_energy() const { + return is_flag_set(get_wind_flag(SetAttrIsKineticEnergy, + WindInfo.kinetic_energy)); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool VarInfo::is_wind_rotation() const { + return (is_u_wind() || + is_v_wind() || + is_wind_direction()); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool VarInfo::is_grid_relative() const { + return (!is_bad_data(SetAttrIsGridRelative) ? + SetAttrIsGridRelative != 0 : + GridRelativeFlag); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool VarInfo::need_uv_wind() const { + return(is_wind_speed() || + is_wind_direction() || + is_kinetic_energy()); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool VarInfo::need_rotation() const { + return is_wind_rotation() && + is_grid_relative(); } /////////////////////////////////////////////////////////////////////////////// @@ -865,13 +977,17 @@ bool VarInfo::validate_wind_attributes(bool do_exit, const char *caller_name) co if(SetAttrIsVWind == 1) n++; if(SetAttrIsWindSpeed == 1) n++; if(SetAttrIsWindDirection == 1) n++; + if(SetAttrIsKineticEnergy == 1) n++; if(n > 1) { ConcatString msg; msg << "\n" << (caller_name == nullptr ? "" : caller_name) << "At most one wind attribute flag (" - << conf_key_is_u_wind << ", " << conf_key_is_v_wind << ", " - << conf_key_is_wind_speed << ", " << conf_key_is_wind_direction + << conf_key_is_u_wind << ", " + << conf_key_is_v_wind << ", " + << conf_key_is_wind_speed << ", " + << conf_key_is_wind_direction << ", " + << conf_key_is_kinetic_energy << ") can be set to true for each field.\n\n"; handle_config_error(msg, do_exit); return false; @@ -882,7 +998,7 @@ bool VarInfo::validate_wind_attributes(bool do_exit, const char *caller_name) co /////////////////////////////////////////////////////////////////////////////// ConcatString parse_set_attr_string(Dictionary &dict, const char *key, - bool check_ws) { + bool check_ws) { ConcatString cs; cs = dict.lookup_string(key, false); diff --git a/src/libcode/vx_data2d/var_info.h b/src/libcode/vx_data2d/var_info.h index bc3b32ae13..1599405113 100644 --- a/src/libcode/vx_data2d/var_info.h +++ b/src/libcode/vx_data2d/var_info.h @@ -28,8 +28,8 @@ class VarInfo { protected: - ConcatString MagicStr; // Requested magic string Dictionary Dict; // Requested fcst/obs dictionary of fields + ConcatString MagicStr; // Requested magic string ConcatString ReqName; // Requested parameter name ConcatString Name; // Name of parameter @@ -60,6 +60,9 @@ class VarInfo RegridInfo DefaultRegrid; // Default regridding logic RegridInfo Regrid; // Regridding logic + bool GridRelativeFlag; // Wind data is grid relative + WindMetadata WindInfo; // Wind variable metadata + // Options to override metadata ConcatString SetAttrName; ConcatString SetAttrUnits; @@ -80,12 +83,14 @@ class VarInfo int SetAttrIsGridRelative; int SetAttrIsWindSpeed; int SetAttrIsWindDirection; + int SetAttrIsKineticEnergy; int SetAttrIsProb; void init_from_scratch(); void assign(const VarInfo &); bool handle_config_error(const ConcatString &msg, bool do_exit) const; - bool is_flag_set(int flag) const; + bool is_flag_set(int) const; + int get_wind_flag(int, const StringArray &) const; void parse_and_set_name_level(Dictionary &dict); bool validate_censor_arrays(const ThreshArray &ta, const NumArray &na, bool do_exit, const char *caller_name=nullptr) const; @@ -98,13 +103,12 @@ class VarInfo VarInfo(const VarInfo &); VarInfo & operator=(const VarInfo &); - virtual VarInfo *clone() const = 0; + virtual std::unique_ptr clone() const = 0; // Conversion function UserFunc_1Arg ConvertFx; void clear(); - void clone_base() const; virtual void dump(std::ostream &) const; @@ -145,6 +149,7 @@ class VarInfo NumArray range() const; RegridInfo regrid() const; + WindMetadata wind_info() const; ConcatString magic_str_attr() const; ConcatString name_attr() const; @@ -167,10 +172,14 @@ class VarInfo virtual bool set_dict(Dictionary &, bool do_exit=true); virtual void add_grib_code(Dictionary &); + bool reset_dict_with_name(const char *); + void set_req_name(const char *); + void set_req_name(const std::string &); void set_name(const char *); - void set_name(const std::string); + void set_name(const std::string &); void set_units(const char *); + void set_units(const std::string &); void set_level_info(const LevelInfo &); void set_req_level_name(const char *); void set_level_name(const char *); @@ -199,6 +208,9 @@ class VarInfo void set_default_regrid(const RegridInfo &); void set_regrid(const RegridInfo &); + void set_grid_relative_flag(bool); + void set_earth_relative(); + void set_level_info_grib(Dictionary & dict); void set_prob_info_grib(ConcatString prob_name, double thresh_lo, double thresh_hi); @@ -213,6 +225,11 @@ class VarInfo virtual bool is_v_wind() const; virtual bool is_wind_speed() const; virtual bool is_wind_direction() const; + bool is_kinetic_energy() const; + bool is_wind_rotation() const; + bool is_grid_relative() const; + bool need_uv_wind() const; + bool need_rotation() const; bool is_prob() const; }; @@ -250,6 +267,7 @@ inline int VarInfo::n_bins() const { return nBins; } inline NumArray VarInfo::range() const { return Range; } inline RegridInfo VarInfo::regrid() const { return Regrid; } +inline WindMetadata VarInfo::wind_info() const { return WindInfo; } inline ConcatString VarInfo::name_attr() const { return(SetAttrName.empty() ? name() : SetAttrName); } inline ConcatString VarInfo::units_attr() const { return(SetAttrUnits.empty() ? units() : SetAttrUnits); } diff --git a/src/libcode/vx_data2d/var_info_pairs.cc b/src/libcode/vx_data2d/var_info_pairs.cc index 631302b251..3cd685aa8b 100644 --- a/src/libcode/vx_data2d/var_info_pairs.cc +++ b/src/libcode/vx_data2d/var_info_pairs.cc @@ -74,11 +74,8 @@ VarInfoPairs & VarInfoPairs::operator=(const VarInfoPairs &f) { /////////////////////////////////////////////////////////////////////////////// -VarInfo *VarInfoPairs::clone() const { - - VarInfoPairs *ret = new VarInfoPairs(*this); - - return (VarInfo *)ret; +unique_ptr VarInfoPairs::clone() const { + return unique_ptr(new VarInfoPairs(*this)); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_data2d/var_info_pairs.h b/src/libcode/vx_data2d/var_info_pairs.h index 51fe27a51f..23121db1b7 100644 --- a/src/libcode/vx_data2d/var_info_pairs.h +++ b/src/libcode/vx_data2d/var_info_pairs.h @@ -33,7 +33,7 @@ class VarInfoPairs : public VarInfo { ~VarInfoPairs(); VarInfoPairs(const VarInfoPairs &); VarInfoPairs & operator=(const VarInfoPairs &); - VarInfo *clone() const; + std::unique_ptr clone() const override; void dump(std::ostream &) const; void clear(); diff --git a/src/libcode/vx_data2d_grib/data2d_grib.cc b/src/libcode/vx_data2d_grib/data2d_grib.cc index a004f5fca0..13ded47942 100644 --- a/src/libcode/vx_data2d_grib/data2d_grib.cc +++ b/src/libcode/vx_data2d_grib/data2d_grib.cc @@ -404,7 +404,8 @@ return count; //////////////////////////////////////////////////////////////////////// -bool MetGrib1DataFile::data_plane(VarInfo &vinfo, DataPlane &plane) { +bool MetGrib1DataFile::data_plane(VarInfo &vinfo, DataPlane &plane, + bool do_winds) { bool status = false; int n_planes = 0; DataPlaneArray plane_array; @@ -412,7 +413,7 @@ bool MetGrib1DataFile::data_plane(VarInfo &vinfo, DataPlane &plane) { int j; // Call data_plane_array() to retrieve all matching records - n_planes = data_plane_array(*vinfo_grib, plane_array); + n_planes = data_plane_array(*vinfo_grib, plane_array, do_winds); // Process multiple matches if ( n_planes > 0 ) { @@ -464,22 +465,22 @@ bool MetGrib1DataFile::data_plane(VarInfo &vinfo, DataPlane &plane) { //////////////////////////////////////////////////////////////////////// int MetGrib1DataFile::data_plane_array(VarInfo &vinfo, - DataPlaneArray &plane_array) { + DataPlaneArray &plane_array, + bool do_winds) { bool status = false; - bool exact; - int i, lower, upper, type_num; GribRecord r; VarInfoGrib *vinfo_grib = (VarInfoGrib *) &vinfo; VarInfoGrib vinfo_grib_winds; LevelInfo cur_level; DataPlane cur_plane; - DataPlaneArray u_plane_array, v_plane_array; + DataPlaneArray u_plane_array; + DataPlaneArray v_plane_array; // Initialize plane_array.clear(); // Loop through the records in the GRIB file looking for matches - for(i=0; in_records(); i++) { + for(int i=0; in_records(); i++) { // Read the current record GF->seek_record(i); @@ -488,7 +489,7 @@ int MetGrib1DataFile::data_plane_array(VarInfo &vinfo, // Check for a range match if(is_range_match(*vinfo_grib, r)) { - exact = is_exact_match(*vinfo_grib, r); + bool exact = is_exact_match(*vinfo_grib, r); mlog << Debug(3) << "MetGrib1DataFile::data_plane_array() -> " << "Found " << ( exact ? "exact" : "range" ) << " match for VarInfo \"" << vinfo.magic_str() @@ -496,35 +497,14 @@ int MetGrib1DataFile::data_plane_array(VarInfo &vinfo, << filename() << "\".\n"; // Get the level information for this record + int lower; + int upper; + int type_num; read_pds_level(r, lower, upper, type_num); // Read current record status = get_data_plane(r, cur_plane); - // Check if these are winds that should be rotated - if(status && - is_grid_relative(r) && - (vinfo_grib->is_u_wind() || - vinfo_grib->is_v_wind() || - vinfo_grib->is_wind_direction())) { - - // Initialize the current VarInfo object - vinfo_grib_winds = *vinfo_grib; - cur_level = vinfo_grib_winds.level(); - - // Reset the level range for pressure and vertical levels - if(cur_level.type() == LevelType_Pres || - cur_level.type() == LevelType_Vert) { - cur_level.set_range(lower, upper); - vinfo_grib_winds.set_level_info(cur_level); - } - - // Rotate the winds - rotate_winds(vinfo_grib_winds, cur_plane); - } - - if(status) status = process_data_plane(&vinfo, cur_plane); - if(!status) { cur_plane.clear(); lower = upper = bad_data_int; @@ -534,81 +514,27 @@ int MetGrib1DataFile::data_plane_array(VarInfo &vinfo, continue; } + // Store whether winds are grid relative + vinfo.set_grid_relative_flag(is_grid_relative(r)); + // Add current record to the data plane array plane_array.add(cur_plane, (double) lower, (double) upper); } } // end for loop - // If no matches were found, check for wind records to be derived. - if(plane_array.n_planes() == 0) { - - // Derive wind speed and direction - if(vinfo_grib->code() == wdir_grib_code || - vinfo_grib->code() == wind_grib_code) { - - mlog << Debug(3) << "MetGrib1DataFile::data_plane_array() -> " - << "Attempt to derive winds from U and V components.\n"; - - // Initialize the current VarInfo object - vinfo_grib_winds = *vinfo_grib; - - // Retrieve U-wind, doing a rotation if necessary - mlog << Debug(3) << "MetGrib1DataFile::data_plane_array() -> " - << "Reading U-wind records.\n"; - vinfo_grib_winds.set_name(ugrd_abbr_str); - data_plane_array(vinfo_grib_winds, u_plane_array); - - // Retrieve V-wind, doing a rotation if necessary - mlog << Debug(3) << "MetGrib1DataFile::data_plane_array() -> " - << "Reading V-wind records.\n"; - vinfo_grib_winds.set_name(vgrd_abbr_str); - data_plane_array(vinfo_grib_winds, v_plane_array); + // Handle wind rotation + if(do_winds) rotate_winds(&vinfo, plane_array); - // Derive wind speed or direction - if(u_plane_array.n_planes() != v_plane_array.n_planes()) { - mlog << Warning << "\nMetGrib1DataFile::data_plane_array() -> " - << "when deriving winds, the number of U-wind records (" - << u_plane_array.n_planes() << ") does not match the " - << "number of V-wind records (" << v_plane_array.n_planes() - << ") for GRIB file \"" << filename() << "\".\n\n"; - return 0; - } - - // Loop through each of the data planes - for(i=0; i " - << "when deriving winds for level " << i+1 - << ", the U-wind levels (" - << u_plane_array.lower(i) << ", " << u_plane_array.upper(i) - << ") do not match the V-wind levels (" - << v_plane_array.lower(i) << ", " << v_plane_array.upper(i) - << ") in GRIB file \"" << filename() << "\".\n\n"; - plane_array.clear(); - return 0; - } - - // Derive wind direction - if(vinfo_grib->code() == wdir_grib_code) { - derive_wdir(u_plane_array[i], v_plane_array[i], cur_plane); - } - // Derive wind speed - else { - derive_wind(u_plane_array[i], v_plane_array[i], cur_plane); - } - - // Add the current data plane - plane_array.add(cur_plane, u_plane_array.lower(i), u_plane_array.upper(i)); - - } // end for + // If nothing was found, try to build derived records + if(plane_array.n_planes() == 0 && do_winds) { + derive_winds(&vinfo, plane_array); + } - } // end if wdir or wind - } // end if n_planes == 0 + // Post-process each data plane + for(int i=0; i " << "Found " << plane_array.n_planes() @@ -619,101 +545,6 @@ int MetGrib1DataFile::data_plane_array(VarInfo &vinfo, return plane_array.n_planes(); } -//////////////////////////////////////////////////////////////////////// -// -// This function rotates the wind data that's passed in from -// grid-relative to earth-relative. -// -//////////////////////////////////////////////////////////////////////// - -void MetGrib1DataFile::rotate_winds(VarInfoGrib &vinfo_grib, DataPlane &plane) { - VarInfoGrib vinfo_grib_winds = vinfo_grib; - DataPlane u2d, v2d, u2d_rot, v2d_rot; - - // For U-wind, retrieve the corresponding V-wind, and rotate - if(vinfo_grib.is_u_wind()) { - - mlog << Debug(3) << "MetGrib1DataFile::rotate_winds() -> " - << "Have U-wind record, reading V-wind record.\n"; - vinfo_grib_winds.set_name(vgrd_abbr_str); - data_plane_scalar(vinfo_grib_winds, v2d); - rotate_uv_grid_to_earth(plane, v2d, grid(), u2d_rot, v2d_rot); - plane = u2d_rot; - } - - // For V-wind, retrieve the corresponding U-wind, and rotate - else if(vinfo_grib.is_v_wind()) { - - mlog << Debug(3) << "MetGrib1DataFile::rotate_winds() -> " - << "Have V-wind record, reading U-wind record.\n"; - vinfo_grib_winds.set_name(ugrd_abbr_str); - data_plane_scalar(vinfo_grib_winds, u2d); - rotate_uv_grid_to_earth(u2d, plane, grid(), u2d_rot, v2d_rot); - plane = v2d_rot; - } - - // For wind direction, rotate - else if(vinfo_grib.is_wind_direction()) { - mlog << Debug(3) << "MetGrib1DataFile::rotate_winds() -> " - << "Have wind direction, calling rotate.\n"; - rotate_wdir_grid_to_earth(plane, grid(), u2d); - plane = u2d; - } - - return; -} - -//////////////////////////////////////////////////////////////////////// -// -// This function retrieves a single data plane as reqested in the -// VarInfo object but does not attempt to rotate or derive winds. -// -//////////////////////////////////////////////////////////////////////// - -bool MetGrib1DataFile::data_plane_scalar(VarInfoGrib &vinfo_grib, - DataPlane &plane) { - int i; - GribRecord r; - bool status = false; - - // Initialize the data plane - plane.clear(); - - // Loop through the records in the GRIB file looking for a match - for(i=0; in_records(); i++) { - - // Read the current record. - GF->seek_record(i); - (*GF) >> r; - - // Check for an exact match - if(is_exact_match(vinfo_grib, r)) { - - mlog << Debug(3) << "MetGrib1DataFile::data_plane_scalar() -> " - << "Found exact match for VarInfo \"" - << vinfo_grib.magic_str() << "\" in GRIB record " - << i+1 << " of GRIB file \"" << filename() - << "\".\n"; - - // Read current record - status = get_data_plane(r, plane); - - if(status) status = process_data_plane(&vinfo_grib, plane); - - break; - } - } // end for loop - - if(!status) { - mlog << Warning << "\nMetGrib1DataFile::data_plane_scalar() -> " - << "No exact match found for VarInfo \"" - << vinfo_grib.magic_str() << "\" in GRIB file \"" - << filename() << "\".\n\n"; - } - - return status; -} - /////////////////////////////////////////////////////////////////////////////// // // Check whether or not the res_flag indicates that the vectors are defined @@ -721,7 +552,7 @@ bool MetGrib1DataFile::data_plane_scalar(VarInfoGrib &vinfo_grib, // ////////////////////////////////////////////////////////////////////////////// -bool is_grid_relative(const GribRecord &r) { +static bool is_grid_relative(const GribRecord &r) { unsigned char res_flag = 0; // LatLon diff --git a/src/libcode/vx_data2d_grib/data2d_grib.h b/src/libcode/vx_data2d_grib/data2d_grib.h index fedc99b8d7..5f5cafed0b 100644 --- a/src/libcode/vx_data2d_grib/data2d_grib.h +++ b/src/libcode/vx_data2d_grib/data2d_grib.h @@ -42,10 +42,6 @@ class MetGrib1DataFile : public Met2dDataFile { DataPlane Plane; - void rotate_winds(VarInfoGrib &, DataPlane &); - - bool data_plane_scalar(VarInfoGrib &, DataPlane &); - public: MetGrib1DataFile(); @@ -86,15 +82,15 @@ class MetGrib1DataFile : public Met2dDataFile { // retrieve the first matching data plane - bool data_plane(VarInfo &, DataPlane &); + bool data_plane(VarInfo &, DataPlane &, bool do_winds = true) override; // retrieve all matching data planes - int data_plane_array(VarInfo &, DataPlaneArray &); + int data_plane_array(VarInfo &, DataPlaneArray &, bool do_winds = true) override; // retrieve the index of the first matching record - int index(VarInfo &); + int index(VarInfo &) override; }; diff --git a/src/libcode/vx_data2d_grib/data2d_grib_utils.cc b/src/libcode/vx_data2d_grib/data2d_grib_utils.cc index 938d390282..2afccda455 100644 --- a/src/libcode/vx_data2d_grib/data2d_grib_utils.cc +++ b/src/libcode/vx_data2d_grib/data2d_grib_utils.cc @@ -43,7 +43,8 @@ bool is_prelim_match( VarInfoGrib & vinfo, const GribRecord & g) unixtime ut, init_ut, valid_ut; const char *method_name = "is_prelim_match() -> "; - int p_code, code_for_lookup= vinfo.field_rec (); + int p_code; + int code_for_lookup = vinfo.field_rec(); double p_thresh_lo, p_thresh_hi; Section1_Header *pds = (Section1_Header *) g.pds; @@ -53,8 +54,8 @@ bool is_prelim_match( VarInfoGrib & vinfo, const GribRecord & g) // clean up the code - except for code 33 and 34 (UGRID and VGRID) // and name WIND when we derive wind speed and direction bool is_lookup_for_wind_components = - ( vinfo.code () == ugrd_grib_code || \ - vinfo.code () == vgrd_grib_code ) && \ + ( vinfo.code () == ugrd_grib_code || + vinfo.code () == vgrd_grib_code ) && field_name == "WIND"; if ( is_lookup_for_wind_components) { @@ -62,9 +63,9 @@ bool is_prelim_match( VarInfoGrib & vinfo, const GribRecord & g) field_name.clear (); code_for_lookup = vinfo.code (); } - vinfo.set_code (bad_data_int); - vinfo.units ().clear (); - vinfo.long_name ().clear (); + vinfo.set_code(bad_data_int); + vinfo.units().clear(); + vinfo.long_name().clear(); // // check ptv @@ -155,22 +156,21 @@ bool is_prelim_match( VarInfoGrib & vinfo, const GribRecord & g) // if it is one of APCP names - (APCP_Z0) - use 'APCP' only if ( check_reg_exp("^APCP_[0-9]*$", field_name.c_str()) ) field_name = "APCP"; - Grib1TableEntry tab; - int tab_match = -1; + vector matches; // if the name is specified, use it if( !field_name.empty() ) { // look up the name in the grib tables - if( !GribTable.lookup_grib1(field_name.c_str(), vinfo_ptv, code_for_lookup, vinfo_center, vinfo_subcenter, tab, tab_match) ) - { - // if did not find with params from the header - try default - if( !GribTable.lookup_grib1(field_name.c_str(), default_grib1_ptv, code_for_lookup, default_grib1_center, default_grib1_subcenter, tab, tab_match) ) - { - // if the lookup still fails, then it's not a match - return false; - } + // if did not find with params from the header - try default + if(GribTable.lookup_grib1(field_name.c_str(), vinfo_ptv, code_for_lookup, + vinfo_center, vinfo_subcenter, matches) == 0 && + GribTable.lookup_grib1(field_name.c_str(), default_grib1_ptv, code_for_lookup, + default_grib1_center, default_grib1_subcenter, matches) == 0) + { + // if both lookups fail, then it's not a match + return false; } } @@ -187,22 +187,23 @@ bool is_prelim_match( VarInfoGrib & vinfo, const GribRecord & g) } // use the specified indexes to look up the field name - if( !GribTable.lookup_grib1(code_for_lookup, vinfo_ptv, vinfo_center, vinfo_subcenter,tab) ) { - //if did not find with params from the header - try default - if( !GribTable.lookup_grib1(code_for_lookup, default_grib1_ptv, default_grib1_center, default_grib1_subcenter, tab) ) - { - mlog << Error << "\n" << method_name - << "no parameter found with matching GRIB1_ptv (" - << vinfo_ptv << ") " << "GRIB1_code (" - << vinfo.field_rec() << "). Use the MET_GRIB_TABLES " - << "environment variable to define custom GRIB tables.\n\n"; - exit(1); - } + // if did not find with params from the header - try default + if(GribTable.lookup_grib1(code_for_lookup, vinfo_ptv, + vinfo_center, vinfo_subcenter, matches) == 0 && + GribTable.lookup_grib1(code_for_lookup, default_grib1_ptv, + default_grib1_center, default_grib1_subcenter, matches) == 0) + { + mlog << Error << "\n" << method_name + << "no parameter found with matching GRIB1_ptv (" + << vinfo_ptv << ") " << "GRIB1_code (" + << vinfo.field_rec() << "). Use the MET_GRIB_TABLES " + << "environment variable to define custom GRIB tables.\n\n"; + exit(1); } } - vinfo.set_code ( tab.code ); - vinfo.set_units ( tab.units.c_str()); - vinfo.set_long_name ( tab.full_name.c_str() ); + vinfo.set_code ( matches[0].code); + vinfo.set_units ( matches[0].units.c_str()); + vinfo.set_long_name ( matches[0].full_name.c_str()); // // test the level type number, if specified diff --git a/src/libcode/vx_data2d_grib/grib_strings.cc b/src/libcode/vx_data2d_grib/grib_strings.cc index c6d68de13c..44a277a7aa 100644 --- a/src/libcode/vx_data2d_grib/grib_strings.cc +++ b/src/libcode/vx_data2d_grib/grib_strings.cc @@ -36,8 +36,8 @@ ConcatString get_grib_code_list_str(int k, int grib_code, int ptv) { // look up the name in the grib tables - Grib1TableEntry tab; - if( !GribTable.lookup_grib1(grib_code, ptv, tab) ){ + vector matches; + if(GribTable.lookup_grib1(grib_code, ptv, matches) == 0){ mlog << Error << "\nget_grib_code_list_str() - unrecognized GRIB1 code " << grib_code << " and/or table version " << ptv << "\n\n"; exit(1); @@ -45,9 +45,9 @@ ConcatString get_grib_code_list_str(int k, int grib_code, int ptv) // return the requested field switch(k) { - case 0: return tab.full_name; // GRIB Code Name - case 1: return tab.units; // GRIB Code Unit - case 2: return tab.parm_name; // GRIB Code Abbreviation + case 0: return matches[0].full_name; // GRIB Code Name + case 1: return matches[0].units; // GRIB Code Unit + case 2: return matches[0].parm_name; // GRIB Code Abbreviation default: mlog << Error << "\nget_grib_code_list_str() - unexpected value for k: " << k << "\n\n"; @@ -246,14 +246,14 @@ int str_to_grib_code(const char *c, int ptv) if( check_reg_exp("[0-9]+", c) ) return atoi(c); // look up the name in the grib tables - int n_matches; - Grib1TableEntry tab; - if( !GribTable.lookup_grib1(c, ptv, bad_data_int, - default_grib1_center, default_grib1_subcenter, - tab, n_matches) ) + vector matches; + if(GribTable.lookup_grib1(c, ptv, bad_data_int, + default_grib1_center, default_grib1_subcenter, + matches) == 0) { return bad_data_int; + } - return tab.code; + return matches[0].code; } diff --git a/src/libcode/vx_data2d_grib/var_info_grib.cc b/src/libcode/vx_data2d_grib/var_info_grib.cc index abb48a9321..9fe890b170 100644 --- a/src/libcode/vx_data2d_grib/var_info_grib.cc +++ b/src/libcode/vx_data2d_grib/var_info_grib.cc @@ -75,11 +75,8 @@ VarInfoGrib & VarInfoGrib::operator=(const VarInfoGrib &f) { /////////////////////////////////////////////////////////////////////////////// -VarInfo *VarInfoGrib::clone() const { - - VarInfoGrib *ret = new VarInfoGrib(*this); - - return (VarInfo *)ret; +unique_ptr VarInfoGrib::clone() const { + return unique_ptr(new VarInfoGrib(*this)); } /////////////////////////////////////////////////////////////////////////////// @@ -102,14 +99,14 @@ void VarInfoGrib::assign(const VarInfoGrib &v) { VarInfo::assign(v); // Copy - PTV = v.ptv(); - Code = v.code(); - LvlType = v.lvl_type(); - PCode = v.p_code(); - Center = v.center (); - Subcenter = v.subcenter (); - FieldRec = v.field_rec (); - TRI = v.tri(); + PTV = v.PTV; + Code = v.Code; + LvlType = v.LvlType; + PCode = v.PCode; + Center = v.Center; + Subcenter = v.Subcenter; + FieldRec = v.FieldRec; + TRI = v.TRI; return; } @@ -122,13 +119,14 @@ void VarInfoGrib::clear() { VarInfo::clear(); // Initialize - PTV = bad_data_int; - Code = bad_data_int; - LvlType = bad_data_int; - PCode = bad_data_int; - Center = bad_data_int; - Subcenter = bad_data_int; - FieldRec = bad_data_int; + PTV = bad_data_int; + Code = bad_data_int; + LvlType = bad_data_int; + PCode = bad_data_int; + Center = bad_data_int; + Subcenter = bad_data_int; + FieldRec = bad_data_int; + TRI = bad_data_int; return; } @@ -139,12 +137,13 @@ void VarInfoGrib::dump(ostream &out) const { // Dump out the contents out << "VarInfoGrib::dump():\n" - << " PTV = " << PTV << "\n" - << " Code = " << Code << "\n" - << " LvlType = " << LvlType << "\n" - << " PCode = " << PCode << "\n" - << " Center = " << Center << "\n" - << " Subcenter = " << Subcenter << "\n"; + << " PTV = " << PTV << "\n" + << " Code = " << Code << "\n" + << " LvlType = " << LvlType << "\n" + << " PCode = " << PCode << "\n" + << " Center = " << Center << "\n" + << " Subcenter = " << Subcenter << "\n" + << " TRI = " << TRI << "\n"; return; } @@ -210,7 +209,6 @@ void VarInfoGrib::set_tri(int v) { void VarInfoGrib::add_grib_code (Dictionary &dict) { ConcatString field_name = dict.lookup_string(conf_key_name, false); - int tab_match = -1; int field_ptv = dict.lookup_int (conf_key_GRIB1_ptv, false); int field_code = dict.lookup_int (conf_key_GRIB1_code, false); @@ -221,27 +219,30 @@ void VarInfoGrib::add_grib_code (Dictionary &dict) } int field_center = dict.lookup_int (conf_key_GRIB1_center, false); int field_subcenter = dict.lookup_int (conf_key_GRIB1_subcenter, false); - Grib1TableEntry tab; + + vector matches; // if the name is specified, use it if( !field_name.empty() ){ // look up the name in the grib tables - if( !GribTable.lookup_grib1(field_name.c_str(), field_ptv, field_code, field_center, field_subcenter, tab, tab_match) ) + // if did not find with params from the header - try default + if(GribTable.lookup_grib1(field_name.c_str(), field_ptv, field_code, + field_center, field_subcenter, + matches) == 0 && + GribTable.lookup_grib1(field_name.c_str(), default_grib1_ptv, field_code, + default_grib1_center, default_grib1_subcenter, + matches) == 0) { - // if did not find with params from the header - try default - if( !GribTable.lookup_grib1(field_name.c_str(), default_grib1_ptv, field_code, default_grib1_center, default_grib1_subcenter, tab, tab_match) ) - { - mlog << Error << "\nVarInfoGrib::add_grib_code() -> " - << "unrecognized GRIB1 field abbreviation '" << field_name - << "' for table version (" << field_ptv - << "), center (" << field_center - << "), and subcenter (" << field_subcenter - << ") or default table version (" << default_grib1_ptv - << "), center (" << default_grib1_center - << "), and subcenter (" << default_grib1_subcenter << ").\n\n"; - exit(1); - } + mlog << Error << "\nVarInfoGrib::add_grib_code() -> " + << "unrecognized GRIB1 field abbreviation '" << field_name + << "' for table version (" << field_ptv + << "), center (" << field_center + << "), and subcenter (" << field_subcenter + << ") or default table version (" << default_grib1_ptv + << "), center (" << default_grib1_center + << "), and subcenter (" << default_grib1_subcenter << ").\n\n"; + exit(1); } } @@ -257,22 +258,25 @@ void VarInfoGrib::add_grib_code (Dictionary &dict) } // use the specified indexes to look up the field name - if( !GribTable.lookup_grib1(field_code, field_ptv, field_center, field_subcenter,tab) ){ - //if did not find with params from the header - try default - if( !GribTable.lookup_grib1(field_code, default_grib1_ptv, default_grib1_center, default_grib1_subcenter,tab) ) - { - mlog << Error << "\nVarInfoGrib::add_grib_code() -> " - << "no parameter found with matching GRIB1_ptv (" - << field_ptv << ") " << "GRIB1_code (" << field_code - << "). Use the MET_GRIB_TABLES environment variable " - << "to define custom GRIB tables.\n\n"; - exit(1); - } + // if did not find with params from the header - try default + if(GribTable.lookup_grib1(field_code, field_ptv, + field_center, field_subcenter, + matches) == 0 && + GribTable.lookup_grib1(field_code, default_grib1_ptv, + default_grib1_center, default_grib1_subcenter, + matches) == 0) + { + mlog << Error << "\nVarInfoGrib::add_grib_code() -> " + << "no parameter found with matching GRIB1_ptv (" + << field_ptv << ") " << "GRIB1_code (" << field_code + << "). Use the MET_GRIB_TABLES environment variable " + << "to define custom GRIB tables.\n\n"; + exit(1); } } - set_code ( tab.code ); - set_long_name ( tab.full_name.c_str() ); - set_units ( tab.units.c_str() ); + set_code ( matches[0].code ); + set_long_name ( matches[0].full_name.c_str() ); + set_units ( matches[0].units.c_str() ); } /////////////////////////////////////////////////////////////////////////////// @@ -281,8 +285,6 @@ bool VarInfoGrib::set_dict(Dictionary & dict, bool do_exit) { VarInfo::set_dict(dict); - int tab_match = -1; - Grib1TableEntry tab; ConcatString field_name = dict.lookup_string(conf_key_name, false); int field_ptv = dict.lookup_int (conf_key_GRIB1_ptv, false); int field_code = dict.lookup_int (conf_key_GRIB1_code, false); @@ -327,8 +329,11 @@ bool VarInfoGrib::set_dict(Dictionary & dict, bool do_exit) { double thresh_lo = dict_prob->lookup_double(conf_key_thresh_lo, false); double thresh_hi = dict_prob->lookup_double(conf_key_thresh_hi, false); + vector matches; + // look up the probability field abbreviation - if( !GribTable.lookup_grib1(prob_name.c_str(), field_ptv, field_code, tab, tab_match) ){ + if(GribTable.lookup_grib1(prob_name.c_str(), field_ptv, field_code, + matches) == 0){ ConcatString msg; msg << "\nVarInfoGrib::set_dict() -> " << "unrecognized GRIB1 probability field abbreviation '" @@ -337,10 +342,9 @@ bool VarInfoGrib::set_dict(Dictionary & dict, bool do_exit) { return false; } - set_p_flag ( true ); - set_p_code ( tab.code ); - set_p_units ( tab.units.c_str() ); - + set_p_flag ( true ); + set_p_code ( matches[0].code ); + set_p_units ( matches[0].units.c_str() ); set_prob_info_grib(prob_name, thresh_lo, thresh_hi); return true; @@ -353,9 +357,8 @@ bool VarInfoGrib::is_precipitation() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsPrecipitation)) { - return(SetAttrIsPrecipitation != 0); - } + int flag = SetAttrIsPrecipitation; + if(!is_bad_data(flag)) return is_flag_set(flag); // // The ReqName member contains the requested GRIB code abbreviation. @@ -379,9 +382,8 @@ bool VarInfoGrib::is_specific_humidity() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsSpecificHumidity)) { - return(SetAttrIsSpecificHumidity != 0); - } + int flag = SetAttrIsSpecificHumidity; + if(!is_bad_data(flag)) return is_flag_set(flag); // // The ReqName member contains the requested GRIB code abbreviation. @@ -405,9 +407,8 @@ bool VarInfoGrib::is_u_wind() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsUWind)) { - return(SetAttrIsUWind != 0); - } + int flag = get_wind_flag(SetAttrIsUWind, WindInfo.u_wind); + if(!is_bad_data(flag)) return is_flag_set(flag); return Code == ugrd_grib_code || ReqName == ugrd_abbr_str; @@ -420,9 +421,8 @@ bool VarInfoGrib::is_v_wind() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsVWind)) { - return(SetAttrIsVWind != 0); - } + int flag = get_wind_flag(SetAttrIsVWind, WindInfo.v_wind); + if(!is_bad_data(flag)) return is_flag_set(flag); return Code == vgrd_grib_code || ReqName == vgrd_abbr_str; @@ -435,9 +435,8 @@ bool VarInfoGrib::is_wind_speed() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsWindSpeed)) { - return(SetAttrIsWindSpeed != 0); - } + int flag = get_wind_flag(SetAttrIsWindSpeed, WindInfo.wind_speed); + if(!is_bad_data(flag)) return is_flag_set(flag); return Code == wind_grib_code || ReqName == wind_abbr_str; @@ -450,9 +449,8 @@ bool VarInfoGrib::is_wind_direction() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsWindDirection)) { - return(SetAttrIsWindDirection != 0); - } + int flag = get_wind_flag(SetAttrIsWindDirection, WindInfo.wind_direction); + if(!is_bad_data(flag)) return is_flag_set(flag); return Code == wdir_grib_code || ReqName == wdir_abbr_str; diff --git a/src/libcode/vx_data2d_grib/var_info_grib.h b/src/libcode/vx_data2d_grib/var_info_grib.h index f3f0f49f96..c333924b39 100644 --- a/src/libcode/vx_data2d_grib/var_info_grib.h +++ b/src/libcode/vx_data2d_grib/var_info_grib.h @@ -18,6 +18,7 @@ #include "vx_config.h" #include "data_file_type.h" +#include "table_lookup.h" /////////////////////////////////////////////////////////////////////////////// // @@ -31,10 +32,6 @@ static const char * const CONFIG_GRIB_Code = "GRIB_Code"; static const char * const CONFIG_GRIB_LvlType = "GRIB_Level"; static const char * const CONFIG_GRIB_PCode = "GRIB_Prob_Code"; -/////////////////////////////////////////////////////////////////////////////// - - - /////////////////////////////////////////////////////////////////////////////// class VarInfoGrib : public VarInfo @@ -67,7 +64,7 @@ class VarInfoGrib : public VarInfo ~VarInfoGrib() override; VarInfoGrib(const VarInfoGrib &); VarInfoGrib & operator=(const VarInfoGrib &); - VarInfo *clone() const override; + std::unique_ptr clone() const override; void dump(std::ostream &) const override; void clear(); diff --git a/src/libcode/vx_data2d_grib2/data2d_grib2.cc b/src/libcode/vx_data2d_grib2/data2d_grib2.cc index 4508f334f1..4895d7f13f 100644 --- a/src/libcode/vx_data2d_grib2/data2d_grib2.cc +++ b/src/libcode/vx_data2d_grib2/data2d_grib2.cc @@ -38,8 +38,9 @@ using namespace std; //////////////////////////////////////////////////////////////////////// -double scaled2dbl(int scale_factor, int scale_value); -int parse_int4(g2int); +static bool is_grid_relative(const Grib2Record *); +static double scaled2dbl(int scale_factor, int scale_value); +static int parse_int4(g2int); //////////////////////////////////////////////////////////////////////// // @@ -82,19 +83,12 @@ MetGrib2DataFile & MetGrib2DataFile::operator=(const MetGrib2DataFile &) { void MetGrib2DataFile::grib2_init_from_scratch() { ScanMode = -1; - - PairMap[ugrd_abbr_str] = vgrd_abbr_str; - PairMap[vgrd_abbr_str] = ugrd_abbr_str; - - return; } //////////////////////////////////////////////////////////////////////// void MetGrib2DataFile::close() { fclose(FileGrib2); - - return; } //////////////////////////////////////////////////////////////////////// @@ -148,7 +142,8 @@ void MetGrib2DataFile::dump(ostream & out, int depth) const { //////////////////////////////////////////////////////////////////////// -bool MetGrib2DataFile::data_plane(VarInfo &vinfo, DataPlane &plane) { +bool MetGrib2DataFile::data_plane(VarInfo &vinfo, DataPlane &plane, + bool do_winds) { // narrow the vinfo pointer auto vinfo_g2 = (VarInfoGrib2*)(&vinfo); @@ -159,26 +154,24 @@ bool MetGrib2DataFile::data_plane(VarInfo &vinfo, DataPlane &plane) { find_record_matches(vinfo_g2, listMatch, listMatchRange); // if no matches were found, check for derived records - if( 1 > listMatch.size() ){ - - DataPlaneArray plane_array = check_derived(vinfo_g2); + if(listMatch.empty() && do_winds) { + DataPlaneArray plane_array; + derive_winds(vinfo_g2, plane_array); // verify that only a single data_plane was found - if( 1 > plane_array.n_planes() ){ + if(plane_array.n_planes() < 1){ mlog << Warning << "\nMetGrib2DataFile::data_plane() -> " << "No matching record found for '" << vinfo_g2->magic_str() << "'\n\n"; return false; - } else if( 1 < plane_array.n_planes() ){ + } else if(plane_array.n_planes() > 1) { mlog << Warning << "\nMetGrib2DataFile::data_plane() -> " - << "Wind speed/direction derivation found " - << plane_array.n_planes() - << " matching records found for '" + << "found " << plane_array.n_planes() + << " matching derived records for '" << vinfo_g2->magic_str() << "'\n\n"; - } // report the matched record @@ -189,12 +182,21 @@ bool MetGrib2DataFile::data_plane(VarInfo &vinfo, DataPlane &plane) { plane = plane_array[0]; - return process_data_plane(vinfo_g2, plane); + // handle wind rotation + bool status = true; + if(do_winds) status = rotate_winds(vinfo_g2, plane); + + if(status) status = process_data_plane(vinfo_g2, plane); + + return status; } // END: if( 1 > listMatch.size() ) // verify that a only single record was found - if( 1 < listMatch.size() ){ + if(listMatch.empty()) { + return false; + } + else if( 1 < listMatch.size() ){ ConcatString msg; for(size_t i=0; i < listMatch.size(); i++) { msg << " Record " << listMatch[i]->RecNum @@ -226,21 +228,25 @@ bool MetGrib2DataFile::data_plane(VarInfo &vinfo, DataPlane &plane) { << Filename << "'\n"; // read the data plane for the matched record - bool read_success = read_grib2_record_data_plane(listMatch[0], - plane); + bool status = read_grib2_record_data_plane(listMatch[0], plane); - // check the data plane for wind rotation - plane = check_uv_rotation(vinfo_g2, listMatch[0], plane); + if(status) { - if(read_success) read_success = process_data_plane(vinfo_g2, plane); + // handle wind rotation + vinfo_g2->set_grid_relative_flag(is_grid_relative(listMatch[0])); + if(status && do_winds) status = rotate_winds(vinfo_g2, plane); - return read_success; + if(status) status = process_data_plane(vinfo_g2, plane); + } + + return status; } //////////////////////////////////////////////////////////////////////// int MetGrib2DataFile::data_plane_array(VarInfo &vinfo, - DataPlaneArray &plane_array) { + DataPlaneArray &plane_array, + bool do_winds) { // Initialize plane_array.clear(); @@ -307,12 +313,12 @@ int MetGrib2DataFile::data_plane_array(VarInfo &vinfo, } // if nothing was found, try to build derived records - else { + else if(do_winds) { - plane_array = check_derived(vinfo_g2); + derive_winds(vinfo_g2, plane_array); // if no matches were found, bail - if( 1 > plane_array.n_planes() ){ + if(plane_array.n_planes() < 1) { mlog << Warning << "\nMetGrib2DataFile::data_plane_array() -> " << "No matching records found for '" << vinfo_g2->magic_str() << "'\n\n"; @@ -321,9 +327,8 @@ int MetGrib2DataFile::data_plane_array(VarInfo &vinfo, // report the matched record mlog << Debug(3) << "\nMetGrib2DataFile::data_plane_array() -> " - << "Wind speed/direction derivation " << "found " << plane_array.n_planes() - << " matching records found for '" + << " matching derived records for '" << vinfo_g2->magic_str() << "'\n\n"; return plane_array.n_planes(); @@ -337,9 +342,6 @@ int MetGrib2DataFile::data_plane_array(VarInfo &vinfo, DataPlane plane; num_read += ( read_grib2_record_data_plane(*it, plane) ? 1 : 0 ); - // check the data plane for wind rotation - plane = check_uv_rotation(vinfo_g2, *it, plane); - // add the data plane to the array at the specified level(s) double lvl_lower = (double)(*it)->LvlVal1; double lvl_upper = (double)(*it)->LvlVal2; @@ -348,9 +350,19 @@ int MetGrib2DataFile::data_plane_array(VarInfo &vinfo, lvl_upper = ( (double)(*it)->LvlVal2 ) / 100.0; } - if(process_data_plane(vinfo_g2, plane)) { - plane_array.add(plane, lvl_lower, lvl_upper); - } + // store grid relative status + vinfo_g2->set_grid_relative_flag(is_grid_relative(*it)); + + // add current plane to the data plane array + plane_array.add(plane, lvl_lower, lvl_upper); + } + + // handle wind rotation + if(do_winds) rotate_winds(vinfo_g2, plane_array); + + // post-process each data plane + for(int i=0; idiscipline() == (*it)->Discipline && - vinfo->parm_cat() == (*it)->ParmCat && - vinfo->parm() == (*it)->Parm ) || - vinfo->name().text() == (*it)->ParmName - ){ + else if ( ( vinfo->discipline() == (*it)->Discipline && + vinfo->parm_cat() == (*it)->ParmCat && + vinfo->parm() == (*it)->Parm ) || + ( vinfo->req_name().text() == (*it)->ParmName ) ){ // test the level type number, if specified if ( !is_bad_data(vinfo->level().type_num()) && @@ -579,115 +589,6 @@ void MetGrib2DataFile::find_record_matches(const VarInfoGrib2* vinfo, //////////////////////////////////////////////////////////////////////// -DataPlane MetGrib2DataFile::check_uv_rotation(const VarInfoGrib2 *vinfo, Grib2Record *rec, DataPlane plane){ - - // check that the field is present in the pair map - string parm_name = vinfo->name().text(); - if( 0 == PairMap.count( parm_name ) || - 0 == (rec->ResCompFlag & 8) ) { - return plane; - } - - // build the magic string of the pair field, and check it - ConcatString pair_mag = build_magic( rec ); - pair_mag.replace(parm_name.data(), PairMap[parm_name].data()); - if( 0 == NameRecMap.count( string(pair_mag.text()) ) ){ - mlog << Debug(3) << "MetGrib2DataFile::check_uv_rotation -> " - << "UV rotation pair record not found: '" << pair_mag - << "'\n"; - return plane; - } - - // read the data plane for the pair record - Grib2Record *rec_pair = NameRecMap[pair_mag.text()]; - DataPlane plane_pair; - read_grib2_record_data_plane(NameRecMap[pair_mag.text()], - plane_pair); - - mlog << Debug(3) << "MetGrib2DataFile::check_uv_rotation() -> " - << "Found pair match \"" << pair_mag << "\" in GRIB2 record " - << rec_pair->RecNum << " field " << rec_pair->FieldNum << "\n"; - - // rotate the winds - DataPlane u2d; - DataPlane v2d; - DataPlane u2d_rot; - DataPlane v2d_rot; - if( 'U' == parm_name.at(0) ){ u2d = plane; v2d = plane_pair; } - else { v2d = plane; u2d = plane_pair; } - rotate_uv_grid_to_earth(u2d, v2d, *Raw_Grid, u2d_rot, v2d_rot); - if( 'U' == parm_name.at(0) ){ plane = u2d_rot; } - else { plane = v2d_rot; } - - return plane; -} - -//////////////////////////////////////////////////////////////////////// - -DataPlaneArray MetGrib2DataFile::check_derived( const VarInfoGrib2 *vinfo ){ - DataPlaneArray array_ret; - - // if the requested field cannot be derived, bail - if( vinfo->name() != "WIND" && - vinfo->name() != "WDIR" ) { - return array_ret; - } - - // read the data_plane objects for each constituent - DataPlaneArray array_u, array_v; - VarInfoGrib2 vinfo_cons(*vinfo); - vinfo_cons.set_name( ugrd_abbr_str ); - data_plane_array(vinfo_cons, array_u); - vinfo_cons.set_name( vgrd_abbr_str ); - data_plane_array(vinfo_cons, array_v); - - // derive wind speed or direction - if( array_u.n_planes() != array_v.n_planes() ){ - mlog << Warning << "\nMetGrib2DataFile::data_plane_array() -> " - << "when deriving winds, the number of U-wind records (" - << array_u.n_planes() - << ") does not match the number of V-wind record (" - << array_v.n_planes() - << ") for GRIB2 file '" << filename() << "'\n\n"; - - return array_ret; - } - - // loop through each of the data planes - for(int i=0; i < array_u.n_planes(); i++) { - - // check that the current level values match - if( !is_eq(array_u.lower(i), array_v.lower(i)) || - !is_eq(array_u.upper(i), array_v.upper(i)) ){ - - mlog << Warning << "\nMetGrib1DataFile::data_plane_array() -> " - << "when deriving winds for level " << i+1 - << ", the U-wind levels (" << array_u.lower(i) - << ", " << array_u.upper(i) - << ") do not match the V-wind levels (" - << array_v.lower(i) << ", " << array_v.upper(i) - << ") in GRIB file '" << filename() << "'\n\n"; - - return array_ret; - } - - // perform the derivation - DataPlane plane_deriv; - if(vinfo->name() == "WIND") derive_wind(array_u[i], array_v[i], - plane_deriv); - else derive_wdir(array_u[i], array_v[i], - plane_deriv); - - // add the current data plane - array_ret.add(plane_deriv, array_u.lower(i), array_u.upper(i)); - - } - - return array_ret; -} - -//////////////////////////////////////////////////////////////////////// - void MetGrib2DataFile::read_grib2_record_list() { gribfield *gfld; long offset = 0; @@ -947,25 +848,22 @@ void MetGrib2DataFile::read_grib2_record_list() { id << rec->Discipline << "_" << rec->ParmCat << "_" << rec->Parm; // use the index to look up the parameter name - Grib2TableEntry tab; - if( !GribTable.lookup_grib2(rec->Discipline, rec->ParmCat, rec->Parm, - gfld->idsect[2], gfld->idsect[0], gfld->idsect[3], tab) ){ + vector matches; + if(GribTable.lookup_grib2(rec->Discipline, rec->ParmCat, rec->Parm, + (int) gfld->idsect[2], (int) gfld->idsect[0], (int) gfld->idsect[3], + matches) == 0){ mlog << Debug(4) << "MetGrib2DataFile::read_grib2_record_list() - unrecognized GRIB2 " << "field indexes - disc: " << rec->Discipline << ", master table: " << gfld->idsect[2] << ", center: " << gfld->idsect[0] << ", local table: " << gfld->idsect[3] << ", parm_cat: " << rec->ParmCat << ", parm: " << rec->Parm << "\n"; rec->ParmName = str_format("DISC%d_CAT%d_PARM%d", rec->Discipline, rec->ParmCat, rec->Parm); } else { - rec->ParmName = tab.parm_name.text(); + rec->ParmName = matches[0].parm_name.text(); } // add the record to the list RecList.emplace_back(rec); - // build data structure for U/V wind pairs - string rec_mag = build_magic(rec).text(); - NameRecMap[rec_mag] = rec; - g2_free(gfld); // if there are more fields in the current record, read the next one @@ -1682,7 +1580,13 @@ int MetGrib2DataFile::index( VarInfo &vinfo ){ // //////////////////////////////////////////////////////////////////////// -double scaled2dbl(int scale_factor, int scale_value) { +static bool is_grid_relative(const Grib2Record *r) { + return (r->ResCompFlag & 8) != 0; +} + +//////////////////////////////////////////////////////////////////////// + +static double scaled2dbl(int scale_factor, int scale_value) { if (scale_factor == 0) return (double) scale_value; if (scale_factor < 0) return ( scale_value * pow(10.0, -scale_factor) ); return ( scale_value / pow(10.0, scale_factor) ); @@ -1690,7 +1594,7 @@ double scaled2dbl(int scale_factor, int scale_value) { //////////////////////////////////////////////////////////////////////// -int parse_int4(g2int i) { +static int parse_int4(g2int i) { unsigned char c[4]; unsigned long n = i; diff --git a/src/libcode/vx_data2d_grib2/data2d_grib2.h b/src/libcode/vx_data2d_grib2/data2d_grib2.h index ddcc6ce8cc..20c4aae5bc 100644 --- a/src/libcode/vx_data2d_grib2/data2d_grib2.h +++ b/src/libcode/vx_data2d_grib2/data2d_grib2.h @@ -86,17 +86,12 @@ class MetGrib2DataFile : public Met2dDataFile { std::vector RecList; - std::map PairMap; - std::map NameRecMap; - int ScanMode; - // // utilities to read a GRIB2 information // - void find_record_matches( const VarInfoGrib2* vinfo, std::vector &listMatchExact, std::vector &listMatchRange @@ -111,14 +106,6 @@ class MetGrib2DataFile : public Met2dDataFile { void read_grib2_record_list(); - DataPlane check_uv_rotation( const VarInfoGrib2 *vinfo, - Grib2Record *rec, - DataPlane plane - ); - - DataPlaneArray check_derived( const VarInfoGrib2 *vinfo ); - - public: MetGrib2DataFile(); @@ -136,15 +123,15 @@ class MetGrib2DataFile : public Met2dDataFile { // retrieve the first matching data plane - bool data_plane(VarInfo &, DataPlane &); + bool data_plane(VarInfo &, DataPlane &, bool do_winds = true) override; // retrieve all matching data planes - int data_plane_array(VarInfo &, DataPlaneArray &); + int data_plane_array(VarInfo &, DataPlaneArray &, bool do_winds = true) override; // retrieve the index of the first matching record - int index(VarInfo &); + int index(VarInfo &) override; // // do stuff diff --git a/src/libcode/vx_data2d_grib2/var_info_grib2.cc b/src/libcode/vx_data2d_grib2/var_info_grib2.cc index 297694fb3e..8d6b08d1b7 100644 --- a/src/libcode/vx_data2d_grib2/var_info_grib2.cc +++ b/src/libcode/vx_data2d_grib2/var_info_grib2.cc @@ -77,11 +77,8 @@ VarInfoGrib2 & VarInfoGrib2::operator=(const VarInfoGrib2 &f) { /////////////////////////////////////////////////////////////////////////////// -VarInfo *VarInfoGrib2::clone() const { - - VarInfoGrib2 *ret = new VarInfoGrib2(*this); - - return (VarInfo *)ret; +unique_ptr VarInfoGrib2::clone() const { + return unique_ptr(new VarInfoGrib2(*this)); } /////////////////////////////////////////////////////////////////////////////// @@ -324,8 +321,6 @@ bool VarInfoGrib2::set_dict(Dictionary & dict, bool do_exit) { VarInfo::set_dict(dict); - int tab_match = -1; - Grib2TableEntry tab; ConcatString field_name = dict.lookup_string(conf_key_name, false); ConcatString ens_str = dict.lookup_string(conf_key_GRIB_ens, false); int field_disc = dict.lookup_int (conf_key_GRIB2_disc, false); @@ -362,6 +357,8 @@ bool VarInfoGrib2::set_dict(Dictionary & dict, bool do_exit) { handle_config_error(msg, do_exit); } + vector matches; + // if the name is specified, use it if( !field_name.empty() ){ @@ -369,9 +366,10 @@ bool VarInfoGrib2::set_dict(Dictionary & dict, bool do_exit) { set_req_name( field_name.c_str() ); // look up the name in the grib tables - if( !GribTable.lookup_grib2(field_name.c_str(), field_disc, field_parm_cat, field_parm, mtab, cntr, ltab, - tab, tab_match) && - field_name != "PROB" ){ + if(GribTable.lookup_grib2(field_name.c_str(), + field_disc, field_parm_cat, field_parm, + mtab, cntr, ltab, matches) == 0 && + field_name != "PROB"){ ConcatString msg; msg << "\nVarInfoGrib2::set_dict() -> " << "unrecognized GRIB2 field abbreviation '" << field_name @@ -395,8 +393,8 @@ bool VarInfoGrib2::set_dict(Dictionary & dict, bool do_exit) { } // use the specified indexes to look up the field name - if( !GribTable.lookup_grib2(field_disc, field_parm_cat, - field_parm, mtab, cntr, ltab, tab) ){ + if(GribTable.lookup_grib2(field_disc, field_parm_cat, field_parm, + mtab, cntr, ltab, matches) == 0){ ConcatString msg; msg << "\nVarInfoGrib2::set_dict() -> " << "no parameter found with matching " @@ -408,20 +406,20 @@ bool VarInfoGrib2::set_dict(Dictionary & dict, bool do_exit) { handle_config_error(msg, do_exit); } - // use the lookup parameter name - field_name = tab.parm_name; + // use the first lookup parameter name + field_name = matches[0].parm_name; } set_ens (ens_str.c_str()); // set the matched parameter lookup information - set_name ( field_name ); - set_req_name ( field_name.c_str() ); + set_name ( field_name ); + set_req_name ( field_name.c_str() ); if( field_name != "PROB" ){ - set_discipline( tab.index_a ); - set_parm_cat ( tab.index_b ); - set_parm ( tab.index_c ); - set_units ( tab.units.c_str() ); - set_long_name ( tab.full_name.c_str() ); + set_discipline( matches[0].disc ); + set_parm_cat ( matches[0].pcat ); + set_parm ( matches[0].pnum ); + set_units ( matches[0].units.c_str() ); + set_long_name ( matches[0].full_name.c_str() ); } // call the parent to set the level information @@ -453,8 +451,8 @@ bool VarInfoGrib2::set_dict(Dictionary & dict, bool do_exit) { double thresh_hi = dict_prob->lookup_double(conf_key_thresh_hi, false); // look up the probability field abbreviation - if(!GribTable.lookup_grib2(prob_name.c_str(), field_disc, field_parm_cat, - field_parm, mtab, cntr, ltab, tab, tab_match)){ + if(GribTable.lookup_grib2(prob_name.c_str(), field_disc, field_parm_cat, + field_parm, mtab, cntr, ltab, matches) == 0){ ConcatString msg; msg << "\nVarInfoGrib2::set_dict() -> " << "unrecognized GRIB2 probability field abbreviation '" @@ -463,12 +461,12 @@ bool VarInfoGrib2::set_dict(Dictionary & dict, bool do_exit) { return false; } - set_discipline ( tab.index_a ); - set_parm_cat ( tab.index_b ); - set_parm ( tab.index_c ); - set_p_flag ( true ); - set_p_units ( tab.units.c_str() ); - set_units ( "%" ); + set_discipline ( matches[0].disc ); + set_parm_cat ( matches[0].pcat ); + set_parm ( matches[0].pnum ); + set_p_flag ( true ); + set_p_units ( matches[0].units.c_str() ); + set_units ( "%" ); set_prob_info_grib(prob_name, thresh_lo, thresh_hi); @@ -482,9 +480,8 @@ bool VarInfoGrib2::is_precipitation() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsPrecipitation)) { - return(SetAttrIsPrecipitation != 0); - } + int flag = SetAttrIsPrecipitation; + if(!is_bad_data(flag)) return is_flag_set(flag); // // Reference: @@ -507,9 +504,8 @@ bool VarInfoGrib2::is_specific_humidity() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsSpecificHumidity)) { - return(SetAttrIsSpecificHumidity != 0); - } + int flag = SetAttrIsSpecificHumidity; + if(!is_bad_data(flag)) return is_flag_set(flag); // // Reference: @@ -527,9 +523,8 @@ bool VarInfoGrib2::is_u_wind() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsUWind)) { - return(SetAttrIsUWind != 0); - } + int flag = get_wind_flag(SetAttrIsUWind, WindInfo.u_wind); + if(!is_bad_data(flag)) return is_flag_set(flag); // // Reference: @@ -549,9 +544,8 @@ bool VarInfoGrib2::is_v_wind() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsVWind)) { - return(SetAttrIsVWind != 0); - } + int flag = get_wind_flag(SetAttrIsVWind, WindInfo.v_wind); + if(!is_bad_data(flag)) return is_flag_set(flag); // // Reference: @@ -571,9 +565,8 @@ bool VarInfoGrib2::is_wind_speed() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsWindSpeed)) { - return(SetAttrIsWindSpeed != 0); - } + int flag = get_wind_flag(SetAttrIsWindSpeed, WindInfo.wind_speed); + if(!is_bad_data(flag)) return is_flag_set(flag); // // Reference: @@ -593,9 +586,8 @@ bool VarInfoGrib2::is_wind_direction() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsWindDirection)) { - return(SetAttrIsWindDirection != 0); - } + int flag = get_wind_flag(SetAttrIsWindDirection, WindInfo.wind_direction); + if(!is_bad_data(flag)) return is_flag_set(flag); // // Reference: diff --git a/src/libcode/vx_data2d_grib2/var_info_grib2.h b/src/libcode/vx_data2d_grib2/var_info_grib2.h index ad1a0f05b8..91f01b25f4 100644 --- a/src/libcode/vx_data2d_grib2/var_info_grib2.h +++ b/src/libcode/vx_data2d_grib2/var_info_grib2.h @@ -72,7 +72,7 @@ class VarInfoGrib2 : public VarInfo ~VarInfoGrib2() override; VarInfoGrib2(const VarInfoGrib2 &); VarInfoGrib2 & operator=(const VarInfoGrib2 &); - VarInfo *clone() const override; + std::unique_ptr clone() const override; void dump(std::ostream &) const override; 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 1b42a8f5c2..c91443ab57 100644 --- a/src/libcode/vx_data2d_nc_cf/data2d_nc_cf.cc +++ b/src/libcode/vx_data2d_nc_cf/data2d_nc_cf.cc @@ -159,7 +159,7 @@ void MetNcCFDataFile::dump(ostream & out, int depth) const { //////////////////////////////////////////////////////////////////////// int MetNcCFDataFile::add_data_planes_by_time(VarInfo &vinfo, const LevelInfo &level, - DataPlaneArray &plane_array) { + DataPlaneArray &plane_array, bool do_winds) { int n_rec = 0; const auto *vinfo_nc = (VarInfoNcCF *)&vinfo; const NcVarInfo *data_var = get_data_var(vinfo); @@ -192,7 +192,7 @@ int MetNcCFDataFile::add_data_planes_by_time(VarInfo &vinfo, const LevelInfo &le auto time_idx = time_offsets[idx]; if (time_idx < time_cnt) { dimension[t_slot] = time_offsets[idx]; - if (data_plane(vinfo, plane, dimension)) { + if (read_data_plane(vinfo, plane, dimension, do_winds)) { plane_array.add(plane, (double)time_lower, (double)time_upper); n_rec++; if (mlog.verbosity_level() >= nc_cf_debug_level) { @@ -210,7 +210,7 @@ int MetNcCFDataFile::add_data_planes_by_time(VarInfo &vinfo, const LevelInfo &le //////////////////////////////////////////////////////////////////////// int MetNcCFDataFile::add_data_planes_by_z(VarInfo &vinfo, const LevelInfo &level, - DataPlaneArray &plane_array) { + DataPlaneArray &plane_array, bool do_winds) { int n_rec = 0; const auto *vinfo_nc = (VarInfoNcCF *)&vinfo; const NcVarInfo *data_var = get_data_var(vinfo); @@ -242,7 +242,7 @@ int MetNcCFDataFile::add_data_planes_by_z(VarInfo &vinfo, const LevelInfo &level auto z_idx = (int)z_offsets[idx]; if (z_idx < z_cnt) { dimension[z_slot] = z_offsets[idx]; - if (data_plane(vinfo, plane, dimension)) { + if (read_data_plane(vinfo, plane, dimension, do_winds)) { plane_array.add(plane, z_lower, z_upper); n_rec++; } @@ -270,56 +270,61 @@ Grid MetNcCFDataFile::build_grid_from_lat_lon_vars(NcVar *lat_var, NcVar *lon_va //////////////////////////////////////////////////////////////////////// -bool MetNcCFDataFile::data_plane(VarInfo &vinfo, DataPlane &plane) -{ - // Not sure why we do this +bool MetNcCFDataFile::data_plane(VarInfo &vinfo, DataPlane &plane, + bool do_winds) { + auto vinfo_nc = (VarInfoNcCF *) &vinfo; + static const string method_name + = "MetNcCFDataFile::data_plane() -> "; - auto vinfo_nc = (VarInfoNcCF *)&vinfo; - static const string method_name - = "MetNcCFDataFile::data_plane(VarInfo &, DataPlane &) -> "; - - LongArray dimension = vinfo_nc->dimension(); - NcVarInfo *data_var = get_data_var(vinfo); - if (nullptr != data_var) { - int time_dim_slot = data_var->t_slot; - int zdim_slot = data_var->z_slot; - - // set vlevels if needed - _file->set_vlevels(data_var); - - for (int idx=0; idxdimension(); + NcVarInfo *data_var = get_data_var(vinfo); + if (nullptr != data_var) { + int time_dim_slot = data_var->t_slot; + int zdim_slot = data_var->z_slot; + + // set vlevels if needed + _file->set_vlevels(data_var); + + for (int idx=0; idxset_grid_relative_flag(false); + + // Attempt to derive the data + if(!status && do_winds) { + status = derive_winds(vinfo_nc, plane); + } + + return status; } //////////////////////////////////////////////////////////////////////// -bool MetNcCFDataFile::data_plane(VarInfo &vinfo, DataPlane &plane, const LongArray &dimension) +bool MetNcCFDataFile::read_data_plane(VarInfo &vinfo, DataPlane &plane, + const LongArray &dimension, bool do_winds) { - // Not sure why we do this - auto vinfo_nc = (VarInfoNcCF *)&vinfo; static const string method_name - = "MetNcCFDataFile::data_plane(VarInfo &, DataPlane &, LongArray &) -> "; + = "MetNcCFDataFile::read_data_plane() -> "; Grid grid_attr = vinfo.grid_attr(); _file->update_grid(grid_attr); @@ -368,7 +373,10 @@ bool MetNcCFDataFile::data_plane(VarInfo &vinfo, DataPlane &plane, const LongArr } } - status = process_data_plane(&vinfo, plane); + // Handle wind rotation + if(status && do_winds) status = rotate_winds(&vinfo, plane); + + if(status) status = process_data_plane(&vinfo, plane); // Set the VarInfo object's name, long_name, level, and units strings @@ -393,10 +401,10 @@ bool MetNcCFDataFile::data_plane(VarInfo &vinfo, DataPlane &plane, const LongArr //////////////////////////////////////////////////////////////////////// int MetNcCFDataFile::data_plane_array(VarInfo &vinfo, - DataPlaneArray &plane_array) { + DataPlaneArray &plane_array, + bool do_winds) { int n_rec = 0; DataPlane plane; - bool status = false; static const string method_name = "MetNcCFDataFile::data_plane_array(VarInfo &, DataPlaneArray &) -> "; @@ -420,16 +428,24 @@ int MetNcCFDataFile::data_plane_array(VarInfo &vinfo, cur_time_index = cur_z_index = 0; if (0 <= t_dim_slot && range_flag == dimension[t_dim_slot] && level.type() == LevelType_Time) { - n_rec = add_data_planes_by_time(vinfo, level, plane_array); + n_rec = add_data_planes_by_time(vinfo, level, plane_array, do_winds); } else if (0 <= z_dim_slot && range_flag == dimension[z_dim_slot] && level.type() == LevelType_Pres) { - n_rec = add_data_planes_by_z(vinfo, level, plane_array); + n_rec = add_data_planes_by_z(vinfo, level, plane_array, do_winds); } - else if (data_plane(vinfo, plane)) { + else if (data_plane(vinfo, plane, do_winds)) { plane_array.add(plane, bad_data_int, bad_data_int); n_rec++; } + // Assume that CF-compliant NetCDF winds are earth-relative + vinfo_nc->set_grid_relative_flag(true); + + // Attempt to derive the data + if(n_rec == 0 && do_winds) { + derive_winds(vinfo_nc, plane_array); + } + return n_rec; } diff --git a/src/libcode/vx_data2d_nc_cf/data2d_nc_cf.h b/src/libcode/vx_data2d_nc_cf/data2d_nc_cf.h index c8a15658d0..eec2fa4585 100644 --- a/src/libcode/vx_data2d_nc_cf/data2d_nc_cf.h +++ b/src/libcode/vx_data2d_nc_cf/data2d_nc_cf.h @@ -35,16 +35,16 @@ class MetNcCFDataFile : public Met2dDataFile { MetNcCFDataFile & operator=(const MetNcCFDataFile &); int add_data_planes_by_time(VarInfo &vinfo, const LevelInfo &level, - DataPlaneArray &plane_array); + DataPlaneArray &plane_array, bool do_winds); int add_data_planes_by_z(VarInfo &vinfo, const LevelInfo &level, - DataPlaneArray &plane_array); + DataPlaneArray &plane_array, bool do_winds); LongArray collect_time_offsets(VarInfo &vinfo); LongArray collect_z_offsets(VarInfo &vinfo); long convert_generic_to_offset(double value, const std::string& dim_name, std::vector values); long convert_time_to_offset(double time_value) const; long convert_z_to_offset(double z_value, const NcVarInfo* data_var); - bool data_plane(VarInfo &, DataPlane &, const LongArray &dimension); + bool read_data_plane(VarInfo &, DataPlane &, const LongArray &dimension, bool do_winds); void error_message(const bool is_dim_time, const int error_code, const double _lower, const double _upper, const long _value, const ConcatString &var_name, @@ -99,15 +99,15 @@ class MetNcCFDataFile : public Met2dDataFile { // retrieve the first matching data plane - bool data_plane(VarInfo &, DataPlane &); + bool data_plane(VarInfo &, DataPlane &, bool do_winds = true) override; // retrieve all matching data planes - int data_plane_array(VarInfo &, DataPlaneArray &); + int data_plane_array(VarInfo &, DataPlaneArray &, bool do_winds = true) override; // retrieve the index of the first matching record - int index(VarInfo &); + int index(VarInfo &) override; // // do stuff diff --git a/src/libcode/vx_data2d_nc_cf/var_info_nc_cf.cc b/src/libcode/vx_data2d_nc_cf/var_info_nc_cf.cc index 52e550f06e..76ebc3adf9 100644 --- a/src/libcode/vx_data2d_nc_cf/var_info_nc_cf.cc +++ b/src/libcode/vx_data2d_nc_cf/var_info_nc_cf.cc @@ -76,11 +76,8 @@ VarInfoNcCF & VarInfoNcCF::operator=(const VarInfoNcCF &f) { /////////////////////////////////////////////////////////////////////////////// -VarInfo *VarInfoNcCF::clone() const { - - VarInfoNcCF *ret = new VarInfoNcCF(*this); - - return ret; +unique_ptr VarInfoNcCF::clone() const { + return unique_ptr(new VarInfoNcCF(*this)); } /////////////////////////////////////////////////////////////////////////////// @@ -383,9 +380,8 @@ bool VarInfoNcCF::is_precipitation() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsPrecipitation)) { - return(SetAttrIsPrecipitation != 0); - } + int flag = SetAttrIsPrecipitation; + if(!is_bad_data(flag)) return is_flag_set(flag); // // Check to see if the VarInfo name begins with the GRIB code abbreviation @@ -403,9 +399,8 @@ bool VarInfoNcCF::is_specific_humidity() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsSpecificHumidity)) { - return(SetAttrIsSpecificHumidity != 0); - } + int flag = SetAttrIsSpecificHumidity; + if(!is_bad_data(flag)) return is_flag_set(flag); // // Check to see if the VarInfo name begins with the GRIB code abbreviation @@ -423,9 +418,8 @@ bool VarInfoNcCF::is_u_wind() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsUWind)) { - return(SetAttrIsUWind != 0); - } + int flag = get_wind_flag(SetAttrIsUWind, WindInfo.u_wind); + if(!is_bad_data(flag)) return is_flag_set(flag); return is_grib_code_abbr_match(Name, ugrd_grib_code); } @@ -437,9 +431,8 @@ bool VarInfoNcCF::is_v_wind() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsVWind)) { - return(SetAttrIsVWind != 0); - } + int flag = get_wind_flag(SetAttrIsVWind, WindInfo.v_wind); + if(!is_bad_data(flag)) return is_flag_set(flag); return is_grib_code_abbr_match(Name, vgrd_grib_code); } @@ -451,9 +444,8 @@ bool VarInfoNcCF::is_wind_speed() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsWindSpeed)) { - return(SetAttrIsWindSpeed != 0); - } + int flag = get_wind_flag(SetAttrIsWindSpeed, WindInfo.wind_speed); + if(!is_bad_data(flag)) return is_flag_set(flag); return is_grib_code_abbr_match(Name, wind_grib_code); } @@ -465,9 +457,8 @@ bool VarInfoNcCF::is_wind_direction() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsWindDirection)) { - return(SetAttrIsWindDirection != 0); - } + int flag = get_wind_flag(SetAttrIsWindDirection, WindInfo.wind_direction); + if(!is_bad_data(flag)) return is_flag_set(flag); return is_grib_code_abbr_match(Name, wdir_grib_code); } diff --git a/src/libcode/vx_data2d_nc_cf/var_info_nc_cf.h b/src/libcode/vx_data2d_nc_cf/var_info_nc_cf.h index 81f33b7db6..514ad11356 100644 --- a/src/libcode/vx_data2d_nc_cf/var_info_nc_cf.h +++ b/src/libcode/vx_data2d_nc_cf/var_info_nc_cf.h @@ -45,7 +45,7 @@ class VarInfoNcCF : public VarInfo ~VarInfoNcCF() override; VarInfoNcCF(const VarInfoNcCF &); VarInfoNcCF & operator=(const VarInfoNcCF &); - VarInfo *clone() const override; + std::unique_ptr clone() const override; void dump(std::ostream &) const override; void clear(); diff --git a/src/libcode/vx_data2d_nc_met/data2d_nc_met.cc b/src/libcode/vx_data2d_nc_met/data2d_nc_met.cc index 616abe0b26..6a1a72fc48 100644 --- a/src/libcode/vx_data2d_nc_met/data2d_nc_met.cc +++ b/src/libcode/vx_data2d_nc_met/data2d_nc_met.cc @@ -209,7 +209,8 @@ void MetNcMetDataFile::set_range_azimuth_times(int i_track_point, DataPlane &pla //////////////////////////////////////////////////////////////////////// -bool MetNcMetDataFile::data_plane(VarInfo &vinfo, DataPlane &plane) { +bool MetNcMetDataFile::data_plane(VarInfo &vinfo, DataPlane &plane, + bool do_winds) { bool status = false; ConcatString req_time_str, data_time_str; VarInfoNcMet * vinfo_nc = (VarInfoNcMet *) &vinfo; @@ -237,6 +238,14 @@ bool MetNcMetDataFile::data_plane(VarInfo &vinfo, DataPlane &plane) { vinfo_nc->dimension(), plane, info); + // Assume that winds in MET output files are earth-relative + vinfo_nc->set_grid_relative_flag(false); + + // Attempt to derive the data + if(!status && do_winds) { + status = derive_winds(vinfo_nc, plane); + } + // Check that the times match those requested if(status) { @@ -277,7 +286,10 @@ bool MetNcMetDataFile::data_plane(VarInfo &vinfo, DataPlane &plane) { status = false; } - status = process_data_plane(&vinfo, plane); + // Handle wind rotation + if(status && do_winds) status = rotate_winds(&vinfo, plane); + + if(status) status = process_data_plane(&vinfo, plane); // Set the VarInfo object's name, long_name, level, and units strings if(info->name_att.length() > 0) vinfo.set_name(info->name_att); @@ -293,7 +305,8 @@ bool MetNcMetDataFile::data_plane(VarInfo &vinfo, DataPlane &plane) { //////////////////////////////////////////////////////////////////////// int MetNcMetDataFile::data_plane_array(VarInfo &vinfo, - DataPlaneArray &plane_array) { + DataPlaneArray &plane_array, + bool do_winds) { bool status = false; int n_rec = 0; DataPlane plane; @@ -302,7 +315,7 @@ int MetNcMetDataFile::data_plane_array(VarInfo &vinfo, plane_array.clear(); // Can only read a single 2D data plane from a MET NetCDF file - status = data_plane(vinfo, plane); + status = data_plane(vinfo, plane, do_winds); // Add the data plane to the DataPlaneArray with no level values if(status) { diff --git a/src/libcode/vx_data2d_nc_met/data2d_nc_met.h b/src/libcode/vx_data2d_nc_met/data2d_nc_met.h index 4174f58e4e..da6e897983 100644 --- a/src/libcode/vx_data2d_nc_met/data2d_nc_met.h +++ b/src/libcode/vx_data2d_nc_met/data2d_nc_met.h @@ -62,15 +62,15 @@ class MetNcMetDataFile : public Met2dDataFile { // retrieve the first matching data plane - bool data_plane(VarInfo &, DataPlane &); + bool data_plane(VarInfo &, DataPlane &, bool do_winds = true) override; // retrieve all matching data planes - int data_plane_array(VarInfo &, DataPlaneArray &); + int data_plane_array(VarInfo &, DataPlaneArray &, bool do_winds = true) override; // retrieve the index of the first matching record - int index(VarInfo &); + int index(VarInfo &) override; // // do stuff diff --git a/src/libcode/vx_data2d_nc_met/var_info_nc_met.cc b/src/libcode/vx_data2d_nc_met/var_info_nc_met.cc index ea10b2df40..2fa0e82c0c 100644 --- a/src/libcode/vx_data2d_nc_met/var_info_nc_met.cc +++ b/src/libcode/vx_data2d_nc_met/var_info_nc_met.cc @@ -72,11 +72,8 @@ VarInfoNcMet & VarInfoNcMet::operator=(const VarInfoNcMet &f) { /////////////////////////////////////////////////////////////////////////////// -VarInfo *VarInfoNcMet::clone() const { - - VarInfoNcMet *ret = new VarInfoNcMet(*this); - - return (VarInfo *)ret; +unique_ptr VarInfoNcMet::clone() const { + return unique_ptr(new VarInfoNcMet(*this)); } /////////////////////////////////////////////////////////////////////////////// @@ -266,9 +263,8 @@ bool VarInfoNcMet::is_precipitation() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsPrecipitation)) { - return(SetAttrIsPrecipitation != 0); - } + int flag = SetAttrIsPrecipitation; + if(!is_bad_data(flag)) return is_flag_set(flag); // // Check to see if the VarInfo name begins with the GRIB code abbreviation @@ -286,9 +282,8 @@ bool VarInfoNcMet::is_specific_humidity() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsSpecificHumidity)) { - return(SetAttrIsSpecificHumidity != 0); - } + int flag = SetAttrIsSpecificHumidity; + if(!is_bad_data(flag)) return is_flag_set(flag); // // Check to see if the VarInfo name begins with the GRIB code abbreviation @@ -306,9 +301,8 @@ bool VarInfoNcMet::is_u_wind() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsUWind)) { - return(SetAttrIsUWind != 0); - } + int flag = get_wind_flag(SetAttrIsUWind, WindInfo.u_wind); + if(!is_bad_data(flag)) return is_flag_set(flag); return is_grib_code_abbr_match(Name, ugrd_grib_code); } @@ -320,10 +314,9 @@ bool VarInfoNcMet::is_v_wind() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsVWind)) { - return(SetAttrIsVWind != 0); - } - + int flag = get_wind_flag(SetAttrIsVWind, WindInfo.v_wind); + if(!is_bad_data(flag)) return is_flag_set(flag); + return is_grib_code_abbr_match(Name, vgrd_grib_code); } @@ -334,9 +327,8 @@ bool VarInfoNcMet::is_wind_speed() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsWindSpeed)) { - return(SetAttrIsWindSpeed != 0); - } + int flag = get_wind_flag(SetAttrIsWindSpeed, WindInfo.wind_speed); + if(!is_bad_data(flag)) return is_flag_set(flag); return is_grib_code_abbr_match(Name, wind_grib_code); } @@ -348,9 +340,8 @@ bool VarInfoNcMet::is_wind_direction() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsWindDirection)) { - return(SetAttrIsWindDirection != 0); - } + int flag = get_wind_flag(SetAttrIsWindDirection, WindInfo.wind_direction); + if(!is_bad_data(flag)) return is_flag_set(flag); return is_grib_code_abbr_match(Name, wdir_grib_code); } diff --git a/src/libcode/vx_data2d_nc_met/var_info_nc_met.h b/src/libcode/vx_data2d_nc_met/var_info_nc_met.h index 4cdbb5948a..42dba7d00b 100644 --- a/src/libcode/vx_data2d_nc_met/var_info_nc_met.h +++ b/src/libcode/vx_data2d_nc_met/var_info_nc_met.h @@ -41,7 +41,7 @@ class VarInfoNcMet : public VarInfo ~VarInfoNcMet() override; VarInfoNcMet(const VarInfoNcMet &); VarInfoNcMet & operator=(const VarInfoNcMet &); - VarInfo *clone() const override; + std::unique_ptr clone() const override; void dump(std::ostream &) const override; void clear(); diff --git a/src/libcode/vx_data2d_nc_wrf/data2d_nc_wrf.cc b/src/libcode/vx_data2d_nc_wrf/data2d_nc_wrf.cc index 037d3dc489..20130822e3 100644 --- a/src/libcode/vx_data2d_nc_wrf/data2d_nc_wrf.cc +++ b/src/libcode/vx_data2d_nc_wrf/data2d_nc_wrf.cc @@ -6,10 +6,8 @@ // ** P.O.Box 3000, Boulder, Colorado, 80307-3000, USA // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* - //////////////////////////////////////////////////////////////////////// - #include #include #include @@ -23,7 +21,6 @@ using namespace std; - //////////////////////////////////////////////////////////////////////// // // Code for class MetNcWrfDataFile @@ -121,9 +118,10 @@ void MetNcWrfDataFile::dump(ostream & out, int depth) const { //////////////////////////////////////////////////////////////////////// -bool MetNcWrfDataFile::data_plane(VarInfo &vinfo, DataPlane &plane) { +bool MetNcWrfDataFile::data_plane(VarInfo &vinfo, DataPlane &plane, + bool do_winds) { bool status = false; - double pressure; + double pressure = bad_data_double; ConcatString level_str; VarInfoNcWrf * vinfo_nc = (VarInfoNcWrf *) &vinfo; NcVarInfo *info = (NcVarInfo *) nullptr; @@ -131,26 +129,36 @@ bool MetNcWrfDataFile::data_plane(VarInfo &vinfo, DataPlane &plane) { // Initialize the data plane plane.clear(); - // Read the data + // Assume that all WRF winds are grid-relative + vinfo_nc->set_grid_relative_flag(true); + + // Read the data if found WrfNc->get_nc_var_info(vinfo_nc->req_name().c_str(), info); - LongArray dimension = vinfo_nc->dimension(); - int dim_count = dimension.n_elements(); - for (int k=0; kvar, k)); - NcVarInfo *var_info = find_var_info_by_dim_name(WrfNc->Var, dim_name, - WrfNc->Nvars); - if (var_info) { - long new_offset = get_index_at_nc_data(var_info->var, - vinfo_nc->dim_value(k), - dim_name, (k == info->t_slot)); - if (new_offset != bad_data_int) dimension[k] = new_offset; + if(info) { + LongArray dimension = vinfo_nc->dimension(); + int dim_count = dimension.n_elements(); + for (int k=0; kvar, k)); + NcVarInfo *var_info = find_var_info_by_dim_name(WrfNc->Var, dim_name, + WrfNc->Nvars); + if (var_info) { + long new_offset = get_index_at_nc_data(var_info->var, + vinfo_nc->dim_value(k), + dim_name, (k == info->t_slot)); + if (new_offset != bad_data_int) dimension[k] = new_offset; + } } } + + status = WrfNc->data(vinfo_nc->req_name().c_str(), + dimension, plane, pressure, info); } - status = WrfNc->data(vinfo_nc->req_name().c_str(), - dimension, plane, pressure, info); + // Attempt to derive the data + if(!status && do_winds) { + status = derive_winds(vinfo_nc, plane); + } // Check that the times match those requested if(status) { @@ -177,13 +185,19 @@ bool MetNcWrfDataFile::data_plane(VarInfo &vinfo, DataPlane &plane) { status = false; } - status = process_data_plane(&vinfo, plane); + // Handle wind rotation + if(status && do_winds) status = rotate_winds(&vinfo, plane); + + if(status) status = process_data_plane(&vinfo, plane); // Set the VarInfo object's name, long_name, and units strings - if(info->name_att.length() > 0) vinfo.set_name(info->name_att); - else vinfo.set_name(info->name); - if(info->long_name_att.length() > 0) vinfo.set_long_name(info->long_name_att.c_str()); - if(info->units_att.length() > 0) vinfo.set_units(info->units_att.c_str()); + // Note that info is null for derived fields + if(info) { + if(info->name_att.length() > 0) vinfo.set_name(info->name_att); + else vinfo.set_name(info->name); + if(info->long_name_att.length() > 0) vinfo.set_long_name(info->long_name_att.c_str()); + if(info->units_att.length() > 0) vinfo.set_units(info->units_att.c_str()); + } // Set the VarInfo object's level string for pressure levels if(!is_bad_data(pressure)) { @@ -198,11 +212,9 @@ bool MetNcWrfDataFile::data_plane(VarInfo &vinfo, DataPlane &plane) { //////////////////////////////////////////////////////////////////////// int MetNcWrfDataFile::data_plane_array(VarInfo &vinfo, - DataPlaneArray &plane_array) { - int i, i_dim, n_level, status, lower, upper; - ConcatString level_str; - double pressure, min_level, max_level; - bool found = false; + DataPlaneArray &plane_array, + bool do_winds) { + double pressure; VarInfoNcWrf *vinfo_nc = (VarInfoNcWrf *) &vinfo; LongArray dim = vinfo_nc->dimension(); NcVarInfo *info = (NcVarInfo *) nullptr; @@ -214,6 +226,8 @@ int MetNcWrfDataFile::data_plane_array(VarInfo &vinfo, plane_array.clear(); // Find the dimension that has the range flag set + int i_dim; + bool found = false; for(i_dim=0; i_dimdata(vinfo_nc->req_name().c_str(), - cur_dim, cur_plane, pressure, info); + bool status = WrfNc->data(vinfo_nc->req_name().c_str(), + cur_dim, cur_plane, pressure, info); + + // Assume that WRF winds are grid-relative + vinfo_nc->set_grid_relative_flag(true); + + // Attempt to derive the data + if(!status && do_winds) { + status = derive_winds(vinfo_nc, cur_plane); + } // Check that the times match those requested if(status) { @@ -270,12 +292,18 @@ int MetNcWrfDataFile::data_plane_array(VarInfo &vinfo, status = false; } - status = process_data_plane(&vinfo, cur_plane); - // Add current plane to the data plane array plane_array.add(cur_plane, pressure, pressure); } + // Handle wind rotation + if(do_winds) rotate_winds(&vinfo, plane_array); + + // post-process each data plane + for(int j=0; jname_att.length() > 0) vinfo.set_name(info->name_att); else vinfo.set_name(info->name); if(info->long_name_att.length() > 0) vinfo.set_long_name(info->long_name_att.c_str()); @@ -295,7 +324,10 @@ int MetNcWrfDataFile::data_plane_array(VarInfo &vinfo, // Set the VarInfo object's level string for pressure levels // Check for a missing value, a single pressure level, or a range // of pressure levels + double min_level; + double max_level; plane_array.level_range(min_level, max_level); + ConcatString level_str; if(is_bad_data(min_level) || is_bad_data(max_level)) level_str << cs_erase << na_str; else if(is_eq(min_level, max_level)) level_str << cs_erase << "P" << nint(min_level); diff --git a/src/libcode/vx_data2d_nc_wrf/data2d_nc_wrf.h b/src/libcode/vx_data2d_nc_wrf/data2d_nc_wrf.h index b23e8f6447..4512d49a61 100644 --- a/src/libcode/vx_data2d_nc_wrf/data2d_nc_wrf.h +++ b/src/libcode/vx_data2d_nc_wrf/data2d_nc_wrf.h @@ -61,15 +61,15 @@ class MetNcWrfDataFile : public Met2dDataFile { // retrieve the first matching data plane - bool data_plane(VarInfo &, DataPlane &); + bool data_plane(VarInfo &, DataPlane &, bool do_winds = true) override; // retrieve all matching data planes - int data_plane_array(VarInfo &, DataPlaneArray &); + int data_plane_array(VarInfo &, DataPlaneArray &, bool do_winds = true) override; // retrieve the index of the first matching record - int index(VarInfo &); + int index(VarInfo &) override; // // do stuff diff --git a/src/libcode/vx_data2d_nc_wrf/var_info_nc_wrf.cc b/src/libcode/vx_data2d_nc_wrf/var_info_nc_wrf.cc index e05a4c0f32..2dfe83dcd1 100644 --- a/src/libcode/vx_data2d_nc_wrf/var_info_nc_wrf.cc +++ b/src/libcode/vx_data2d_nc_wrf/var_info_nc_wrf.cc @@ -72,11 +72,8 @@ VarInfoNcWrf & VarInfoNcWrf::operator=(const VarInfoNcWrf &f) { /////////////////////////////////////////////////////////////////////////////// -VarInfo *VarInfoNcWrf::clone() const { - - VarInfoNcWrf *ret = new VarInfoNcWrf(*this); - - return (VarInfo *)ret; +unique_ptr VarInfoNcWrf::clone() const { + return unique_ptr(new VarInfoNcWrf(*this)); } /////////////////////////////////////////////////////////////////////////////// @@ -303,9 +300,8 @@ bool VarInfoNcWrf::is_precipitation() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsPrecipitation)) { - return(SetAttrIsPrecipitation != 0); - } + int flag = SetAttrIsPrecipitation; + if(!is_bad_data(flag)) return is_flag_set(flag); // // Check to see if the VarInfo name matches any of expected Pinterp @@ -323,9 +319,8 @@ bool VarInfoNcWrf::is_specific_humidity() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsSpecificHumidity)) { - return(SetAttrIsSpecificHumidity != 0); - } + int flag = SetAttrIsSpecificHumidity; + if(!is_bad_data(flag)) return is_flag_set(flag); // // Check to see if the VarInfo name matches any of expected Pinterp @@ -343,14 +338,11 @@ bool VarInfoNcWrf::is_u_wind() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsUWind)) { - return(SetAttrIsUWind != 0); - } + int flag = get_wind_flag(SetAttrIsUWind, WindInfo.u_wind); + if(!is_bad_data(flag)) return is_flag_set(flag); // Check if the VarInfo name is U or U where is an integer - if( regex_match (Name.c_str(), regex("^U[0-9]*$") )) { - return true; - } + if(regex_match(Name.c_str(), regex("^U[0-9]*$"))) return true; // // Check to see if the VarInfo name matches any of expected Pinterp @@ -368,14 +360,11 @@ bool VarInfoNcWrf::is_v_wind() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsVWind)) { - return(SetAttrIsVWind != 0); - } + int flag = get_wind_flag(SetAttrIsVWind, WindInfo.v_wind); + if(!is_bad_data(flag)) return is_flag_set(flag); // Check if the VarInfo name is V or V where is an integer - if( regex_match (Name.c_str(), regex("^V[0-9]*$") )) { - return true; - } + if(regex_match(Name.c_str(), regex("^V[0-9]*$"))) return true; // // Check to see if the VarInfo name matches any of expected Pinterp @@ -393,9 +382,8 @@ bool VarInfoNcWrf::is_wind_speed() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsWindSpeed)) { - return(SetAttrIsWindSpeed != 0); - } + int flag = get_wind_flag(SetAttrIsWindSpeed, WindInfo.wind_speed); + if(!is_bad_data(flag)) return is_flag_set(flag); // // Check to see if the VarInfo name matches any of expected Pinterp @@ -413,25 +401,10 @@ bool VarInfoNcWrf::is_wind_direction() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsWindDirection)) { - return(SetAttrIsWindDirection != 0); - } + int flag = get_wind_flag(SetAttrIsWindDirection, WindInfo.wind_direction); + if(!is_bad_data(flag)) return is_flag_set(flag); return false; } - -/////////////////////////////////////////////////////////////////////////////// - -bool VarInfoNcWrf::is_grid_relative() const { - - // - // Check to see if the VarInfo name matches any of expected Pinterp - // variables that should be rotated from grid-relative to earth-relative. - // - return has_prefix(pinterp_grid_relative_names, - n_pinterp_grid_relative_names, - Name.c_str()); -} - /////////////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_data2d_nc_wrf/var_info_nc_wrf.h b/src/libcode/vx_data2d_nc_wrf/var_info_nc_wrf.h index 6acae0afef..10b1f00178 100644 --- a/src/libcode/vx_data2d_nc_wrf/var_info_nc_wrf.h +++ b/src/libcode/vx_data2d_nc_wrf/var_info_nc_wrf.h @@ -138,32 +138,6 @@ static const int n_pinterp_v_wind_names = /////////////////////////////////////////////////////////////////////////////// -// -// List of wind variable names that should be rotated from grid-relative -// to earth-relative. MET is not able to read winds from Pinterp files since -// they are defined on a staggered grid. If the code is enhanced to do so, -// the data in these variables should be rotated from grid-relative to -// earth-relative prior to verifying. -// Taken from the WRF version 3.2 Registry.EM file -// - -static const char *pinterp_grid_relative_names[] = { - "UU", // x-wind component, m s-1 - "UZ0", // U WIND COMPONENT AT ZNT, m s-1 - "VV", // y-wind component, m s-1 - "VZ0" // V WIND COMPONENT AT ZNT, m s-1 -}; - -// -// Number of Pinterp grid relative variable names -// - -static const int n_pinterp_grid_relative_names = - sizeof(pinterp_grid_relative_names)/ - sizeof(*pinterp_grid_relative_names); - -/////////////////////////////////////////////////////////////////////////////// - // // List of wind speed variable names // Taken from the WRF version 3.2 Registry.EM file @@ -204,7 +178,7 @@ class VarInfoNcWrf : public VarInfo ~VarInfoNcWrf() override; VarInfoNcWrf(const VarInfoNcWrf &); VarInfoNcWrf & operator=(const VarInfoNcWrf &); - VarInfo *clone() const override; + std::unique_ptr clone() const override; void dump(std::ostream &) const override; void clear(); @@ -242,7 +216,6 @@ class VarInfoNcWrf : public VarInfo bool is_v_wind() const override; bool is_wind_speed() const override; bool is_wind_direction() const override; - bool is_grid_relative() const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_data2d_nc_wrf/wrf_file.cc b/src/libcode/vx_data2d_nc_wrf/wrf_file.cc index 3eb71979fc..9c2901333e 100644 --- a/src/libcode/vx_data2d_nc_wrf/wrf_file.cc +++ b/src/libcode/vx_data2d_nc_wrf/wrf_file.cc @@ -537,20 +537,20 @@ if ( !args_ok(a) ) { int dim_count = var->getDimCount(); if ( dim_count != a.n_elements() ) { - mlog << Error << "\n" << method_name + mlog << Warning << "\n" << method_name << "needed " << dim_count << " arguments for variable " << (GET_NC_NAME_P(var)) << ", got " << (a.n_elements()) << "\n\n"; - exit ( 1 ); + return bad_data_double; } if (dim_count >= max_wrf_args ) { - mlog << Error << "\n" << method_name + mlog << Warning << "\n" << method_name << " too may arguments for variable \"" << (GET_NC_NAME_P(var)) << "\"\n\n"; - exit ( 1 ); + return bad_data_double; } @@ -604,7 +604,7 @@ if ( dim_count != a.n_elements() ) { << "needed " << dim_count << " arguments for variable " << var_name << ", got " << a.n_elements() << "\n\n"; - exit ( 1 ); + return false; } diff --git a/src/libcode/vx_data2d_python/data2d_python.cc b/src/libcode/vx_data2d_python/data2d_python.cc index b585ee2c60..2996333487 100644 --- a/src/libcode/vx_data2d_python/data2d_python.cc +++ b/src/libcode/vx_data2d_python/data2d_python.cc @@ -295,7 +295,8 @@ return; //////////////////////////////////////////////////////////////////////// -bool MetPythonDataFile::data_plane(VarInfo &vinfo, DataPlane &plane) +bool MetPythonDataFile::data_plane(VarInfo &vinfo, DataPlane &plane, + bool do_winds) { @@ -332,7 +333,9 @@ if ( status ) { plane = Plane; - status = process_data_plane(&vinfo, plane); + if(status && do_winds) status = rotate_winds(&vinfo, plane); + + if(status) status = process_data_plane(&vinfo, plane); } @@ -360,7 +363,9 @@ return true; //////////////////////////////////////////////////////////////////////// -int MetPythonDataFile::data_plane_array(VarInfo &vinfo, DataPlaneArray &plane_array) +int MetPythonDataFile::data_plane_array(VarInfo &vinfo, + DataPlaneArray &plane_array, + bool do_winds) { @@ -398,7 +403,9 @@ if ( status ) { plane = Plane; - status = process_data_plane(&vinfo, plane); + if(status && do_winds) status = rotate_winds(&vinfo, plane); + + if(status) status = process_data_plane(&vinfo, plane); } diff --git a/src/libcode/vx_data2d_python/data2d_python.h b/src/libcode/vx_data2d_python/data2d_python.h index 623967fd49..440838c444 100644 --- a/src/libcode/vx_data2d_python/data2d_python.h +++ b/src/libcode/vx_data2d_python/data2d_python.h @@ -86,11 +86,11 @@ class MetPythonDataFile : public Met2dDataFile { void dump (std::ostream &, int depth = 0) const; - bool data_plane(VarInfo &, DataPlane &); + bool data_plane(VarInfo &, DataPlane &, bool do_winds = true) override; - int data_plane_array(VarInfo &, DataPlaneArray &); + int data_plane_array(VarInfo &, DataPlaneArray &, bool do_winds = true) override; - int index(VarInfo &); + int index(VarInfo &) override; bool data_plane(DataPlane &); diff --git a/src/libcode/vx_data2d_python/var_info_python.cc b/src/libcode/vx_data2d_python/var_info_python.cc index 5ad44b5737..cefa0bac8d 100644 --- a/src/libcode/vx_data2d_python/var_info_python.cc +++ b/src/libcode/vx_data2d_python/var_info_python.cc @@ -75,11 +75,8 @@ VarInfoPython & VarInfoPython::operator=(const VarInfoPython &f) { /////////////////////////////////////////////////////////////////////////////// -VarInfo *VarInfoPython::clone() const { - - VarInfoPython *ret = new VarInfoPython(*this); - - return (VarInfo *)ret; +unique_ptr VarInfoPython::clone() const { + return unique_ptr(new VarInfoPython(*this)); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/libcode/vx_data2d_python/var_info_python.h b/src/libcode/vx_data2d_python/var_info_python.h index 0bf98532e6..52c1527def 100644 --- a/src/libcode/vx_data2d_python/var_info_python.h +++ b/src/libcode/vx_data2d_python/var_info_python.h @@ -41,7 +41,7 @@ class VarInfoPython : public VarInfo ~VarInfoPython(); VarInfoPython(const VarInfoPython &); VarInfoPython & operator=(const VarInfoPython &); - VarInfo *clone() const; + std::unique_ptr clone() const override; void dump(std::ostream &) const; void clear(); diff --git a/src/libcode/vx_data2d_ugrid/data2d_ugrid.cc b/src/libcode/vx_data2d_ugrid/data2d_ugrid.cc index a8c75a83dc..303cd2cfeb 100644 --- a/src/libcode/vx_data2d_ugrid/data2d_ugrid.cc +++ b/src/libcode/vx_data2d_ugrid/data2d_ugrid.cc @@ -163,12 +163,12 @@ void MetUGridDataFile::dump(ostream & out, int depth) const { //////////////////////////////////////////////////////////////////////// -bool MetUGridDataFile::data_plane(VarInfo &vinfo, DataPlane &plane) -{ +bool MetUGridDataFile::data_plane(VarInfo &vinfo, DataPlane &plane, + bool do_winds) { bool status = false; auto data_var = (NcVarInfo *)nullptr; static const string method_name - = "MetUGridDataFile::data_plane(VarInfo &, DataPlane &) -> "; + = "MetUGridDataFile::data_plane(VarInfo &, DataPlane &, bool) -> "; // Initialize the data plane @@ -177,8 +177,17 @@ bool MetUGridDataFile::data_plane(VarInfo &vinfo, DataPlane &plane) ConcatString req_name = vinfo.req_name(); data_var = _file->find_by_name(req_name.c_str()); if (nullptr != data_var) { + // Read the data - status = data_plane(vinfo, plane, data_var); + status = data_plane(vinfo, plane, data_var, do_winds); + + // Assume that UGRID winds are earth-relative + vinfo.set_grid_relative_flag(false); + + // Attempt to derive the data + if(!status && do_winds) { + status = derive_winds(&vinfo, plane); + } } else { LevelInfo level = vinfo.level(); @@ -191,7 +200,7 @@ bool MetUGridDataFile::data_plane(VarInfo &vinfo, DataPlane &plane) int vlevel = extract_vlevels(req_name, nc_var_info->name.c_str()); if (vlevel >= lvl_lower && vlevel <= lvl_upper) { vinfo.set_req_name(nc_var_info->name.c_str()); - status = data_plane(vinfo, plane, nc_var_info); + status = data_plane(vinfo, plane, nc_var_info, do_winds); if (status) { mlog << Debug(5) << method_name << "Found range match for VarInfo \"" << req_name @@ -213,7 +222,8 @@ bool MetUGridDataFile::data_plane(VarInfo &vinfo, DataPlane &plane) // The requested variable name is the prefix of the actual variable name. // -bool MetUGridDataFile::data_plane(VarInfo &vinfo, DataPlane &plane, const NcVarInfo *data_var) +bool MetUGridDataFile::data_plane(VarInfo &vinfo, DataPlane &plane, + const NcVarInfo *data_var, bool do_winds) { // Not sure why we do this bool status = false; @@ -221,7 +231,7 @@ bool MetUGridDataFile::data_plane(VarInfo &vinfo, DataPlane &plane, const NcVarI VarInfoUGrid *vinfo_nc = (VarInfoUGrid *)&vinfo; static const string method_name_s = "MetUGridDataFile::data_plane() -> "; static const string method_name - = "MetUGridDataFile::data_plane(VarInfo &, DataPlane &, const NcVarInfo *) -> "; + = "MetUGridDataFile::data_plane(VarInfo &, DataPlane &, const NcVarInfo *, bool) -> "; // Initialize the data plane @@ -300,7 +310,7 @@ bool MetUGridDataFile::data_plane(VarInfo &vinfo, DataPlane &plane, const NcVarI } // Read the data - status = read_data_plane(data_var->name, vinfo, plane, dimension); + status = read_data_plane(data_var->name, vinfo, plane, dimension, do_winds); } return status; @@ -309,7 +319,8 @@ bool MetUGridDataFile::data_plane(VarInfo &vinfo, DataPlane &plane, const NcVarI //////////////////////////////////////////////////////////////////////// int MetUGridDataFile::data_plane_array(VarInfo &vinfo, - DataPlaneArray &plane_array) { + DataPlaneArray &plane_array, + bool do_winds) { int n_rec = 0; DataPlane plane; static const string method_name @@ -322,7 +333,7 @@ int MetUGridDataFile::data_plane_array(VarInfo &vinfo, long lvl_lower = level.lower(); long lvl_upper = level.upper(); ConcatString req_name = vinfo.req_name(); - NcVarInfo *data_vinfo = _file->find_by_name(req_name.c_str()); + NcVarInfo *data_vinfo = _file->find_by_name(req_name.c_str()); if (level.type() == LevelType_Time) { mlog << Error << "\n" << method_name << "LevelType_Time for unstructured grid is not enabled\n\n"; @@ -336,7 +347,7 @@ int MetUGridDataFile::data_plane_array(VarInfo &vinfo, int vlevel = extract_vlevels(req_name, nc_var_info->name.c_str()); if (vlevel >= lvl_lower && vlevel <= lvl_upper) { vinfo.set_req_name(nc_var_info->name.c_str()); - if (data_plane(vinfo, plane, nc_var_info)) { + if (data_plane(vinfo, plane, nc_var_info, do_winds)) { plane_array.add(plane, vlevel, vlevel); n_rec++; } @@ -369,13 +380,13 @@ int MetUGridDataFile::data_plane_array(VarInfo &vinfo, } for (int idx=tmp_lower; idx<=tmp_upper; idx++) { _cur_vert_index = idx; - if (data_plane(vinfo, plane, data_vinfo)) { + if (data_plane(vinfo, plane, data_vinfo, do_winds)) { plane_array.add(plane, lvl_lower, lvl_upper); n_rec++; } } } - else if (data_plane(vinfo, plane)) { + else if (data_plane(vinfo, plane, do_winds)) { plane_array.add(plane, lvl_lower, lvl_upper); n_rec++; } @@ -741,7 +752,8 @@ int MetUGridDataFile::index(VarInfo &vinfo){ //////////////////////////////////////////////////////////////////////// bool MetUGridDataFile::read_data_plane(ConcatString var_name, VarInfo &vinfo, - DataPlane &plane, LongArray &dimension) { + DataPlane &plane, const LongArray &dimension, + bool do_winds) { static const string method_name = "MetUGridDataFile::read_data_plane() -> "; // Read the data @@ -787,10 +799,10 @@ bool MetUGridDataFile::read_data_plane(ConcatString var_name, VarInfo &vinfo, status = false; } + // Handle wind rotation + if(status && do_winds) status = rotate_winds(&vinfo, plane); + if (status) status = process_data_plane(&vinfo, plane); - else { - mlog << Debug(2) << "\n" << method_name << "not processed data_plane\n"; - } // Set the VarInfo object's name, long_name, level, and units strings diff --git a/src/libcode/vx_data2d_ugrid/data2d_ugrid.h b/src/libcode/vx_data2d_ugrid/data2d_ugrid.h index 6f9ab51ff3..cb759ea87e 100644 --- a/src/libcode/vx_data2d_ugrid/data2d_ugrid.h +++ b/src/libcode/vx_data2d_ugrid/data2d_ugrid.h @@ -86,21 +86,22 @@ class MetUGridDataFile : public Met2dDataFile { // retrieve the first matching data plane - bool data_plane(VarInfo &, DataPlane &); - bool data_plane(VarInfo &, DataPlane &, const NcVarInfo *); + bool data_plane(VarInfo &, DataPlane &, bool do_winds = true) override; + bool data_plane(VarInfo &, DataPlane &, const NcVarInfo *, bool do_winds); // retrieve all matching data planes - int data_plane_array(VarInfo &, DataPlaneArray &); + int data_plane_array(VarInfo &, DataPlaneArray &, bool do_winds = true) override; // retrieve the index of the first matching record int extract_vlevels(const ConcatString &var_name_base, const char *var_name); - int index(VarInfo &); + int index(VarInfo &) override; bool read_data_plane(ConcatString var_name, VarInfo &vinfo, - DataPlane &plane, LongArray &dimension); + DataPlane &plane, const LongArray &dimension, + bool do_winds); // // do stuff diff --git a/src/libcode/vx_data2d_ugrid/var_info_ugrid.cc b/src/libcode/vx_data2d_ugrid/var_info_ugrid.cc index ab71e4bdb7..f4d0178b86 100644 --- a/src/libcode/vx_data2d_ugrid/var_info_ugrid.cc +++ b/src/libcode/vx_data2d_ugrid/var_info_ugrid.cc @@ -72,11 +72,8 @@ VarInfoUGrid & VarInfoUGrid::operator=(const VarInfoUGrid &f) { /////////////////////////////////////////////////////////////////////////////// -VarInfo *VarInfoUGrid::clone() const { - - auto ret = new VarInfoUGrid(*this); - - return (VarInfo *)ret; +unique_ptr VarInfoUGrid::clone() const { + return unique_ptr(new VarInfoUGrid(*this)); } /////////////////////////////////////////////////////////////////////////////// @@ -319,9 +316,8 @@ bool VarInfoUGrid::is_precipitation() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsPrecipitation)) { - return(SetAttrIsPrecipitation != 0); - } + int flag = SetAttrIsPrecipitation; + if(!is_bad_data(flag)) return is_flag_set(flag); // // Check to see if the VarInfo name begins with the GRIB code abbreviation @@ -339,9 +335,8 @@ bool VarInfoUGrid::is_specific_humidity() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsSpecificHumidity)) { - return(SetAttrIsSpecificHumidity != 0); - } + int flag = SetAttrIsSpecificHumidity; + if(!is_bad_data(flag)) return is_flag_set(flag); // // Check to see if the VarInfo name begins with the GRIB code abbreviation @@ -359,9 +354,8 @@ bool VarInfoUGrid::is_u_wind() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsUWind)) { - return(SetAttrIsUWind != 0); - } + int flag = get_wind_flag(SetAttrIsUWind, WindInfo.u_wind); + if(!is_bad_data(flag)) return is_flag_set(flag); return is_grib_code_abbr_match(Name, ugrd_grib_code); } @@ -373,9 +367,8 @@ bool VarInfoUGrid::is_v_wind() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsVWind)) { - return(SetAttrIsVWind != 0); - } + int flag = get_wind_flag(SetAttrIsVWind, WindInfo.v_wind); + if(!is_bad_data(flag)) return is_flag_set(flag); return is_grib_code_abbr_match(Name, vgrd_grib_code); } @@ -387,9 +380,8 @@ bool VarInfoUGrid::is_wind_speed() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsWindSpeed)) { - return(SetAttrIsWindSpeed != 0); - } + int flag = get_wind_flag(SetAttrIsWindSpeed, WindInfo.wind_speed); + if(!is_bad_data(flag)) return is_flag_set(flag); return is_grib_code_abbr_match(Name, wind_grib_code); } @@ -401,9 +393,8 @@ bool VarInfoUGrid::is_wind_direction() const { // // Check set_attrs entry // - if(!is_bad_data(SetAttrIsWindDirection)) { - return(SetAttrIsWindDirection != 0); - } + int flag = get_wind_flag(SetAttrIsWindDirection, WindInfo.wind_direction); + if(!is_bad_data(flag)) return is_flag_set(flag); return is_grib_code_abbr_match(Name, wdir_grib_code); } diff --git a/src/libcode/vx_data2d_ugrid/var_info_ugrid.h b/src/libcode/vx_data2d_ugrid/var_info_ugrid.h index 0d96e2eac1..1725956fd5 100644 --- a/src/libcode/vx_data2d_ugrid/var_info_ugrid.h +++ b/src/libcode/vx_data2d_ugrid/var_info_ugrid.h @@ -45,7 +45,7 @@ class VarInfoUGrid : public VarInfo ~VarInfoUGrid() override; VarInfoUGrid(const VarInfoUGrid &); VarInfoUGrid & operator=(const VarInfoUGrid &); - VarInfo *clone() const override; + std::unique_ptr clone() const override; void dump(std::ostream &) const override; void clear(); diff --git a/src/libcode/vx_grid/latlon_grid.h b/src/libcode/vx_grid/latlon_grid.h index 81ccf0afe2..52df6ba48d 100644 --- a/src/libcode/vx_grid/latlon_grid.h +++ b/src/libcode/vx_grid/latlon_grid.h @@ -89,7 +89,7 @@ class LatLonGrid : public GridRep { //////////////////////////////////////////////////////////////////////// -inline double LatLonGrid::scale_km() const { return -1.0; } +inline double LatLonGrid::scale_km() const { return delta_lat * M_PI * grib_earth_radius_km / 180.0; } inline bool LatLonGrid::wrap_lon() const { return wrapLon; } diff --git a/src/libcode/vx_gsl_prob/Makefile.am b/src/libcode/vx_gsl_prob/Makefile.am index 29277933d0..1cf9ef08c7 100644 --- a/src/libcode/vx_gsl_prob/Makefile.am +++ b/src/libcode/vx_gsl_prob/Makefile.am @@ -14,6 +14,7 @@ noinst_LIBRARIES = libvx_gsl_prob.a libvx_gsl_prob_a_SOURCES = \ gsl_bvn.cc gsl_bvn.h \ gsl_cdf.cc gsl_cdf.h \ + gsl_fft2d.cc gsl_fft2d.h \ gsl_randist.cc gsl_randist.h \ gsl_statistics.cc gsl_statistics.h \ gsl_wavelet2d.cc gsl_wavelet2d.h \ diff --git a/src/libcode/vx_gsl_prob/Makefile.in b/src/libcode/vx_gsl_prob/Makefile.in index 3dbe1f7966..95c7d768c6 100644 --- a/src/libcode/vx_gsl_prob/Makefile.in +++ b/src/libcode/vx_gsl_prob/Makefile.in @@ -109,6 +109,7 @@ libvx_gsl_prob_a_AR = $(AR) $(ARFLAGS) libvx_gsl_prob_a_LIBADD = am_libvx_gsl_prob_a_OBJECTS = libvx_gsl_prob_a-gsl_bvn.$(OBJEXT) \ libvx_gsl_prob_a-gsl_cdf.$(OBJEXT) \ + libvx_gsl_prob_a-gsl_fft2d.$(OBJEXT) \ libvx_gsl_prob_a-gsl_randist.$(OBJEXT) \ libvx_gsl_prob_a-gsl_statistics.$(OBJEXT) \ libvx_gsl_prob_a-gsl_wavelet2d.$(OBJEXT) @@ -130,6 +131,7 @@ depcomp = $(SHELL) $(top_srcdir)/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/libvx_gsl_prob_a-gsl_bvn.Po \ ./$(DEPDIR)/libvx_gsl_prob_a-gsl_cdf.Po \ + ./$(DEPDIR)/libvx_gsl_prob_a-gsl_fft2d.Po \ ./$(DEPDIR)/libvx_gsl_prob_a-gsl_randist.Po \ ./$(DEPDIR)/libvx_gsl_prob_a-gsl_statistics.Po \ ./$(DEPDIR)/libvx_gsl_prob_a-gsl_wavelet2d.Po @@ -349,6 +351,7 @@ noinst_LIBRARIES = libvx_gsl_prob.a libvx_gsl_prob_a_SOURCES = \ gsl_bvn.cc gsl_bvn.h \ gsl_cdf.cc gsl_cdf.h \ + gsl_fft2d.cc gsl_fft2d.h \ gsl_randist.cc gsl_randist.h \ gsl_statistics.cc gsl_statistics.h \ gsl_wavelet2d.cc gsl_wavelet2d.h \ @@ -405,6 +408,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvx_gsl_prob_a-gsl_bvn.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvx_gsl_prob_a-gsl_cdf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvx_gsl_prob_a-gsl_fft2d.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvx_gsl_prob_a-gsl_randist.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvx_gsl_prob_a-gsl_statistics.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvx_gsl_prob_a-gsl_wavelet2d.Po@am__quote@ # am--include-marker @@ -457,6 +461,20 @@ libvx_gsl_prob_a-gsl_cdf.obj: gsl_cdf.cc @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvx_gsl_prob_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libvx_gsl_prob_a-gsl_cdf.obj `if test -f 'gsl_cdf.cc'; then $(CYGPATH_W) 'gsl_cdf.cc'; else $(CYGPATH_W) '$(srcdir)/gsl_cdf.cc'; fi` +libvx_gsl_prob_a-gsl_fft2d.o: gsl_fft2d.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvx_gsl_prob_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libvx_gsl_prob_a-gsl_fft2d.o -MD -MP -MF $(DEPDIR)/libvx_gsl_prob_a-gsl_fft2d.Tpo -c -o libvx_gsl_prob_a-gsl_fft2d.o `test -f 'gsl_fft2d.cc' || echo '$(srcdir)/'`gsl_fft2d.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libvx_gsl_prob_a-gsl_fft2d.Tpo $(DEPDIR)/libvx_gsl_prob_a-gsl_fft2d.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='gsl_fft2d.cc' object='libvx_gsl_prob_a-gsl_fft2d.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) $(libvx_gsl_prob_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libvx_gsl_prob_a-gsl_fft2d.o `test -f 'gsl_fft2d.cc' || echo '$(srcdir)/'`gsl_fft2d.cc + +libvx_gsl_prob_a-gsl_fft2d.obj: gsl_fft2d.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvx_gsl_prob_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libvx_gsl_prob_a-gsl_fft2d.obj -MD -MP -MF $(DEPDIR)/libvx_gsl_prob_a-gsl_fft2d.Tpo -c -o libvx_gsl_prob_a-gsl_fft2d.obj `if test -f 'gsl_fft2d.cc'; then $(CYGPATH_W) 'gsl_fft2d.cc'; else $(CYGPATH_W) '$(srcdir)/gsl_fft2d.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libvx_gsl_prob_a-gsl_fft2d.Tpo $(DEPDIR)/libvx_gsl_prob_a-gsl_fft2d.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='gsl_fft2d.cc' object='libvx_gsl_prob_a-gsl_fft2d.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) $(libvx_gsl_prob_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libvx_gsl_prob_a-gsl_fft2d.obj `if test -f 'gsl_fft2d.cc'; then $(CYGPATH_W) 'gsl_fft2d.cc'; else $(CYGPATH_W) '$(srcdir)/gsl_fft2d.cc'; fi` + libvx_gsl_prob_a-gsl_randist.o: gsl_randist.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvx_gsl_prob_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libvx_gsl_prob_a-gsl_randist.o -MD -MP -MF $(DEPDIR)/libvx_gsl_prob_a-gsl_randist.Tpo -c -o libvx_gsl_prob_a-gsl_randist.o `test -f 'gsl_randist.cc' || echo '$(srcdir)/'`gsl_randist.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libvx_gsl_prob_a-gsl_randist.Tpo $(DEPDIR)/libvx_gsl_prob_a-gsl_randist.Po @@ -625,6 +643,7 @@ clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am distclean: distclean-am -rm -f ./$(DEPDIR)/libvx_gsl_prob_a-gsl_bvn.Po -rm -f ./$(DEPDIR)/libvx_gsl_prob_a-gsl_cdf.Po + -rm -f ./$(DEPDIR)/libvx_gsl_prob_a-gsl_fft2d.Po -rm -f ./$(DEPDIR)/libvx_gsl_prob_a-gsl_randist.Po -rm -f ./$(DEPDIR)/libvx_gsl_prob_a-gsl_statistics.Po -rm -f ./$(DEPDIR)/libvx_gsl_prob_a-gsl_wavelet2d.Po @@ -675,6 +694,7 @@ installcheck-am: maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/libvx_gsl_prob_a-gsl_bvn.Po -rm -f ./$(DEPDIR)/libvx_gsl_prob_a-gsl_cdf.Po + -rm -f ./$(DEPDIR)/libvx_gsl_prob_a-gsl_fft2d.Po -rm -f ./$(DEPDIR)/libvx_gsl_prob_a-gsl_randist.Po -rm -f ./$(DEPDIR)/libvx_gsl_prob_a-gsl_statistics.Po -rm -f ./$(DEPDIR)/libvx_gsl_prob_a-gsl_wavelet2d.Po diff --git a/src/libcode/vx_gsl_prob/gsl_fft2d.cc b/src/libcode/vx_gsl_prob/gsl_fft2d.cc new file mode 100644 index 0000000000..a7692535f6 --- /dev/null +++ b/src/libcode/vx_gsl_prob/gsl_fft2d.cc @@ -0,0 +1,77 @@ +// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* +// ** Copyright UCAR (c) 1992 - 2026 +// ** 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 +// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* + +//////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +#include "gsl/gsl_matrix.h" + +#include "vx_log.h" +#include "gsl_fft2d.h" + +using namespace std; + +//////////////////////////////////////////////////////////////////////// +// +// Apply discrete cosine transform to 2D data by transforming the +// columns (nx) and rows (nx) separately +// +//////////////////////////////////////////////////////////////////////// + +extern void dct_typeII_2d(double *data, int ncol, int nrow) { + + // Store input data in a gsl matrix + gsl_matrix *m = gsl_matrix_alloc(nrow, ncol); + for(int i=0; i + +#include "vx_util.h" +#include "gsl/gsl_fft_real.h" + +//////////////////////////////////////////////////////////////////////// + +extern void dct_typeII_2d(double *data, int ncol, int nrow); + +//////////////////////////////////////////////////////////////////////// + +#endif /* __MET_GSL_FFT2D_H__ */ + +//////////////////////////////////////////////////////////////////////// + diff --git a/src/libcode/vx_gsl_prob/vx_gsl_prob.h b/src/libcode/vx_gsl_prob/vx_gsl_prob.h index 2ee6f3b774..4f10c74ee7 100644 --- a/src/libcode/vx_gsl_prob/vx_gsl_prob.h +++ b/src/libcode/vx_gsl_prob/vx_gsl_prob.h @@ -16,6 +16,7 @@ #include "gsl_cdf.h" #include "gsl_bvn.h" +#include "gsl_fft2d.h" #include "gsl_randist.h" #include "gsl_statistics.h" #include "gsl_wavelet2d.h" diff --git a/src/libcode/vx_ioda/ioda.h b/src/libcode/vx_ioda/ioda.h index 7b256549bc..be256bd370 100644 --- a/src/libcode/vx_ioda/ioda.h +++ b/src/libcode/vx_ioda/ioda.h @@ -24,19 +24,6 @@ enum class e_ioda_format { v1, v2 }; //////////////////////////////////////////////////////////////////////// -struct IODAHeaders { - void clear(); -}; - -//////////////////////////////////////////////////////////////////////// - -struct IODAMetadata { - - void clear(); -}; - -//////////////////////////////////////////////////////////////////////// - class IODAReader { public: diff --git a/src/libcode/vx_nav/nav.cc b/src/libcode/vx_nav/nav.cc index a7a1f5f1ea..db7e6147d4 100644 --- a/src/libcode/vx_nav/nav.cc +++ b/src/libcode/vx_nav/nav.cc @@ -23,12 +23,6 @@ using namespace std; ////////////////////////////////////////////////////////////////// -//static const double cf = 57.2957795130823208768; - - -////////////////////////////////////////////////////////////////// - - struct Vector3D { double x, y, z; }; @@ -58,31 +52,17 @@ double gc_dist(double lat1, double lon1, double lat2, double lon2) { -double x, dp, dl; -double lat1_radians, lat2_radians; -double lon1_radians, lon2_radians; +double lat1_radians = lat1 * rad_per_deg; +double lat2_radians = lat2 * rad_per_deg; +double lon1_radians = lon1 * rad_per_deg; +double lon2_radians = lon2 * rad_per_deg; -lat1_radians = lat1 * rad_per_deg; -lat2_radians = lat2 * rad_per_deg; -lon1_radians = lon1 * rad_per_deg; -lon2_radians = lon2 * rad_per_deg; -//lat1_radians = deg_to_rad(lat1); -//lat2_radians = deg_to_rad(lat2); -//lon1_radians = deg_to_rad(lon1); -//lon2_radians = deg_to_rad(lon2); +double dp = (lat1_radians - lat2_radians); +double dl = (lon1_radians - lon2_radians); -dp = (lat1_radians - lat2_radians); -dl = (lon1_radians - lon2_radians); +double x = haversine(dp) + cos(lat1_radians)*cos(lat2_radians)*haversine(dl); -//dp = (lat1 - lat2)/cf; -//dl = (lon1 - lon2)/cf; - -x = haversine(dp) + cos(lat1_radians)*cos(lat2_radians)*haversine(dl); -//x = haversine(dp) + cos(lat1/cf)*cos(lat2/cf)*haversine(dl); - -x = earth_radius_km*ahaversine(x); - -return x; +return earth_radius_km*ahaversine(x); } @@ -157,21 +137,19 @@ double rl_dist(double lat1, double lon1, double lat2, double lon2) { -double d, beta; +double d; if ( fabs(lat1 - lat2) < 0.0001 ) { d = earth_radius_km*cosd(lat1)*(lon1 - lon2) * rad_per_deg; -// d = earth_radius_km*cos(lat1/cf)*((lon1 - lon2)/cf); return fabs(d); } -beta = rl_bearing(lat1, lon1, lat2, lon2); +double beta = rl_bearing(lat1, lon1, lat2, lon2); d = earth_radius_km*( rad_per_deg * (lat1 - lat2)/cosd(beta) ); -//d = earth_radius_km*( ((lat1 - lat2)/cf)/cos(beta/cf) ); return fabs(d); @@ -199,13 +177,10 @@ double rl_bearing(double lat1, double lon1, double lat2, double lon2) { -double mp1, mp2, beta; - -mp1 = meridional_parts(lat1); -mp2 = meridional_parts(lat2); +double mp1 = meridional_parts(lat1); +double mp2 = meridional_parts(lat2); -beta = atan2d( rad_per_deg * (lon1 - lon2), mp2 - mp1); -//beta = cf*atan2((lon1 - lon2)/cf, mp2 - mp1); +double beta = atan2d( rad_per_deg * (lon1 - lon2), mp2 - mp1); return beta; @@ -231,7 +206,6 @@ double meridional_parts(double a) { a = a * rad_per_deg; -//a /= cf; return log(tan(piover4 + (0.5*a))); @@ -259,24 +233,13 @@ double gc_bearing(double lat1, double lon1, double lat2, double lon2) { -double x, y, dl, beta; +double dl = lon1 - lon2; -//lat1 /= cf; lon1 /= cf; +double x = cosd(lat1)*sind(lat2) - sind(lat1)*cosd(lat2)*cosd(dl); -//lat2 /= cf; lon2 /= cf; +double y = cosd(lat2)*sind(dl); -dl = lon1 - lon2; - -x = cosd(lat1)*sind(lat2) - sind(lat1)*cosd(lat2)*cosd(dl); -//x = cos(lat1)*sin(lat2) - sin(lat1)*cos(lat2)*cos(dl); - -y = cosd(lat2)*sind(dl); -//y = cos(lat2)*sin(dl); - -beta = atan2d(y, x); -//beta = cf*atan2(y, x); - -return beta; +return atan2d(y, x); } @@ -306,51 +269,34 @@ void gc_point_v1(double lat1, double lon1, double lat2, double lon2, { -double sp1, sp2, sl1, sl2; -double cp1, cp2, cl1, cl2; -double t, st, sth, stmt, theta; -double x, y, z; - -theta = gc_dist(lat1, lon1, lat2, lon2)/earth_radius_km; - -t = dist/earth_radius_km; - -sth = sin(theta); +double theta = gc_dist(lat1, lon1, lat2, lon2)/earth_radius_km; -st = sin(t); +double t = dist/earth_radius_km; -stmt = sin(theta - t); +double sth = sin(theta); +double st = sin(t); +double stmt = sin(theta - t); -//lat1 /= cf; lon1 /= cf; +double sp1 = sind(lat1); +double sp2 = sind(lat2); +double sl1 = sind(lon1); +double sl2 = sind(lon2); -//lat2 /= cf; lon2 /= cf; +double cp1 = cosd(lat1); +double cp2 = cosd(lat2); +double cl1 = cosd(lon1); +double cl2 = cosd(lon2); -sp1 = sind(lat1); sp2 = sind(lat2); -//sp1 = sin(lat1); sp2 = sin(lat2); - -sl1 = sind(lon1); sl2 = sind(lon2); -//sl1 = sin(lon1); sl2 = sin(lon2); - -cp1 = cosd(lat1); cp2 = cosd(lat2); -//cp1 = cos(lat1); cp2 = cos(lat2); - -cl1 = cosd(lon1); cl2 = cosd(lon2); -//cl1 = cos(lon1); cl2 = cos(lon2); - -x = cp1*cl1*stmt + cp2*cl2*st; - -y = cp1*sl1*stmt + cp2*sl2*st; - -z = (sp1*stmt + sp2*st)/sth; +double x = cp1*cl1*stmt + cp2*cl2*st; +double y = cp1*sl1*stmt + cp2*sl2*st; +double z = (sp1*stmt + sp2*st)/sth; lat = asind(z); -//lat = cf*asin(z); if ( (fabs(x) + fabs(y)) < 1.0e-6 ) lon = 0.0; else lon = atan2d(y, x); -// lon = cf*atan2(y, x); return; @@ -384,41 +330,28 @@ void gc_point_v2(double lat1, double lon1, double bear, double dist, { -double x, y, z, t; -double sp, cp, cl, sl, sb, cb, st, ct; - -//lat1 /= cf; -//lon1 /= cf; - -//bear /= cf; - -t = dist/earth_radius_km; +double t = dist/earth_radius_km; -sp = sind(lat1); cp = cosd(lat1); -//sp = sin(lat1); cp = cos(lat1); +double sp = sind(lat1); +double cp = cosd(lat1); +double sl = sind(lon1); +double cl = cosd(lon1); -sl = sind(lon1); cl = cosd(lon1); -//sl = sin(lon1); cl = cos(lon1); +double sb = sind(bear); +double cb = cosd(bear); +double st = sin(t); +double ct = cos(t); -sb = sind(bear); cb = cosd(bear); -//sb = sin(bear); cb = cos(bear); - -st = sin(t); ct = cos(t); - -x = cp*sl*ct - sp*sl*cb*st - cl*sb*st; - -y = cp*cl*ct - sp*cl*cb*st + sl*sb*st; - -z = sp*ct + cp*cb*st; +double x = cp*sl*ct - sp*sl*cb*st - cl*sb*st; +double y = cp*cl*ct - sp*cl*cb*st + sl*sb*st; +double z = sp*ct + cp*cb*st; lat = asind(z); -//lat = cf*asin(z); if ( (fabs(x) + fabs(y)) < 1.0e-6 ) lon = 0.0; else lon = atan2d(x, y); -// lon = cf*atan2(x, y); return; @@ -485,39 +418,28 @@ void rl_point_v2(double lat1, double lon1, double bear, double dist, { -double t, sb, cb, tb, mp, mp1; +double sb = sind(bear); +double cb = cosd(bear); -//bear /= cf; - -sb = sind(bear); cb = cosd(bear); -//sb = sin(bear); cb = cos(bear); - -t = dist/earth_radius_km; +double t = dist/earth_radius_km; lat = lat1 + deg_per_rad * (t*cb); -//lat = lat1 + cf*t*cb; if ( fabs(cb) < 1.0e-5 ) lon = lon1 * rad_per_deg - t*( sb/cosd(lat1) ); -// lon = (lon1/cf) - t*( sb/cos(lat1/cf) ); else { - tb = tand(bear); -// tb = tan(bear); - - mp = meridional_parts(lat); - - mp1 = meridional_parts(lat1); + double tb = tand(bear); + double mp = meridional_parts(lat); + double mp1 = meridional_parts(lat1); lon = lon1 * rad_per_deg - tb*( mp - mp1 ); -// lon = (lon1/cf) - tb*( mp - mp1 ); } lon += twopi*floor( 0.5 - (lon/twopi) ); lon = lon * deg_per_rad; -//lon *= cf; return; @@ -546,31 +468,32 @@ return; double gc_dist_to_line(double lat1, double lon1, double lat2, double lon2, double lat3, double lon3) { - Vector3D a, b, c, g, f, t; - double lat4, lon4, dist12, dist14, dist24, dist; // Convert to cartesian coordinates - a = latlon_to_xyz(lat1, lon1); - b = latlon_to_xyz(lat2, lon2); - c = latlon_to_xyz(lat3, lon3); + Vector3D a = latlon_to_xyz(lat1, lon1); + Vector3D b = latlon_to_xyz(lat2, lon2); + Vector3D c = latlon_to_xyz(lat3, lon3); // Compute intersection of arc AB with perpendicular from C - g = cross_product(a, b); - f = cross_product(c, g); - t = cross_product(g, f); + Vector3D g = cross_product(a, b); + Vector3D f = cross_product(c, g); + Vector3D t = cross_product(g, f); // Convert intersection point from cartesian back to lat/lon + double lat4; + double lon4; xyz_to_latlon(t, lat4, lon4); // Length of the arc segment - dist12 = gc_dist(lat1, lon1, lat2, lon2); + double dist12 = gc_dist(lat1, lon1, lat2, lon2); // Distance from intersection point to end points - dist14 = gc_dist(lat1, lon1, lat4, lon4); - dist24 = gc_dist(lat2, lon2, lat4, lon4); + double dist14 = gc_dist(lat1, lon1, lat4, lon4); + double dist24 = gc_dist(lat2, lon2, lat4, lon4); // If intersection is between the end points, // use the perpendicular distance + double dist; if(dist14 <= dist12 && dist24 <= dist12) { dist = gc_dist(lat3, lon3, lat4, lon4); } diff --git a/src/libcode/vx_shapedata/mode_conf_info.cc b/src/libcode/vx_shapedata/mode_conf_info.cc index 571de92c08..3f229a4027 100644 --- a/src/libcode/vx_shapedata/mode_conf_info.cc +++ b/src/libcode/vx_shapedata/mode_conf_info.cc @@ -910,7 +910,8 @@ const DictionaryEntry * ee = dict->lookup(conf_key_field); if ( !ee ) { - mlog << "\nModeConfInfo::read_fields () -> \"field\" entry not found in dictionary!\n\n"; + mlog << Error << "\nModeConfInfo::read_fields () -> " + << "\"field\" entry not found in dictionary!\n\n"; exit ( 1 ); @@ -989,7 +990,8 @@ void ModeConfInfo::read_fields_1 (Mode_Field_Info * & info_array, Dictionary * d const DictionaryEntry * ee = dict->lookup(conf_key_field); if ( !ee ) { - mlog << "\nModeConfInfo::read_fields () -> \"field\" entry not found in dictionary!\n\n"; + mlog << Error << "\nModeConfInfo::read_fields () -> \"field\" " + << "entry not found in dictionary!\n\n"; exit ( 1 ); @@ -1668,7 +1670,8 @@ GrdFileType ModeConfInfo::file_type_for_field(bool isFcst, int field_index) const DictionaryEntry *e = dict->lookup(conf_key_field); if ( !e ) { - mlog << "\nModeConfInfo::read_fields () -> \"field\" entry not found in dictionary!\n\n"; + mlog << Error << "\nModeConfInfo::read_fields () -> \"field\" " + << "entry not found in dictionary!\n\n"; exit ( 1 ); } diff --git a/src/libcode/vx_shapedata/mode_field_info.cc b/src/libcode/vx_shapedata/mode_field_info.cc index 5ff21bd6fe..d2f335b2c5 100644 --- a/src/libcode/vx_shapedata/mode_field_info.cc +++ b/src/libcode/vx_shapedata/mode_field_info.cc @@ -122,7 +122,7 @@ dict = 0; conf = 0; -var_info = 0; +var_info.reset(); clear(); @@ -155,7 +155,7 @@ conv_radius = 0; vld_thresh = 0.0; -if ( var_info ) { delete var_info; var_info = 0; } +var_info.reset(); conv_radius_array.clear(); @@ -207,7 +207,7 @@ dict = _dict; conf = _conf; -var_info = VarInfoFactory::new_var_info(type); +var_info = std::unique_ptr(VarInfoFactory::new_var_info(type)); if ( _multivar ) { diff --git a/src/libcode/vx_shapedata/mode_field_info.h b/src/libcode/vx_shapedata/mode_field_info.h index 3f73d6ecfa..93c3a67d85 100644 --- a/src/libcode/vx_shapedata/mode_field_info.h +++ b/src/libcode/vx_shapedata/mode_field_info.h @@ -73,7 +73,7 @@ class Mode_Field_Info { int conv_radius; // Convolution radius in grid squares double vld_thresh; // Minimum ratio of valid data points in the convolution area - VarInfo * var_info; // allocated + std::unique_ptr var_info; IntArray conv_radius_array; // List of convolution radii in grid squares ThreshArray conv_thresh_array; // List of conv thresholds to use diff --git a/src/tools/core/ensemble_stat/ensemble_stat.cc b/src/tools/core/ensemble_stat/ensemble_stat.cc index 550975473b..57bb47f856 100644 --- a/src/tools/core/ensemble_stat/ensemble_stat.cc +++ b/src/tools/core/ensemble_stat/ensemble_stat.cc @@ -439,7 +439,7 @@ static void process_command_line(int argc, char **argv) { mlog << Debug(1) << "Ensemble Files[" << n_ens_files << "]:\n"; for(int i=0; i " << "trouble reading file \"" << infile << "\"\n\n"; @@ -740,8 +745,8 @@ static bool get_data_plane_array(const char *infile, GrdFileType ftype, } // end if found - // Deallocate the data file pointer, if necessary - if(mtddf) { delete mtddf; mtddf = (Met2dDataFile *) nullptr; } + // Cleanup + if(mtddf) { delete mtddf; mtddf = nullptr; } return found; } diff --git a/src/tools/core/pcp_combine/pcp_combine.cc b/src/tools/core/pcp_combine/pcp_combine.cc index 5cee4ab207..0e580490ad 100644 --- a/src/tools/core/pcp_combine/pcp_combine.cc +++ b/src/tools/core/pcp_combine/pcp_combine.cc @@ -1308,18 +1308,19 @@ static bool get_field(const char *filename, ConcatString config_str = parse_config_str(cur_field); mlog << Debug(1) - << "Reading data (" << config_str + << "\nReading data (" << config_str << ") from input file: " << filename << "\n"; // - // Parse the config string. + // Parse the config string into a local copy. // - config.read_string(config_str.c_str()); + MetConfig cur_config = config; + cur_config.read_string(config_str.c_str()); // // Get the gridded file type from config string, if present. // - ftype = parse_conf_file_type(&config); + ftype = parse_conf_file_type(&cur_config); // // If not set by the config string, use the file list type. @@ -1368,7 +1369,7 @@ static bool get_field(const char *filename, // // Initialize the VarInfo object with a config. // - cur_var->set_dict(config); + cur_var->set_dict(cur_config); // // Set the VarInfo timing object. diff --git a/src/tools/core/wavelet_stat/wavelet_stat.cc b/src/tools/core/wavelet_stat/wavelet_stat.cc index 50d89d0911..695e40fca0 100644 --- a/src/tools/core/wavelet_stat/wavelet_stat.cc +++ b/src/tools/core/wavelet_stat/wavelet_stat.cc @@ -91,7 +91,6 @@ static void setup_ps_file (unixtime, int); static void build_outfile_name(unixtime, int, const char *, ConcatString &); static double get_fill_value(const DataPlane &, int); -static void fill_bad_data(DataPlane &, double); static void pad_field(DataPlane &, double); static void get_tile(const DataPlane &, const DataPlane &, int, int, NumArray &, NumArray &); @@ -405,10 +404,10 @@ static void process_scores() { // Replace any bad data in the fields with a fill value mlog << Debug(2) << "Forecast field: "; - fill_bad_data(fcst_dp_fill, fcst_fill); + fcst_dp_fill.replace_bad_data(fcst_fill); mlog << Debug(2) << "Observation field: "; - fill_bad_data(obs_dp_fill, obs_fill); + obs_dp_fill.replace_bad_data(obs_fill); // Pad the fields out to the nearest power of two if requested if(conf_info.grid_decomp_flag == GridDecompType::Pad) { @@ -769,27 +768,8 @@ static double get_fill_value(const DataPlane &dp, int i_vx) { // Otherwise, fill them with the mean of the valid data. // else { - int count = 0; - double sum = 0.0; - int nxy = dp.nxy(); - -#pragma omp parallel default(none) \ - shared(nxy, dp, sum, count) - { - -#pragma omp for schedule(static) \ - reduction(+: sum, count) - for(int i=0; i 0) fill_val = sum/count; - else fill_val = 0.0; + fill_val = dp.mean(); + if(is_bad_data(fill_val)) fill_val = 0.0; } return fill_val; @@ -797,41 +777,6 @@ static double get_fill_value(const DataPlane &dp, int i_vx) { //////////////////////////////////////////////////////////////////////// -static void fill_bad_data(DataPlane &dp, double fill_val) { - - // - // Replace any bad data values with the fill value - // - int count = 0; - int nxy = dp.nxy(); - -#pragma omp parallel default(none) \ - shared(nxy, dp, fill_val, count) - { - -#pragma omp for schedule(static) \ - reduction(+: count) - for(int i=0; i 0) { - mlog << Debug(2) << "Replaced " << count - << " bad data values out of " << nxy - << " points with fill value of " - << fill_val << ".\n"; - } - - return; -} - -//////////////////////////////////////////////////////////////////////// - static void pad_field(DataPlane &dp, double pad_val) { DataPlane dp_pad; diff --git a/src/tools/dev_utils/gen_climo_bin.cc b/src/tools/dev_utils/gen_climo_bin.cc index 1b4fe5ed8d..4ef81ea262 100644 --- a/src/tools/dev_utils/gen_climo_bin.cc +++ b/src/tools/dev_utils/gen_climo_bin.cc @@ -187,7 +187,7 @@ void process_binary() { // Read Nx*Ny records nxy = grid.nx()*grid.ny(); - mlog << "Processing " << nxy << " grid points.\n"; + mlog << Debug(2) << "Processing " << nxy << " grid points.\n"; // Loop over grid points for(i=0; i " << "trouble reading file \"" << infile << "\"\n\n"; exit(1); @@ -700,8 +702,8 @@ static bool get_data_plane(const char *infile, GrdFileType ftype, } // end if found - // Deallocate the data file pointer, if necessary - if(mtddf) { delete mtddf; mtddf = (Met2dDataFile *) nullptr; } + // Cleanup + if(mtddf) { delete mtddf; mtddf = nullptr; } return found; } diff --git a/src/tools/other/grid_diag/Makefile.am b/src/tools/other/grid_diag/Makefile.am index 59e735bf05..c279ecc888 100644 --- a/src/tools/other/grid_diag/Makefile.am +++ b/src/tools/other/grid_diag/Makefile.am @@ -28,6 +28,7 @@ grid_diag_LDADD = -lvx_stat_out \ -lvx_data2d_nc_cf \ $(UGRID_MET_LIBS) \ $(PYTHON_MET_LIBS) \ + -lvx_nav \ -lvx_statistics \ -lvx_data2d \ -lvx_nc_util \ diff --git a/src/tools/other/grid_diag/Makefile.in b/src/tools/other/grid_diag/Makefile.in index 2e0f91a9c9..666078f5dc 100644 --- a/src/tools/other/grid_diag/Makefile.in +++ b/src/tools/other/grid_diag/Makefile.in @@ -343,6 +343,7 @@ grid_diag_LDADD = -lvx_stat_out \ -lvx_data2d_nc_cf \ $(UGRID_MET_LIBS) \ $(PYTHON_MET_LIBS) \ + -lvx_nav \ -lvx_statistics \ -lvx_data2d \ -lvx_nc_util \ diff --git a/src/tools/other/grid_diag/grid_diag.cc b/src/tools/other/grid_diag/grid_diag.cc index 624946d791..bcb3424b74 100644 --- a/src/tools/other/grid_diag/grid_diag.cc +++ b/src/tools/other/grid_diag/grid_diag.cc @@ -22,6 +22,7 @@ // 005 10/03/22 Prestopnik MET #2227 Remove using namespace std and netCDF from header files // 006 10/26/22 Linden MET #2232 Refine the Grid-Diag output variable names when specifying two input data sources // 007 01/07/26 Halley Gotway MET #3171 Multiple masks and information theory +// 008 02/12/26 Halley Gotway MET #3304 Power spectrum // //////////////////////////////////////////////////////////////////////// @@ -32,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +45,7 @@ #include "series_data.h" #include "series_pdf.h" +#include "nav.h" #include "vx_statistics.h" #include "vx_nc_util.h" #include "vx_regrid.h" @@ -60,19 +63,41 @@ using namespace netCDF; static void process_command_line(int, char **); static void setup_diag_info(void); static void process_series(void); -static void process_hist1d(const vector &); -static void process_hist2d(const vector &); +static void process_hist1d(const vector &); +static void process_hist2d(const vector &); +static void prepare_power_spectrum_data(vector &); +static void process_power_spectrum(const vector &); +static void process_error_power_spectrum(const vector &); +static void accumulate(vector &, const vector &); +static DataPlane dct_typeII(const DataPlane &); static void process_info_theory(void); static void setup_nc_file(void); +static void read_series_data(int, VarInfo *, const StringArray &, + const GrdFileType, InputDataInfo &); +static bool read_wind_series(const StringArray &, + int, VarInfo *, const StringArray &, + const GrdFileType, DataPlane &); +static void regrid_data(VarInfo *, const Grid &, DataPlane &); static ConcatString get_nc_var_str(const VarInfo *, int); +static ConcatString get_nc_att_str(const ConcatString &, + const ConcatString &); static void write_nc_var_int(const char *, const char *, int); -static void add_var_att_local(NcVar *, const char *, const ConcatString &); +static void add_var_data_atts(NcVar *, const ConcatString &, + const ConcatString &, + const ConcatString &); +static void add_var_att_local(NcVar *, const char *, + const ConcatString &); static void write_hist_bins(void); static void write_hist1d(void); static void write_hist2d(void); +static void write_wavelengths(void); +static void write_power_spectrum(void); +static void write_error_power_spectrum(void); static void write_info_theory(void); static void clean_up(void); +static double get_grid_res_km(const Grid &); + static Met2dDataFile *get_mtddf(const StringArray &, const int); static void usage(int exit_code=1); @@ -109,10 +134,17 @@ int met_main(int argc, char *argv[]) { // Write information theory output if(conf_info.nc_info.do_info_theory) write_info_theory(); + // Write power spectrum output for the full domain + if(conf_info.nc_info.do_power_spectrum) { + write_wavelengths(); + write_power_spectrum(); + if(multiple_data_sources) write_error_power_spectrum(); + } + // Write benchmarking metrics - #ifdef WITH_PROFILER +#ifdef WITH_PROFILER ctrack::result_print(); - #endif +#endif // Close files and deallocate memory clean_up(); @@ -173,8 +205,8 @@ static void process_command_line(int argc, char **argv) { // List the config files mlog << Debug(1) - << "Default Config File: " << default_config_file << "\n" - << "User Config File: " << config_file << "\n"; + << "Default Config File: " << default_config_file << "\n" + << "User Config File: " << config_file << "\n"; // Read the config files conf_info.read_config(default_config_file.c_str(), @@ -230,7 +262,7 @@ static void process_command_line(int argc, char **argv) { } // end for i // Process the configuration - conf_info.process_config(file_types); + conf_info.process_config(file_types, data_grid); // Determine the verification grid grid = parse_vx_grid(conf_info.data_info[0]->regrid(), @@ -259,21 +291,22 @@ string get_tool_name() { //////////////////////////////////////////////////////////////////////// static void setup_diag_info(void) { - #ifdef WITH_PROFILER +#ifdef WITH_PROFILER CTRACK; - #endif +#endif // Resize based on the number of variables and masks diag_info.resize(conf_info.get_n_data()); for(auto &info : diag_info) info.resize(conf_info.get_n_mask()); + power_info.resize(conf_info.get_n_data()); // Loop over variables for(int i_var=0; i_var < conf_info.get_n_data(); i_var++) { // Find bin ranges - const VarInfo *i_data = conf_info.data_info[i_var]; - NumArray range(i_data->range()); - int i_n_bins = i_data->n_bins(); + const VarInfo *i_vinfo = conf_info.data_info[i_var]; + NumArray range(i_vinfo->range()); + int i_n_bins = i_vinfo->n_bins(); double var_min = range[0]; double var_max = range[1]; double bin_delta = (var_max - var_min) / i_n_bins; @@ -290,27 +323,27 @@ static void setup_diag_info(void) { // 1D histogram mlog << Debug(2) - << "Initializing " << i_data->magic_str_attr() + << "Initializing " << i_vinfo->magic_str_attr() << " histogram with " << i_n_bins << " bins from " << var_min << " to " << var_max << ".\n"; vector hist1d; init_pdf(i_n_bins, hist1d); // Keep track of unique output variable names - if(nc_var_sa.has(i_data->magic_str_attr())) unique_variable_names = false; - nc_var_sa.add(i_data->magic_str_attr()); + if(nc_var_sa.has(i_vinfo->magic_str_attr())) unique_variable_names = false; + nc_var_sa.add(i_vinfo->magic_str_attr()); // 2D histograms map > hist2d; for(int j_var=i_var+1; j_var < conf_info.get_n_data(); j_var++) { - const VarInfo *j_data = conf_info.data_info[j_var]; + const VarInfo *j_vinfo = conf_info.data_info[j_var]; - int j_n_bins = j_data->n_bins(); + int j_n_bins = j_vinfo->n_bins(); mlog << Debug(2) - << "Initializing " << i_data->magic_str_attr() << "_" - << j_data->magic_str_attr() << " joint histogram with " + << "Initializing " << i_vinfo->magic_str_attr() << "_" + << j_vinfo->magic_str_attr() << " joint histogram with " << i_n_bins << " x " << j_n_bins << " bins.\n"; hist2d[j_var] = vector(); @@ -336,15 +369,14 @@ static void setup_diag_info(void) { //////////////////////////////////////////////////////////////////////// static void process_series(void) { - vector data_dp(conf_info.get_n_data()); + vector in_data(conf_info.get_n_data()); const StringArray *cur_files; const GrdFileType *cur_ftype; - Grid cur_grid; // List the lengths of the series options mlog << Debug(1) - << "Processing " << conf_info.get_n_data() << " data fields" - << " from " << n_series << " input file(s).\n"; + << "Processing " << conf_info.get_n_data() << " data fields" + << " from " << n_series << " input file(s).\n"; // Loop over the input files for(int i_series=0; i_series < n_series; i_series++) { @@ -357,7 +389,7 @@ static void process_series(void) { // Read the input data for this series entry for(int i_var=0; i_var < conf_info.get_n_data(); i_var++) { - VarInfo *i_data = conf_info.data_info[i_var]; + VarInfo *i_vinfo = conf_info.data_info[i_var]; // Check for separate data files for each field if(data_files.size() > 1) { @@ -370,58 +402,34 @@ static void process_series(void) { } mlog << Debug(2) - << "Reading field " << i_data->magic_str_attr() + << "Reading field " << i_vinfo->magic_str_attr() << " data from file: " << (*cur_files)[i_series] << "\n"; - get_series_entry(i_series, i_data, *cur_files, *cur_ftype, - data_dp[i_var], cur_grid); - - // Regrid, if necessary - if(!(cur_grid == grid)) { - mlog << Debug(2) - << "Regridding field " << i_data->magic_str_attr() - << " to the verification grid using " - << i_data->regrid().get_str() << ".\n"; - data_dp[i_var] = met_regrid(data_dp[i_var], - cur_grid, grid, - i_data->regrid()); - } + // Read the series data + read_series_data(i_series, i_vinfo, *cur_files, *cur_ftype, + in_data[i_var]); - // Initialize time ranges - if(i_series == 0 && i_var == 0) { - init_beg = init_end = data_dp[i_var].init(); - valid_beg = valid_end = data_dp[i_var].valid(); - lead_beg = lead_end = data_dp[i_var].lead(); - } - // Update time ranges - else { - if(data_dp[i_var].init() < init_beg) { - init_beg = data_dp[i_var].init(); - } - if(data_dp[i_var].init() > init_end) { - init_end = data_dp[i_var].init(); - } - if(data_dp[i_var].valid() < valid_beg) { - valid_beg = data_dp[i_var].valid(); - } - if(data_dp[i_var].valid() > valid_end) { - valid_end = data_dp[i_var].valid(); - } - if(data_dp[i_var].lead() < lead_beg) { - lead_beg = data_dp[i_var].lead(); - } - if(data_dp[i_var].lead() > lead_end) { - lead_end = data_dp[i_var].lead(); - } - } } // end for i_var // Process the 1D histograms - process_hist1d(data_dp); + process_hist1d(in_data); // Process the 2D histograms - process_hist2d(data_dp); + process_hist2d(in_data); + + // Process the power spectrum + if(conf_info.nc_info.do_power_spectrum) { + + // Prepare power spectrum data + prepare_power_spectrum_data(in_data); + + // Process the power spectrum + process_power_spectrum(in_data); + + // Process the error power spectrum + if(multiple_data_sources) process_error_power_spectrum(in_data); + } } // end for i_series @@ -429,15 +437,15 @@ static void process_series(void) { if(conf_info.nc_info.do_info_theory) process_info_theory(); } - + //////////////////////////////////////////////////////////////////////// -static void process_hist1d(const vector &data_dp) { +static void process_hist1d(const vector &in_data) { // Update the 1D histogram counts for(int i_var=0; i_var < conf_info.get_n_data(); i_var++) { - const VarInfo *i_data = conf_info.data_info[i_var]; + const VarInfo *i_vinfo = conf_info.data_info[i_var]; // Loop over the masks for(int i_mask=0; i_mask < conf_info.get_n_mask(); i_mask++) { @@ -445,7 +453,7 @@ static void process_hist1d(const vector &data_dp) { DiagInfo *i_diag = &diag_info[i_var][i_mask]; // Apply the mask before updating the data ranges - DataPlane dp(data_dp[i_var]); + DataPlane dp(in_data[i_var].dp); apply_mask(dp, conf_info.mask_mp[i_mask]); double min; double max; @@ -461,27 +469,27 @@ static void process_hist1d(const vector &data_dp) { update_pdf(i_diag->bin_min[0], i_diag->bin_delta, i_diag->hist1d, - data_dp[i_var], + in_data[i_var].dp, conf_info.mask_mp[i_mask]); mlog << Debug(2) - << "Processed " << i_data->magic_str_attr() + << "Processed " << i_vinfo->magic_str_attr() << " data over region " << conf_info.mask_name[i_mask] << " with range (" << i_diag->var_min << ", " << i_diag->var_max << ") into bins with range (" - << i_data->range()[0] << ", " - << i_data->range()[1] << ").\n"; + << i_vinfo->range()[0] << ", " + << i_vinfo->range()[1] << ").\n"; // Compare input data and bin ranges - if(i_diag->var_min < i_data->range()[0] || - i_diag->var_max > i_data->range()[1]) { + if(i_diag->var_min < i_vinfo->range()[0] || + i_diag->var_max > i_vinfo->range()[1]) { mlog << Warning << "\nprocess_hist1d() -> " - << "the range of the " << i_data->magic_str_attr() + << "the range of the " << i_vinfo->magic_str_attr() << " data over region " << conf_info.mask_name[i_mask] << " (" << i_diag->var_min << ", " << i_diag->var_max << ") falls outside the configuration file range (" - << i_data->range()[0] << ", " - << i_data->range()[1] << ")!\n\n"; + << i_vinfo->range()[0] << ", " + << i_vinfo->range()[1] << ")!\n\n"; } } // end for i_mask } // end for i_var @@ -489,16 +497,16 @@ static void process_hist1d(const vector &data_dp) { //////////////////////////////////////////////////////////////////////// -static void process_hist2d(const vector &data_dp) { +static void process_hist2d(const vector &in_data) { // Process the 2D joint histograms for(int i_var=0; i_var < conf_info.get_n_data(); i_var++) { - const VarInfo *i_data = conf_info.data_info[i_var]; + const VarInfo *i_vinfo = conf_info.data_info[i_var]; for(int j_var=i_var+1; j_var < conf_info.get_n_data(); j_var++) { - const VarInfo *j_data = conf_info.data_info[j_var]; + const VarInfo *j_vinfo = conf_info.data_info[j_var]; for(int i_mask=0; i_mask < conf_info.get_n_mask(); i_mask++) { @@ -506,14 +514,14 @@ static void process_hist2d(const vector &data_dp) { DiagInfo *j_diag = &diag_info[j_var][i_mask]; // Update 2D histogram counts - update_joint_pdf(i_data->n_bins(), - j_data->n_bins(), + update_joint_pdf(i_vinfo->n_bins(), + j_vinfo->n_bins(), i_diag->bin_min[0], j_diag->bin_min[0], i_diag->bin_delta, j_diag->bin_delta, i_diag->hist2d[j_var], - data_dp[i_var], data_dp[j_var], + in_data[i_var].dp, in_data[j_var].dp, conf_info.mask_mp[i_mask]); } // end for i_mask } // end for j_var @@ -522,6 +530,198 @@ static void process_hist2d(const vector &data_dp) { //////////////////////////////////////////////////////////////////////// +static void prepare_power_spectrum_data(vector &in_data) { + + for(int i_var=0; i_var < conf_info.get_n_data(); i_var++) { + + int nxy = in_data[i_var].dp.nxy(); + int n_vld = in_data[i_var].dp.n_good_data(); + + // Check for bad data + if(n_vld < nxy) { + + PowerSpectrumInfo *ps_ptr = &conf_info.ps_info[i_var]; + + // Skip power spectrum for missing data + if(ps_ptr->missing_flag == MissingDataType::None || + (double) n_vld/nxy < ps_ptr->vld_thresh) { + + mlog << Debug(3) << "Skipping " + << conf_info.data_info[i_var]->magic_str_attr() + << " power spectrum due to missing data.\n"; + ps_ptr->skip = true; + } + else { + + // Initialize to the config file value + double fill; + ConcatString desc; + + // Replace with the mean of the field + if(ps_ptr->missing_flag == MissingDataType::Mean) { + fill = in_data[i_var].dp.mean(); + desc = "mean"; + } + // Use config file value + else { + fill = ps_ptr->missing_value; + desc = "constant"; + } + + mlog << Debug(3) << "Replacing " + << conf_info.data_info[i_var]->magic_str_attr() + << " power spectrum missing data with a " << desc + << " value of " << fill << ".\n"; + in_data[i_var].dp.replace_bad_data(fill); + } + } + } // end for i_var +} + +//////////////////////////////////////////////////////////////////////// + +static void process_power_spectrum(const vector &in_data) { + + // Process the power spectrum for the full input domain + for(int i_var=0; i_var < conf_info.get_n_data(); i_var++) { + + // Check skip + if(conf_info.ps_info[i_var].skip) continue; + + // Process U/V for kinetic energy + if(in_data[i_var].uv_flag) { + + // Apply the discrete cosine transforms + DataPlane u_dct_dp(dct_typeII(in_data[i_var].u_dp)); + DataPlane v_dct_dp(dct_typeII(in_data[i_var].v_dp)); + + // Compute the radial energy + vector u_re = radial_energy(u_dct_dp); + vector v_re = radial_energy(v_dct_dp); + + // Combine the components + vector re(u_re.size()); + for(size_t i=0; i re = radial_energy(dct_dp); + + // Sum the radial energy + accumulate(power_info[i_var].power, re); + } + } // end for i_var +} + +//////////////////////////////////////////////////////////////////////// + +static void process_error_power_spectrum(const vector &in_data) { + + // Process the power spectrum for the full input domain + for(int i_var=0; i_var < conf_info.get_n_data(); i_var++) { + + // Check skip + if(conf_info.ps_info[i_var].skip) continue; + + const VarInfo *i_vinfo = conf_info.data_info[i_var]; + + for(int j_var=i_var+1; j_var < conf_info.get_n_data(); j_var++) { + + // Check skip + if(conf_info.ps_info[j_var].skip) continue; + + const VarInfo *j_vinfo = conf_info.data_info[j_var]; + + // Process U/V for kinetic energy + if(in_data[i_var].uv_flag && in_data[j_var].uv_flag) { + + // Compute difference fields + DataPlane u_diff_dp(subtract(in_data[i_var].u_dp, + in_data[j_var].u_dp)); + DataPlane v_diff_dp(subtract(in_data[i_var].v_dp, + in_data[j_var].v_dp)); + + // Apply the discrete cosine transforms + DataPlane u_dct_dp(dct_typeII(u_diff_dp)); + DataPlane v_dct_dp(dct_typeII(v_diff_dp)); + + // Compute the radial energy + vector u_re = radial_energy(u_dct_dp); + vector v_re = radial_energy(v_dct_dp); + + // Combine the components + vector re(u_re.size()); + for(size_t i=0; i re = radial_energy(dct_dp); + + // Sum the radial energy + accumulate(power_info[i_var].error_power[j_var], re); + } + } // end for j_var + } // end for i_var +} + +//////////////////////////////////////////////////////////////////////// + +static void accumulate(vector &sum, const vector &cur) { + + // Initialize the sum if needed + if(sum.empty()) { + sum = cur; + } + // Otherwise, accumulate values + else { + + // Must be the same size + if(sum.size() != cur.size()) { + mlog << Error << "\naccumulate() -> " + << "vector lengths do not match (" << sum.size() + << " != " << cur.size() << ")!\n\n"; + exit(1); + } + + // Increment each element + for(int i=0; i hist2d_i_sum(i_data->n_bins(), 0); - vector hist2d_j_sum(j_data->n_bins(), 0); + vector hist2d_i_sum(i_vinfo->n_bins(), 0); + vector hist2d_j_sum(j_vinfo->n_bins(), 0); - for(int i=0; in_bins(); i++) { - for(int j=0; jn_bins(); j++) { + for(int i=0; in_bins(); i++) { + for(int j=0; jn_bins(); j++) { - int n = i * j_data->n_bins() + j; + int n = i * j_vinfo->n_bins() + j; // Increment sums hist2d_ij_sum += i_diag->hist2d[j_var][n]; @@ -584,15 +784,15 @@ static void process_info_theory() { } // end for i // Compute probabilities and acccumulate mutual information - for(int i=0; in_bins(); i++) { + for(int i=0; in_bins(); i++) { auto p_i = (double) hist2d_i_sum[i] / (double) hist2d_ij_sum; - for(int j=0; jn_bins(); j++) { + for(int j=0; jn_bins(); j++) { auto p_j = (double) hist2d_j_sum[j] / (double) hist2d_ij_sum; - int n = i * j_data->n_bins() + j; + int n = i * j_vinfo->n_bins() + j; auto p_ij = (double) i_diag->hist2d[j_var][n] / (double) hist2d_ij_sum; @@ -613,6 +813,106 @@ static void process_info_theory() { //////////////////////////////////////////////////////////////////////// +static void read_series_data(int i_series, VarInfo *i_vinfo, + const StringArray &in_files, + const GrdFileType in_ftype, + InputDataInfo &in_data) { + Grid cur_grid; + + // Read the requested field + get_series_entry(i_series, i_vinfo, in_files, in_ftype, + in_data.dp, cur_grid); + + // Regrid, if needed + regrid_data(i_vinfo, cur_grid, in_data.dp); + + // Check for kinetic energy + in_data.uv_flag = i_vinfo->is_kinetic_energy(); + + // Read U/V components for kinetic energy + if(in_data.uv_flag) { + if(!read_wind_series(i_vinfo->wind_info().u_wind, + i_series, i_vinfo, in_files, in_ftype, in_data.u_dp) || + !read_wind_series(i_vinfo->wind_info().v_wind, + i_series, i_vinfo, in_files, in_ftype, in_data.v_dp)) { + mlog << Error << "\nread_series_data() -> " + << "trouble reading U/V wind components " + << "for kinetic energy field \"" + << i_vinfo->magic_str() << "\"!\n\n"; + exit(1); + } + } + + // Initialize time ranges + auto dp = &in_data.dp; + if(valid_beg == 0 && valid_end == 0) { + init_beg = init_end = dp->init(); + valid_beg = valid_end = dp->valid(); + lead_beg = lead_end = dp->lead(); + } + // Update time ranges + else { + if(dp->init() < init_beg) init_beg = dp->init(); + if(dp->init() > init_end) init_end = dp->init(); + if(dp->valid() < valid_beg) valid_beg = dp->valid(); + if(dp->valid() > valid_end) valid_end = dp->valid(); + if(dp->lead() < lead_beg) lead_beg = dp->lead(); + if(dp->lead() > lead_end) lead_end = dp->lead(); + } +} + +//////////////////////////////////////////////////////////////////////// + +static bool read_wind_series(const StringArray &wind_names, + int i_series, VarInfo *i_vinfo, + const StringArray &in_files, + const GrdFileType in_ftype, + DataPlane &wind_dp) { + bool found = false; + auto wind_vinfo = i_vinfo->clone(); + Grid cur_grid; + + // Loop over possible wind names + for(int i=0; ireset_dict_with_name(wind_names[i].c_str()) && + get_series_entry(i_series, wind_vinfo.get(), in_files, in_ftype, + wind_dp, cur_grid)) { + found = true; + + mlog << Debug(3) << "Found matching wind field \"" + << wind_vinfo->magic_str() << "\".\n"; + + // Regrid, if needed + regrid_data(i_vinfo, cur_grid, wind_dp); + + break; + } + } + + return found; +} + +//////////////////////////////////////////////////////////////////////// + +static void regrid_data(VarInfo *vinfo, const Grid &cur_grid, + DataPlane &dp) { + + // Check for grid match + if(cur_grid == grid) return; + + mlog << Debug(2) + << "Regridding field " << vinfo->magic_str_attr() + << " to the verification grid using " + << vinfo->regrid().get_str() << ".\n"; + dp = met_regrid(dp, cur_grid, grid, vinfo->regrid()); + + return; +} + +//////////////////////////////////////////////////////////////////////// + static ConcatString get_nc_var_str(const VarInfo *info, int index) { ConcatString cs; @@ -630,6 +930,21 @@ static ConcatString get_nc_var_str(const VarInfo *info, int index) { //////////////////////////////////////////////////////////////////////// +static ConcatString get_nc_att_str(const ConcatString &cs1, + const ConcatString &cs2) { + + // Return one if equal + if(cs1 == cs2) return cs1; + + // Otherwise, include both strings + ConcatString cs(cs1); + cs << " and " << cs2; + + return cs; +} + +//////////////////////////////////////////////////////////////////////// + static void setup_nc_file(void) { // Create NetCDF file @@ -685,6 +1000,12 @@ static void setup_nc_file(void) { int mask_size = conf_info.mask_mp[i_mask].count(); mask_size_var.putVar(offsets, counts, &mask_size); } + + // Add the power spectra dimension + if(conf_info.nc_info.do_power_spectrum) { + wavenumber_dim = add_dim(nc_out, "wavenumber", + (long) min(grid.nx(), grid.ny())); + } } //////////////////////////////////////////////////////////////////////// @@ -705,6 +1026,21 @@ static void write_nc_var_int(const char *var_name, //////////////////////////////////////////////////////////////////////// +static void add_var_data_atts(NcVar *var, + const ConcatString &long_name_cs, + const ConcatString &level_cs, + const ConcatString &units_cs) { + + // Add variable attributes for long_name, level, and units + if(var) { + add_var_att_local(var, "long_name", long_name_cs); + add_var_att_local(var, "level", level_cs); + add_var_att_local(var, "units", units_cs); + } +} + +//////////////////////////////////////////////////////////////////////// + static void add_var_att_local(NcVar *var, const char *att_name, const ConcatString &att_value) { if(att_value.nonempty()) add_att(var, att_name, att_value.c_str()); @@ -717,15 +1053,15 @@ static void write_hist_bins(void) { for(int i_var=0; i_var < conf_info.get_n_data(); i_var++) { - const VarInfo *i_data = conf_info.data_info[i_var]; + const VarInfo *i_vinfo = conf_info.data_info[i_var]; DiagInfo *i_diag = &diag_info[i_var][0]; // Define NetCDF variable name - ConcatString var_str(get_nc_var_str(i_data, i_var+1)); + ConcatString var_str(get_nc_var_str(i_vinfo, i_var+1)); // Define NetCDF dimensions NcDim var_dim = add_dim(nc_out, var_str, - (long) i_data->n_bins()); + (long) i_vinfo->n_bins()); data_var_dims.emplace_back(var_dim); // Create NetCDF variable @@ -745,16 +1081,11 @@ static void write_hist_bins(void) { // Add variable attributes ConcatString cs; cs << cs_erase << "Minimum value of " << var_str << " bin"; - add_var_att_local(&var_min, "long_name", cs); - add_var_att_local(&var_min, "units", i_data->units_attr()); - + add_var_data_atts(&var_min, cs, i_vinfo->level_attr(), i_vinfo->units_attr()); cs << cs_erase << "Maximum value of " << var_str << " bin"; - add_var_att_local(&var_max, "long_name", cs); - add_var_att_local(&var_max, "units", i_data->units_attr()); - + add_var_data_atts(&var_max, cs, i_vinfo->level_attr(), i_vinfo->units_attr()); cs << cs_erase << "Midpoint value of " << var_str << " bin"; - add_var_att_local(&var_mid, "long_name", cs); - add_var_att_local(&var_mid, "units", i_data->units_attr()); + add_var_data_atts(&var_mid, cs, i_vinfo->level_attr(), i_vinfo->units_attr()); // Write bin values for the current variable var_min.putVar(i_diag->bin_min.data()); @@ -773,10 +1104,10 @@ static void write_hist1d(void) { // Define and write 1D histograms for(int i_var=0; i_var < conf_info.get_n_data(); i_var++) { - const VarInfo *i_data = conf_info.data_info[i_var]; + const VarInfo *i_vinfo = conf_info.data_info[i_var]; // Define NetCDF variable name - ConcatString var_str(get_nc_var_str(i_data, i_var+1)); + ConcatString var_str(get_nc_var_str(i_vinfo, i_var+1)); ConcatString var_name("hist_"); var_name << var_str; @@ -790,7 +1121,7 @@ static void write_hist1d(void) { // Add variable attributes ConcatString cs; cs << "Histogram of " << var_str << " values"; - add_var_att_local(&var, "long_name", cs); + add_var_data_atts(&var, cs, i_vinfo->level_attr(), i_vinfo->units_attr()); // Write 1D histogram for each mask for(int i_mask=0; i_mask < conf_info.get_n_mask(); i_mask++) { @@ -800,7 +1131,7 @@ static void write_hist1d(void) { offsets[0] = i_mask; offsets[1] = 0; counts[0] = 1; - counts[1] = i_data->n_bins(); + counts[1] = i_vinfo->n_bins(); var.putVar(offsets, counts, hist); @@ -817,17 +1148,17 @@ static void write_hist2d(void) { // Define and write 2D joint histograms for(int i_var=0; i_var < conf_info.get_n_data(); i_var++) { - const VarInfo *i_data = conf_info.data_info[i_var]; + const VarInfo *i_vinfo = conf_info.data_info[i_var]; for(int j_var=i_var+1; j_var < conf_info.get_n_data(); j_var++) { - const VarInfo *j_data = conf_info.data_info[j_var]; + const VarInfo *j_vinfo = conf_info.data_info[j_var]; // Define NetCDF variable name ConcatString var_str; - var_str << get_nc_var_str(i_data, i_var+1) << "_" - << get_nc_var_str(j_data, j_var+1); - ConcatString var_name("hist_"); + var_str << get_nc_var_str(i_vinfo, i_var+1) << "_" + << get_nc_var_str(j_vinfo, j_var+1); + ConcatString var_name("hist_"); var_name << var_str; // Create NetCDF variable @@ -841,7 +1172,11 @@ static void write_hist2d(void) { // Add variable attributes ConcatString cs; cs << "Joint histogram of " << var_str << " values"; - add_var_att_local(&var, "long_name", cs); + add_var_data_atts(&var, cs, + get_nc_att_str(i_vinfo->level_attr(), + j_vinfo->level_attr()), + get_nc_att_str(i_vinfo->units_attr(), + j_vinfo->units_attr())); // Write 2D histogram for each mask for(int i_mask=0; i_mask < conf_info.get_n_mask(); i_mask++) { @@ -852,8 +1187,8 @@ static void write_hist2d(void) { offsets[1] = 0; offsets[2] = 0; counts[0] = 1; - counts[1] = i_data->n_bins(); - counts[2] = j_data->n_bins(); + counts[1] = i_vinfo->n_bins(); + counts[2] = j_vinfo->n_bins(); var.putVar(offsets, counts, hist); @@ -870,10 +1205,10 @@ static void write_info_theory(void) { // Write entropy for each 1D histogram for(int i_var=0; i_var < conf_info.get_n_data(); i_var++) { - const VarInfo *i_data = conf_info.data_info[i_var]; + const VarInfo *i_vinfo = conf_info.data_info[i_var]; // Define NetCDF variable name - ConcatString var_str(get_nc_var_str(i_data, i_var+1)); + ConcatString var_str(get_nc_var_str(i_vinfo, i_var+1)); ConcatString var_name("entropy_"); var_name << var_str; @@ -884,8 +1219,7 @@ static void write_info_theory(void) { // Add variable attributes ConcatString cs; cs << "Entropy value for " << var_str; - add_var_att_local(&var, "long_name", cs); - add_var_att_local(&var, "units", units_cs); + add_var_data_atts(&var, cs, i_vinfo->level_attr(), units_cs); // Store the data vector data(conf_info.get_n_mask()); @@ -901,18 +1235,18 @@ static void write_info_theory(void) { // Write joint entropy and mutual information for each 2D joint histogram for(int i_var=0; i_var < conf_info.get_n_data(); i_var++) { - const VarInfo *i_data = conf_info.data_info[i_var]; + const VarInfo *i_vinfo = conf_info.data_info[i_var]; for(int j_var=i_var+1; j_var < conf_info.get_n_data(); j_var++) { - const VarInfo *j_data = conf_info.data_info[j_var]; + const VarInfo *j_vinfo = conf_info.data_info[j_var]; ConcatString var_str; - var_str << get_nc_var_str(i_data, i_var+1) << "_" - << get_nc_var_str(j_data, j_var+1); + var_str << get_nc_var_str(i_vinfo, i_var+1) << "_" + << get_nc_var_str(j_vinfo, j_var+1); // Define NetCDF variable names - ConcatString je_var_name("joint_entropy_"); + ConcatString je_var_name("joint_entropy_"); je_var_name << var_str; ConcatString mi_var_name("mutual_information_"); mi_var_name << var_str; @@ -923,26 +1257,163 @@ static void write_info_theory(void) { NcVar mi_var = add_var(nc_out, mi_var_name, ncFloat, mask_dim, deflate_level); + // Level attribute + ConcatString level_cs(get_nc_att_str(i_vinfo->level_attr(), + j_vinfo->level_attr())); + // Add variable attributes ConcatString cs; cs << "Joint entropy value for " << var_str; - add_var_att_local(&je_var, "long_name", cs); - add_var_att_local(&je_var, "units", units_cs); + add_var_data_atts(&je_var, cs, level_cs, units_cs); cs << cs_erase << "Mutual information value for " << var_str; - add_var_att_local(&mi_var, "long_name", cs); - add_var_att_local(&mi_var, "units", units_cs); + add_var_data_atts(&mi_var, cs, level_cs, units_cs); // Store the data vector je_data(conf_info.get_n_mask()); - vector mi_data(conf_info.get_n_mask()); + vector mi_vinfo(conf_info.get_n_mask()); for(int i_mask=0; i_mask < conf_info.get_n_mask(); i_mask++) { je_data[i_mask] = diag_info[i_var][i_mask].joint_entropy[j_var]; - mi_data[i_mask] = diag_info[i_var][i_mask].mutual_information[j_var]; + mi_vinfo[i_mask] = diag_info[i_var][i_mask].mutual_information[j_var]; } // Write the data je_var.putVar(je_data.data()); - mi_var.putVar(mi_data.data()); + mi_var.putVar(mi_vinfo.data()); + + } // end for j_var + } // end for i_var +} + +//////////////////////////////////////////////////////////////////////// + +static void write_wavelengths(void) { + + // Define wavenumber values + int n_waves = wavenumber_dim.getSize(); + vector wavenumbers(n_waves); + iota(wavenumbers.begin(), wavenumbers.end(), 1); + + // Define wavelength values + vector wavelengths(n_waves); + double grid_res_km = get_grid_res_km(grid); + for(int i=0; iunits_attr() << ")^2"; + add_var_data_atts(&var, cs, i_vinfo->level_attr(), units_cs); + add_var_att_local(&var, "mask", full_domain_str); + + // Write power spectrum + PowerInfo *p_diag = &power_info[i_var]; + + // Divide sums by the series length + for(auto &x : p_diag->power) x /= n_series; + + // Write the mean power data + var.putVar(p_diag->power.data()); + + } // end for i_var +} + +//////////////////////////////////////////////////////////////////////// + +static void write_error_power_spectrum(void) { + + // Define and write the error power spectrum + for(int i_var=0; i_var < conf_info.get_n_data(); i_var++) { + + // Check skip + if(conf_info.ps_info[i_var].skip) continue; + + const VarInfo *i_vinfo = conf_info.data_info[i_var]; + + for(int j_var=i_var+1; j_var < conf_info.get_n_data(); j_var++) { + + // Check skip + if(conf_info.ps_info[j_var].skip) continue; + + const VarInfo *j_vinfo = conf_info.data_info[j_var]; + + // Define NetCDF variable name + ConcatString i_var_str(get_nc_var_str(i_vinfo, i_var+1)); + ConcatString j_var_str(get_nc_var_str(j_vinfo, j_var+1)); + ConcatString var_name("error_power_spectrum_"); + var_name << i_var_str << "_" << j_var_str; + + // Create NetCDF variable + NcVar var = add_var(nc_out, var_name, ncFloat, wavenumber_dim, + deflate_level); + + // Level attribute + ConcatString level_cs(get_nc_att_str(i_vinfo->level_attr(), + j_vinfo->level_attr())); + + // Units attribute + ConcatString units_cs("("); + units_cs << get_nc_att_str(i_vinfo->units_attr(), + j_vinfo->units_attr()) << ")^2"; + + // Add variable attributes + ConcatString cs("Power spectrum of errors for "); + cs << i_var_str << " minus " << j_var_str; + add_var_data_atts(&var, cs, level_cs, units_cs); + add_var_att_local(&var, "mask", full_domain_str); + + // Write power spectrum + PowerInfo *p_diag = &power_info[i_var]; + + // Divide sums by the series length + for(auto &x : p_diag->error_power[j_var]) x /= n_series; + + // Write the mean error power + var.putVar(p_diag->error_power[j_var].data()); } // end for j_var } // end for i_var @@ -952,20 +1423,16 @@ static void write_info_theory(void) { static Met2dDataFile *get_mtddf(const StringArray &file_list, const int i_field) { - Met2dDataFile *mtddf = nullptr; - Dictionary *dict = nullptr; - Dictionary i_dict; - GrdFileType file_type; int i; // Conf: data.field - dict = conf_info.conf.lookup_array(conf_key_data_field); + Dictionary *dict = conf_info.conf.lookup_array(conf_key_data_field); // Get the i-th data.field entry - i_dict = parse_conf_i_vx_dict(dict, i_field); + Dictionary i_dict = parse_conf_i_vx_dict(dict, i_field); // Look for file_type in the i-th data.field entry - file_type = parse_conf_file_type(&i_dict); + GrdFileType file_type = parse_conf_file_type(&i_dict); // Find the first file that actually exists for(i=0; i < file_list.n(); i++) { @@ -980,6 +1447,7 @@ static Met2dDataFile *get_mtddf(const StringArray &file_list, } // Read first valid file + Met2dDataFile *mtddf = nullptr; if(!(mtddf = Met2dDataFileFactory::new_met_2d_data_file( file_list[i].c_str(), file_type))) { mlog << Error << "\nget_mtddf() -> " @@ -1008,6 +1476,57 @@ static void clean_up(void) { return; } +//////////////////////////////////////////////////////////////////////// +// +// Estimate the grid spacking in km +// +//////////////////////////////////////////////////////////////////////// + +static double get_grid_res_km(const Grid &g) { + double res_km; + + // Use the grid scale, if well-defined + if(g.scale_km() > 0) { + res_km = g.scale_km(); + } + // Otherwise, determine the scale from the grid points + else { + + int nx = grid.nx(); + int ny = grid.ny(); + + // X-spacing at the center + double lat1; + double lon1; + g.xy_to_latlon(0, (ny - 1)/2.0, lat1, lon1); + double lat2; + double lon2; + g.xy_to_latlon(nx - 1, (ny - 1)/2.0, lat2, lon2); + double dx_km = gc_dist(lat1, lon1, lat2, lon2) / (nx - 1); + + // Y-spacing at the center + g.xy_to_latlon((nx - 1)/2.0, 0, lat1, lon1); + g.xy_to_latlon((nx - 1)/2.0, ny - 1, lat2, lon2); + double dy_km = gc_dist(lat1, lon1, lat2, lon2) / (ny - 1); + + // Log message when grid spacing differs + if(!is_eq(dx_km, dy_km, 0.1)) { + mlog << Debug(3) << "Grid spacing in the X (" << dx_km + << " km) and Y (" << dy_km << " km) dimensions differ.\n"; + } + res_km = min(dx_km, dy_km); + } + + // Attempt to round to the nearest integer + if(is_eq(res_km, (double) nint(res_km), 0.1)) { + res_km = (double) nint(res_km); + } + + mlog << Debug(3) << "Using grid spacing of " << res_km << " km.\n"; + + return res_km; +} + //////////////////////////////////////////////////////////////////////// __attribute__((noreturn)) static void usage(int exit_code) { @@ -1053,8 +1572,8 @@ __attribute__((noreturn)) static void usage(int exit_code) { //////////////////////////////////////////////////////////////////////// static void set_data_files(const StringArray & a) { - data_files.emplace_back(a); if(!data_files.empty()) multiple_data_sources = true; + data_files.emplace_back(a); } //////////////////////////////////////////////////////////////////////// diff --git a/src/tools/other/grid_diag/grid_diag.h b/src/tools/other/grid_diag/grid_diag.h index 810b9aee50..389f21d5ad 100644 --- a/src/tools/other/grid_diag/grid_diag.h +++ b/src/tools/other/grid_diag/grid_diag.h @@ -87,6 +87,7 @@ static GridDiagConfInfo conf_info; // Output NetCDF file static netCDF::NcFile *nc_out = nullptr; netCDF::NcDim mask_dim; +netCDF::NcDim wavenumber_dim; std::vector data_var_dims; int deflate_level; @@ -108,6 +109,14 @@ static Grid grid; // Input files static Met2dDataFile *data_mtddf = nullptr; +// Struct to store scalar and vector input data +struct InputDataInfo { + DataPlane dp; + DataPlane u_dp; + DataPlane v_dp; + bool uv_flag = false; +}; + // Struct to store diagnostic info for each field and masking region struct DiagInfo { @@ -117,7 +126,7 @@ struct DiagInfo { std::vector bin_mid; double bin_delta; - // Input data info + // Range of input data values double var_min; double var_max; @@ -134,6 +143,17 @@ struct DiagInfo { // DiagInfo objects [n_data][n_mask] std::vector > diag_info; +// Struct to store power spectrum information +struct PowerInfo { + + // Power spectrum + std::vector power; + std::map > error_power; +}; + +// PowerInfo objects [n_data] +std::vector power_info; + // Series length static int n_series = bad_data_int; diff --git a/src/tools/other/grid_diag/grid_diag_conf_info.cc b/src/tools/other/grid_diag/grid_diag_conf_info.cc index 068e9f2d3d..a41e1b8568 100644 --- a/src/tools/other/grid_diag/grid_diag_conf_info.cc +++ b/src/tools/other/grid_diag/grid_diag_conf_info.cc @@ -46,7 +46,8 @@ void GridDiagNcOutInfo::clear() { bool GridDiagNcOutInfo::all_false() const { - bool status = do_hist1d || do_hist2d || do_info_theory; + bool status = do_hist1d || do_hist2d || + do_info_theory || do_power_spectrum; return !status; } @@ -55,9 +56,10 @@ bool GridDiagNcOutInfo::all_false() const { void GridDiagNcOutInfo::set_all_false() { - do_hist1d = false; - do_hist2d = false; - do_info_theory = false; + do_hist1d = false; + do_hist2d = false; + do_info_theory = false; + do_power_spectrum = false; return; } @@ -66,9 +68,10 @@ void GridDiagNcOutInfo::set_all_false() { void GridDiagNcOutInfo::set_all_true() { - do_hist1d = true; - do_hist2d = true; - do_info_theory = true; + do_hist1d = true; + do_hist2d = true; + do_info_theory = true; + do_power_spectrum = true; return; } @@ -113,6 +116,7 @@ void GridDiagConfInfo::clear() { if(info) { delete info; info = nullptr; } } data_info.clear(); + ps_info.clear(); return; } @@ -153,11 +157,13 @@ void GridDiagConfInfo::set_n_data() { // Allocate space based on the number of verification tasks data_info.resize(n_data, nullptr); + ps_info.resize(n_data); } //////////////////////////////////////////////////////////////////////// -void GridDiagConfInfo::process_config(vector file_types) { +void GridDiagConfInfo::process_config(vector file_types, + Grid &data_grid) { ConcatString s; StringArray sa; Dictionary i_dict; @@ -176,7 +182,7 @@ void GridDiagConfInfo::process_config(vector file_types) { Dictionary *dict = conf.lookup_array(conf_key_data_field); // Parse the data field information - for(int i=0; i<(int) data_info.size(); i++) { + for(int i=0; i 1 ? @@ -191,6 +197,11 @@ void GridDiagConfInfo::process_config(vector file_types) { // Set the current dictionaries data_info[i]->set_dict(i_dict); + // Update the grid definition, if needed + if(!data_grid.is_set() && data_info[i]->grid_attr().is_set()) { + data_grid = data_info[i]->grid_attr(); + } + // Dump the contents of the current VarInfo if(mlog.verbosity_level() >= 5) { mlog << Debug(5) @@ -209,6 +220,9 @@ void GridDiagConfInfo::process_config(vector file_types) { exit(1); } + // Conf: power_spectrum + ps_info[i] = parse_conf_power_spectrum(&i_dict); + } // end for i // Conf: output_flag @@ -249,9 +263,10 @@ void GridDiagConfInfo::parse_output_flag() { // Parse the various entries auto d = e->dict_value(); - nc_info.do_hist1d = d->lookup_bool(conf_key_hist1d_flag); - nc_info.do_hist2d = d->lookup_bool(conf_key_hist2d_flag); - nc_info.do_info_theory = d->lookup_bool(conf_key_info_theory_flag); + nc_info.do_hist1d = d->lookup_bool(conf_key_hist1d_flag); + nc_info.do_hist2d = d->lookup_bool(conf_key_hist2d_flag); + nc_info.do_info_theory = d->lookup_bool(conf_key_info_theory_flag); + nc_info.do_power_spectrum = d->lookup_bool(conf_key_power_spectrum_flag); return; } diff --git a/src/tools/other/grid_diag/grid_diag_conf_info.h b/src/tools/other/grid_diag/grid_diag_conf_info.h index d0012d92a5..2fb1393743 100644 --- a/src/tools/other/grid_diag/grid_diag_conf_info.h +++ b/src/tools/other/grid_diag/grid_diag_conf_info.h @@ -32,6 +32,7 @@ struct GridDiagNcOutInfo { bool do_hist1d; bool do_hist2d; bool do_info_theory; + bool do_power_spectrum; GridDiagNcOutInfo(); @@ -61,6 +62,9 @@ class GridDiagConfInfo { std::vector data_info; // VarInfo pointer array [n_data] + // Power spectrum options + std::vector ps_info; // [n_data] + // Masking region names and MaskPlanes StringArray mask_name; std::vector mask_mp; @@ -74,7 +78,7 @@ class GridDiagConfInfo { void read_config(const char *, const char *); void set_n_data(); - void process_config(std::vector); + void process_config(std::vector, Grid &); void parse_output_flag(); void process_masks(const Grid &); diff --git a/src/tools/other/point2grid/point2grid.cc b/src/tools/other/point2grid/point2grid.cc index ac8278c6a3..470c6cb0f2 100644 --- a/src/tools/other/point2grid/point2grid.cc +++ b/src/tools/other/point2grid/point2grid.cc @@ -360,10 +360,15 @@ static void process_command_line(int argc, char **argv) { StringArray var_names; auto vinfo = VarInfoFactory::new_var_info(FileType_NcMet); for(int i=0; iclear(); + // Populate the VarInfo object using the config string + config.read(replace_path(config_const_filename).c_str()); config.read_string(FieldSA[i].c_str()); vinfo->set_dict(config); + vname = vinfo->name(); if (var_names.has(vname)) { mlog << Error << "\n" << method_name @@ -628,10 +633,15 @@ static int get_obs_type(NcFile *nc) { bool has_attr_grid = false; auto vinfo = VarInfoFactory::new_var_info(FileType_NcCF); for(int i=0; iclear(); + // Populate the VarInfo object using the config string + config.read(replace_path(config_const_filename).c_str()); config.read_string(FieldSA[i].c_str()); vinfo->set_dict(config); + if (vinfo->grid_attr().is_set()) { has_attr_grid = true; break; @@ -816,6 +826,7 @@ void process_point_met_data(MetPointData *met_point_obs, MetConfig &config, VarI vinfo->clear(); // Populate the VarInfo object using the config string + config.read(replace_path(config_const_filename).c_str()); config.read_string(FieldSA[i].c_str()); vinfo->set_dict(config); @@ -1191,7 +1202,7 @@ void process_point_met_data(MetPointData *met_point_obs, MetConfig &config, VarI mlog << Debug(3) << "Using " << var_count << " " << vinfo->name() << " observations to populate " << to_count << " of " << to_grid.nxy() << " grid points.\n"; - if (0 < filtered_count ) mlog << log_msg << "\n"; + if (0 < filtered_count ) mlog << Debug(3) << log_msg << "\n"; } } // end for i @@ -1316,11 +1327,15 @@ static void process_point_nccf_file(NcFile *nc_in, MetConfig &config, if (0 < FieldSA.n() && !user_defined_latlon) { ConcatString coordinates_value; auto var_info = VarInfoNcCF(*(VarInfoNcCF *)vinfo); + // Initialize var_info.clear(); + // Populate the VarInfo object using the config string + config.read(replace_path(config_const_filename).c_str()); config.read_string(FieldSA[0].c_str()); var_info.set_dict(config); + NcVar var_data = get_nc_var(nc_in, var_info.name().c_str()); if (get_nc_att_value(&var_data, coordinates_att_name, coordinates_value)) { StringArray sa = coordinates_value.split(" "); @@ -1408,6 +1423,7 @@ static void process_point_nccf_file(NcFile *nc_in, MetConfig &config, var_cell_mapping.clear(); // Populate the VarInfo object using the config string + config.read(replace_path(config_const_filename).c_str()); config.read_string(FieldSA[i].c_str()); vinfo->set_dict(config); @@ -1819,6 +1835,7 @@ static void process_goes_file(NcFile *nc_in, MetConfig &config, VarInfo *vinfo, vinfo->clear(); // Populate the VarInfo object using the config string + config.read(replace_path(config_const_filename).c_str()); config.read_string(FieldSA[i].c_str()); vinfo->set_dict(config);