diff --git a/docs/requirements.txt b/docs/requirements.txt index dd7a29c..700910c 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -16,5 +16,5 @@ # harmony-py~=0.4.10 netCDF4~=1.6.4 -notebook~=7.0.4 +notebook~=7.2.2 xarray~=2023.9.0 diff --git a/hoss/coordinate_utilities.py b/hoss/coordinate_utilities.py new file mode 100644 index 0000000..1550d81 --- /dev/null +++ b/hoss/coordinate_utilities.py @@ -0,0 +1,220 @@ +""" This module contains utility functions used for + coordinate variables and functions to convert the + coordinate variable data to projected x/y dimension values +""" + +import numpy as np +from netCDF4 import Dataset + +# from numpy import ndarray +from varinfo import VariableFromDmr, VarInfoFromDmr + +from hoss.exceptions import ( + IncompatibleCoordinateVariables, + InvalidCoordinateDataset, + InvalidCoordinateVariable, + MissingCoordinateVariable, + MissingVariable, +) + + +def get_projected_dimension_names(varinfo: VarInfoFromDmr, variable_name: str) -> str: + """returns the x-y projection variable names that would + match the group of the input variable. The 'projected_y' dimension + and 'projected_x' names are returned with the group pathname + + """ + variable = varinfo.get_variable(variable_name) + + if variable is not None: + projected_dimension_names = [ + f'{variable.group_path}/projected_y', + f'{variable.group_path}/projected_x', + ] + else: + raise MissingVariable(variable_name) + + return projected_dimension_names + + +def get_projected_dimension_names_from_coordinate_variables( + varinfo: VarInfoFromDmr, + variable_name: str, +) -> list[str]: + """ + Returns the projected dimensions names from coordinate variables + """ + latitude_coordinates, longitude_coordinates = get_coordinate_variables( + varinfo, [variable_name] + ) + + if len(latitude_coordinates) == 1 and len(longitude_coordinates) == 1: + projected_dimension_names = get_projected_dimension_names( + varinfo, latitude_coordinates[0] + ) + + # if the override is the variable + elif ( + varinfo.get_variable(variable_name).is_latitude() + or varinfo.get_variable(variable_name).is_longitude() + ): + projected_dimension_names = get_projected_dimension_names( + varinfo, variable_name + ) + else: + projected_dimension_names = [] + return projected_dimension_names + + +def get_variables_with_anonymous_dims( + varinfo: VarInfoFromDmr, variables: set[str] +) -> set[str]: + """ + returns a set of variables without any dimensions + associated with it + """ + + return set( + variable + for variable in variables + if (len(varinfo.get_variable(variable).dimensions) == 0) + or (any_absent_dimension_variables(varinfo, variable)) + ) + + +def any_absent_dimension_variables(varinfo: VarInfoFromDmr, variable: str) -> bool: + """returns variable with fake dimensions - dimensions + that have been created by opendap, but are not really + dimension variables + """ + return any( + varinfo.get_variable(dimension) is None + for dimension in varinfo.get_variable(variable).dimensions + ) + + +def get_coordinate_variables( + varinfo: VarInfoFromDmr, + requested_variables: list, +) -> tuple[list, list]: + """This function returns latitude and longitude variables listed in the + CF-Convention coordinates metadata attribute. It returns them in a specific + order [latitude, longitude]" + """ + + coordinate_variables_list = varinfo.get_references_for_attribute( + requested_variables, 'coordinates' + ) + latitude_coordinate_variables = [ + coordinate + for coordinate in coordinate_variables_list + if varinfo.get_variable(coordinate).is_latitude() + ] + + longitude_coordinate_variables = [ + coordinate + for coordinate in coordinate_variables_list + if varinfo.get_variable(coordinate).is_longitude() + ] + + return latitude_coordinate_variables, longitude_coordinate_variables + + +def get_row_col_sizes_from_coordinate_datasets( + lat_arr: np.ndarray, + lon_arr: np.ndarray, +) -> tuple[int, int]: + """ + This function returns the row and column sizes of the coordinate datasets + + """ + # ToDo - if the coordinates are 3D + if lat_arr.ndim > 1 and lon_arr.shape == lat_arr.shape: + col_size = lat_arr.shape[1] + row_size = lat_arr.shape[0] + elif ( + lat_arr.ndim == 1 + and lon_arr.ndim == 1 + and lat_arr.size > 0 + and lon_arr.size > 0 + ): + # Todo: The ordering needs to be checked + col_size = lon_arr.size + row_size = lat_arr.size + else: + raise IncompatibleCoordinateVariables(lon_arr.shape, lat_arr.shape) + return row_size, col_size + + +def get_coordinate_array( + prefetch_dataset: Dataset, + coordinate_name: str, +) -> np.ndarray: + """This function returns the `numpy` array from a + coordinate dataset. + + """ + try: + coordinate_array = prefetch_dataset[coordinate_name][:] + except IndexError as exception: + raise MissingCoordinateVariable(coordinate_name) from exception + + return coordinate_array + + +def get_1D_dim_array_data_from_dimvalues( + dim_values: np.ndarray, dim_indices: np.ndarray, dim_size: int +) -> np.ndarray: + """ + return a full dimension data array based on the 2 projected points and + grid size + """ + + if (dim_indices[1] != dim_indices[0]) and (dim_values[1] != dim_values[0]): + dim_resolution = (dim_values[1] - dim_values[0]) / ( + dim_indices[1] - dim_indices[0] + ) + else: + raise InvalidCoordinateDataset(dim_values[0], dim_indices[0]) + + dim_min = dim_values[0] - (dim_resolution * dim_indices[0]) + dim_max = dim_values[1] + (dim_resolution * (dim_size - 1 - dim_indices[1])) + return np.linspace(dim_min, dim_max, dim_size) + + +def get_valid_indices( + coordinate_row_col: np.ndarray, coordinate: VariableFromDmr +) -> np.ndarray: + """ + Returns indices of a valid array without fill values if the fill + value is provided. If it is not provided, we check for valid values + for latitude and longitude + """ + # get_attribute_value returns a value of type `str` + coordinate_fill = coordinate.get_attribute_value('_FillValue') + if coordinate_fill is not None: + is_not_fill = ~np.isclose(coordinate_row_col, float(coordinate_fill)) + else: + # Creates an entire array of `True` values. + is_not_fill = np.ones_like(coordinate_row_col, dtype=bool) + + if coordinate.is_longitude(): + valid_indices = np.where( + np.logical_and( + is_not_fill, + np.logical_and( + coordinate_row_col >= -180.0, coordinate_row_col <= 360.0 + ), + ) + )[0] + elif coordinate.is_latitude(): + valid_indices = np.where( + np.logical_and( + is_not_fill, + np.logical_and(coordinate_row_col >= -90.0, coordinate_row_col <= 90.0), + ) + )[0] + else: + valid_indices = np.empty((0, 0)) + + return valid_indices diff --git a/hoss/exceptions.py b/hoss/exceptions.py index 1cb1439..26ba3f8 100644 --- a/hoss/exceptions.py +++ b/hoss/exceptions.py @@ -57,7 +57,7 @@ class InvalidRequestedRange(CustomError): def __init__(self): super().__init__( 'InvalidRequestedRange', - 'Input request specified range outside supported ' 'dimension range', + 'Input request specified range outside supported dimension range', ) @@ -108,6 +108,79 @@ def __init__(self): ) +class MissingVariable(CustomError): + """This exception is raised when HOSS tries to get variables and + they are missing or empty. + + """ + + def __init__(self, referring_variable): + super().__init__( + 'MissingVariable', + f'"{referring_variable}" is ' 'not present in source granule file.', + ) + + +class MissingCoordinateVariable(CustomError): + """This exception is raised when HOSS tries to get latitude and longitude + variables and they are missing or empty. These variables are referred to + in the science variables with coordinate attributes. + + """ + + def __init__(self, referring_variable): + super().__init__( + 'MissingCoordinateVariable', + f'Coordinate: "{referring_variable}" is ' + 'not present in source granule file.', + ) + + +class InvalidCoordinateVariable(CustomError): + """This exception is raised when HOSS tries to get latitude and longitude + variables and they have fill values to the extent that it cannot be used. + These variables are referred in the science variables with coordinate attributes. + + """ + + def __init__(self, referring_variable): + super().__init__( + 'InvalidCoordinateVariable', + f'Coordinate: "{referring_variable}" is ' + 'not valid in source granule file.', + ) + + +class IncompatibleCoordinateVariables(CustomError): + """This exception is raised when HOSS tries to get latitude and longitude + coordinate variable and they do not match in shape or have a size of 0. + + """ + + def __init__(self, longitude_shape, latitude_shape): + super().__init__( + 'IncompatibleCoordinateVariables', + f'Longitude coordinate shape: "{longitude_shape}"' + f'does not match the latitude coordinate shape: "{latitude_shape}"', + ) + + +class InvalidCoordinateDataset(CustomError): + """This exception is raised when the two values passed to + the function computing the resolution are equal. This could + occur when there are too many fill values and distinct valid + indices could not be obtained + + """ + + def __init__(self, dim_value, dim_index): + super().__init__( + 'InvalidCoordinateDataset', + 'Cannot compute the dimension resolution for ' + f'dim_value: "{dim_value}" dim_index: "{dim_index}"', + ) + + class UnsupportedShapeFileFormat(CustomError): """This exception is raised when the shape file included in the input Harmony message is not GeoJSON. diff --git a/hoss/hoss_config.json b/hoss/hoss_config.json index c214d6b..7a4caea 100644 --- a/hoss/hoss_config.json +++ b/hoss/hoss_config.json @@ -59,24 +59,6 @@ "Epoch": "2018-01-01T00:00:00.000000" } ], - "Grid_Mapping_Data": [ - { - "Grid_Mapping_Dataset_Name": "EASE2_Global", - "grid_mapping_name": "lambert_cylindrical_equal_area", - "standard_parallel": 30.0, - "longitude_of_central_meridian": 0.0, - "false_easting": 0.0, - "false_northing": 0.0 - }, - { - "Grid_Mapping_Dataset_Name": "EASE2_Polar", - "grid_mapping_name": "lambert_azimuthal_equal_area", - "longitude_of_projection_origin": 0.0, - "latitude_of_projection_origin": 90.0, - "false_easting": 0.0, - "false_northing": 0.0 - } - ], "CF_Overrides": [ { "Applicability": { @@ -161,34 +143,58 @@ { "Applicability": { "Mission": "SMAP", - "ShortNamePath": "SPL3FT(P|P_E)" + "ShortNamePath": "SPL3FT(P|P_E)", + "Variable_Pattern": "(?i).*global.*" }, - "Applicability_Group": [ + "Attributes": [ { - "Applicability": { - "Variable_Pattern": "(?i).*global.*" - }, - "Attributes": [ - { - "Name": "Grid_Mapping", - "Value": "EASE2_Global" - } - ], - "_Description": "Some versions of these collections omit global grid mapping information" - }, + "Name": "grid_mapping", + "Value": "/EASE2_global_projection" + } + ], + "_Description": "SMAP L3 collections omit global grid mapping information" + }, + { + "Applicability": { + "Mission": "SMAP", + "ShortNamePath": "SPL3FT(P|P_E)", + "Variable_Pattern": "(?i).*polar.*" + }, + "Attributes": [ { - "Applicability": { - "Variable_Pattern": "(?i).*polar.*" - }, - "Attributes": [ - { - "Name": "Grid_Mapping", - "Value": "EASE2_Polar" - } - ], - "_Description": "Some versions of these collections omit polar grid mapping information" + "Name": "grid_mapping", + "Value": "/EASE2_polar_projection" } - ] + ], + "_Description": "SMAP L3 collections omit polar grid mapping information" + }, + { + "Applicability": { + "Mission": "SMAP", + "ShortNamePath": "SPL3SMP_E", + "Variable_Pattern": "Soil_Moisture_Retrieval_Data_(A|P)M/.*" + }, + "Attributes": [ + { + "Name": "grid_mapping", + "Value": "/EASE2_global_projection" + } + ], + "_Description": "SMAP L3 collections omit global grid mapping information" + }, + { + "Applicability": { + "Mission": "SMAP", + "ShortNamePath": "SPL3SMP_E", + "Variable_Pattern": "Soil_Moisture_Retrieval_Data_Polar_(A|P)M/.*" + }, + "Attributes": [ + { + "Name": "grid_mapping", + "Value": "/EASE2_polar_projection" + } + ], + "_Description": "SMAP L3 collections omit polar grid mapping information" }, { "Applicability": { @@ -197,11 +203,84 @@ }, "Attributes": [ { - "Name": "Grid_Mapping", - "Value": "EASE2_Polar" + "Name": "grid_mapping", + "Value": "/EASE2_polar_projection" } ], - "_Description": "Some versions of these collections omit polar grid mapping information" + "_Description": "SMAP L3 collections omit polar grid mapping information" + }, + { + "Applicability": { + "Mission": "SMAP", + "ShortNamePath": "SPL3SM(P|A|AP)|SPL2SMAP_S" + }, + "Attributes": [ + { + "Name": "grid_mapping", + "Value": "/EASE2_global_projection" + } + ], + "_Description": "SMAP L3 collections omit global grid mapping information" + }, + { + "Applicability": { + "Mission": "SMAP", + "ShortNamePath": "SPL3FT(P|P_E)|SPL3SM(P|P_E|A|AP)|SPL2SMAP_S", + "Variable_Pattern": "/EASE2_global_projection" + }, + "Attributes": [ + { + "Name": "grid_mapping_name", + "Value": "lambert_cylindrical_equal_area" + }, + { + "Name":"standard_parallel", + "Value": 30.0 + }, + { + "Name": "longitude_of_central_meridian", + "Value": 0.0 + }, + { + "Name": "false_easting", + "Value": 0.0 + }, + { + "Name": "false_northing", + "Value": 0.0 + } + ], + "_Description": "Provide missing global grid mapping attributes for SMAP L3 collections." + }, + { + "Applicability": { + "Mission": "SMAP", + "ShortNamePath": "SPL3FT(P|P_E)|SPL3SM(P|P_E|A|AP)|SPL2SMAP_S", + "Variable_Pattern": "/EASE2_polar_projection" + }, + "Attributes": [ + { + "Name": "grid_mapping_name", + "Value": "lambert_azimuthal_equal_area" + }, + { + "Name": "longitude_of_projection_origin", + "Value" : 0.0 + }, + { + "Name": "latitude_of_projection_origin", + "Value": 90.0 + }, + { + "Name": "false_easting", + "Value": 0.0 + }, + { + "Name": "false_northing", + "Value": 0.0 + } + ], + "_Description": "Provide missing polar grid mapping attributes for SMAP L3 collections." }, { "Applicability": { @@ -211,12 +290,40 @@ }, "Attributes": [ { - "Name": "_fill", - "Value": "-9999" + "Name": "_FillValue", + "Value": "-9999.0" } ], "_Description": "Ensure metadata fill value matches what is present in arrays." }, + { + "Applicability": { + "Mission": "SMAP", + "ShortNamePath": "SPL3SM(A|P|AP|P_E)", + "Variable_Pattern": "/Soil_Moisture_Retrieval_(Data|Data_AM|Data_Polar_AM)/(latitude|longitude).*" + }, + "Attributes": [ + { + "Name": "_FillValue", + "Value": "-9999.0" + } + ], + "_Description": "Ensure metadata fill value matches what is present in arrays." + }, + { + "Applicability": { + "Mission": "SMAP", + "ShortNamePath": "SPL3SMP", + "Variable_Pattern": "/Soil_Moisture_Retrieval_Data_PM/.*" + }, + "Attributes": [ + { + "Name": "coordinates", + "Value": "/Soil_Moisture_Retrieval_Data_PM/latitude_pm, /Soil_Moisture_Retrieval_Data_PM/longitude_pm" + } + ], + "_Description": "Ensure variables in /Soil_Moisture_Retrieval_Data_PM group point to correct coordinate variables." + }, { "Applicability": { "Mission": "SMAP", diff --git a/pip_requirements.txt b/pip_requirements.txt index 41ba9c5..1bb9bce 100644 --- a/pip_requirements.txt +++ b/pip_requirements.txt @@ -1,6 +1,6 @@ # This file should contain requirements to be installed via Pip. # Open source packages available from PyPI -earthdata-varinfo ~= 1.0.0 +earthdata-varinfo ~= 2.3.0 harmony-service-lib ~= 1.0.25 netCDF4 ~= 1.6.4 numpy ~= 1.24.2 diff --git a/tests/data/SC_SPL3SMP_008.dmr b/tests/data/SC_SPL3SMP_008.dmr new file mode 100644 index 0000000..b45abae --- /dev/null +++ b/tests/data/SC_SPL3SMP_008.dmr @@ -0,0 +1,2778 @@ + + + + 3.21.0-428 + + + 3.21.0-428 + + + libdap-3.21.0-103 + + + +# TheBESKeys::get_as_config() +AllowedHosts=^https?:\/\/ +BES.Catalog.catalog.FollowSymLinks=Yes +BES.Catalog.catalog.RootDirectory=/usr/share/hyrax +BES.Catalog.catalog.TypeMatch=dmrpp:.*\.(dmrpp)$; +BES.Catalog.catalog.TypeMatch+=h5:.*(\.bz2|\.gz|\.Z)?$; +BES.Data.RootDirectory=/dev/null +BES.LogName=./bes.log +BES.UncompressCache.dir=/tmp/hyrax_ux +BES.UncompressCache.prefix=ux_ +BES.UncompressCache.size=500 +BES.module.cmd=/usr/lib64/bes/libdap_xml_module.so +BES.module.dap=/usr/lib64/bes/libdap_module.so +BES.module.dmrpp=/usr/lib64/bes/libdmrpp_module.so +BES.module.h5=/usr/lib64/bes/libhdf5_module.so +BES.modules=dap,cmd,h5,dmrpp +H5.DefaultHandleDimensions=true +H5.EnableCF=false +H5.EnableCheckNameClashing=true + + + + build_dmrpp -c /tmp/bes_conf_IAud -f /usr/share/hyrax/DATA/SMAP_L3_SM_P_20150331_R18290_002.h5 -r /tmp/dmr__0PmwFd -u OPeNDAP_DMRpp_DATA_ACCESS_URL -M + + + + + + + The SMAP observatory houses an L-band radiometer that operates at 1.414 GHz and an L-band radar that operates at 1.225 GHz. The instruments share a rotating reflector antenna with a 6 meter aperture that scans over a 1000 km swath. The bus is a 3 axis stabilized spacecraft that provides momentum compensation for the rotating antenna. + + + 14.60000038 + + + SMAP + + + + + JPL CL#14-2285, JPL 400-1567 + + + SMAP Handbook + + + 2014-07-01 + + + + + JPL CL#14-2285, JPL 400-1567 + + + SMAP Handbook + + + 2014-07-01 + + + + + The SMAP 1.414 GHz L-Band Radiometer + + + L-Band Radiometer + + + SMAP RAD + + + + + The SMAP 1.225 GHz L-Band Radar Instrument + + + L-Band Synthetic Aperture Radar + + + SMAP SAR + + + + + JPL CL#14-2285, JPL 400-1567 + + + SMAP Handbook + + + 2014-07-01 + + + + + + soil_moisture + + + + Percentage of EASE2 grid cells with Retrieved Soil Moistures outside the Acceptable Range. + + + Percentage of EASE2 grid cells with soil moisture measures that fall outside of a predefined acceptable range. + + + directInternal + + + percent + + + 100. + + + + + Percentage of EASE2 grid cells that lack soil moisture retrieval values relative to the total number of grid cells where soil moisture retrieval was attempted. + + + Percent of Missing Data + + + percent + + + 176.5011139 + + + directInternal + + + + + + eng + + + utf8 + + + 1.0 + + + Product Specification Document for the SMAP Level 3 Passive Soil Moisture Product (L3_SM_P) + + + 2013-02-08 + + + L3_SM_P + + + + + doi:10.5067/OMHVSRGFX38O + + + SPL3SMP + + + SMAP + + + utf8 + + + National Aeronautics and Space Administration (NASA) + + + eng + + + 008 + + + Daily global composite of up-to 30 half-orbit L2_SM_P soil moisture estimates based on radiometer brightness temperature measurements acquired by the SMAP radiometer during ascending and descending half-orbits at approximately 6 PM and 6 AM local solar time. + + + onGoing + + + 2021-08-31 + + + The software that generates the Level 3 Soil Moisture Passive product and the data system that automates its production were designed and implemented + at the Jet Propulsion Laboratory, California Institute of Technology in Pasadena, California. + + + geoscientificInformation + + + The SMAP L3_SM_P algorithm provides daily global composite of soil moistures based on radiometer data on a 36 km grid. + + + SMAP L3 Radiometer Global Daily 36 km EASE-Grid Soil Moisture + + + National Snow and Ice Data Center + + + R18 + + + grid + + + The Calibration and Validation Version 2 Release of the SMAP Level 3 Daily Global Composite Passive Soil Moisture Science Processing Software. + + + + + SPL3SMP + + + utf8 + + + be9694f7-6503-4c42-8423-4c824df6c1f2 + + + eng + + + 1.8.13 + + + 008 + + + Daily global composite of up-to 15 half-orbit L2_SM_P soil moisture estimates based on radiometer brightness temperature measurements acquired by the SMAP radiometer during descending half-orbits at approximately 6 AM local solar time. + + + 2022-03-11 + + + onGoing + + + The software that generates the Level 3 SM_P product and the data system that automates its production were designed and implemented at the Jet Propulsion Laboratory, California Institute of Technology in Pasadena, California. + + + geoscientificInformation + + + The SMAP L3_SM_P effort provides soil moistures based on radiometer data on a 36 km grid. + + + HDF5 + + + SMAP_L3_SM_P_20150331_R18290_002.h5 + + + asNeeded + + + R18290 + + + Jet Propulsion Laboratory + + + 2016-05-01 + + + L3_SM_P + + + grid + + + The Calibration and Validation Version 2 Release of the SMAP Level 3 Daily Global Composite Passive Soil Moisture Science Processing Software. + + + + + Soil moisture is retrieved over land targets on the descending (AM) SMAP half-orbits when the SMAP spacecraft is travelling from North to South, while the SMAP instruments are operating in the nominal mode. The L3_SM_P product represents soil moisture retrieved over the entre UTC day. Retrievals are performed but flagged as questionable over urban areas, mountainous areas with high elevation variability, and areas with high ( &gt; 5 kg/m**2) vegetation water content; for retrievals using the high-resolution radar, cells in the nadir region are also flagged. Retrievals are inhibited for permanent snow/ice, frozen ground, and excessive static or transient open water in the cell, and for excessive RFI in the sensor data. + + + 180. + + + -180. + + + -85.04450226 + + + 85.04450226 + + + 2015-03-31T00:00:00.000Z + + + 2015-03-31T23:59:59.999Z + + + + + SMAP_L3_SM_P_20150331_R18290_002.qa + + + An ASCII product that contains statistical information on data product results. These statistics enable data producers and users to assess the quality of the data in the data product granule. + + + 2022-03-11 + + + + + 0 + + + 0 + + + 0 + + + 2 + + + SMAP Fixed Earth Grids, SMAP Science Document no: 033, May 11, 2009 + + + point + + + + 406 + + + 36. + + + + + EASE-Grid 2.0 + + + EASE-Grid 2.0: Incremental but Significant Improvements for Earth-Gridded Data Sets (ISPRS Int. J. Geo-Inf. 2012, 1, 32-45; doi:10.3390/ijgi1010032) + + + 2012-03-31 + + + + + 964 + + + 36. + + + + + The Equal-Area Scalable Earth Grid (EASE-Grid 2.0) is used for gridding satellite data sets. The EASE-Grid 2.0 is defined on the WGS84 ellipsoid to allow users to import data into standard GIS software formats such as GeoTIFF without reprojection. + + + Equal-Area Scalable Earth Grid + + + + + + 869 + + + 864 + + + + + + A configuration file that specifies the complete set of elements within the input Level 2 SM_P Product that the Radiometer Level 3_SM_P Science Processing Software (SPS) needs in order to function. + + + R18290 + + + SMAP_L3_SM_P_SPS_InputConfig_L2_SM_P.xml + + + 2022-03-11 + + + + + Precomputed longitude at each EASEGrid cell on 36-km grid on Global Cylindrical Projection + + + 002 + + + EZ2Lon_M36_002.float32 + + + 2013-05-09 + + + + + Passive soil moisture estimates onto a 36-km global Earth-fixed grid, based on radiometer measurements acquired when the SMAP spacecraft is travelling from North to South at approximately 6:00 AM local time. + + + SMAP_L2_SM_P_00864_D_20150331T162851_R18290_001.h5 + SMAP_L2_SM_P_00865_A_20150331T162945_R18290_001.h5 + SMAP_L2_SM_P_00865_D_20150331T171859_R18290_001.h5 + SMAP_L2_SM_P_00866_A_20150331T180814_R18290_001.h5 + SMAP_L2_SM_P_00866_D_20150331T185725_R18290_001.h5 + SMAP_L2_SM_P_00867_A_20150331T194640_R18290_001.h5 + SMAP_L2_SM_P_00867_D_20150331T203555_R18290_001.h5 + SMAP_L2_SM_P_00868_A_20150331T212510_R18290_001.h5 + SMAP_L2_SM_P_00868_D_20150331T221420_R18290_001.h5 + SMAP_L2_SM_P_00869_A_20150331T230335_R18290_001.h5 + SMAP_L2_SM_P_00869_D_20150331T235250_R18290_001.h5 + + + doi:10.5067/LPJ8F0TAK6E0 + + + L2_SM_P + + + 2022-02-22 + 2022-02-22 + 2022-02-22 + 2022-02-22 + 2022-02-22 + 2022-02-22 + 2022-02-22 + 2022-02-22 + 2022-02-22 + 2022-02-22 + 2022-02-22 + + + 36. + + + + + A configuration file that specifies the source of the values for each of the data elements that comprise the metadata in the output Radiometer Level 3_SM_P product. + + + R18290 + + + SMAP_L3_SM_P_SPS_MetConfig_L2_SM_P.xml + + + 2022-03-11 + + + + + A configuration file that lists the entire content of the output Radiometer Level 3_SM_P_ product. + + + R18290 + + + SMAP_L3_SM_P_SPS_OutputConfig_L3_SM_P.xml + + + 2022-03-11 + + + + + A configuration file generated automatically within the SMAP data system that specifies all of the conditions required for each individual run of the Radiometer Level 3 SM P Science Processing Software (SPS). + + + R18290 + + + SMAP_L3_SM_P_SPS_RunConfig_20220311T185327319.xml + + + 2022-03-11 + + + + + + Soil moisture retrieved using default retrieval algorithm from brightness temperatures acquired by the SMAP radiometer during the spacecraft descending pass. Level 2 granule data are then mosaicked on a daily basis to form the Level 3 product. + + + 2022-03-11T18:53:27.319Z + + + Algorithm Theoretical Basis Document: SMAP L2 and L3 Radiometer Soil Moisture (Passive) Data Products: L2_SM_P &amp; L3_SM_P + + + 2000-01-01T11:58:55.816Z + + + 023 + + + L3_SM_P_SPS + + + 2021-08-17 + + + 015 + + + L3_SM_P_SPS + + + Version 1.1 + + + 2019-04-15 + + + 2015-10-30 + + + 026 + + + Level 3 soil moisture product is formed by mosaicking Level 2 soil moisture granule data acquired over one day. + + + Algorithm Theoretical Basis Document: SMAP L2 and L3 Radiometer Soil Moisture (Passive) Data Products: L2_SM_P &amp; L3_SM_P + + + 2451545. + + + J2000 + + + 2012-10-26 + + + Soil Moisture Active Passive Mission (SMAP) Science Data System (SDS) Operations Facility + + + Soil Moisture Active Passive (SMAP) Radiometer processing algorithm + + + Preliminary + + + + + + + + + Longitude of the center of the Earth based grid cell. + + + degrees_east + + + + + + + Latitude of the center of the Earth based grid cell. + + + degrees_north + + + + + + + 0. + + + The fraction of the area of the 36 km grid cell that is covered by static water based on a Digital Elevation Map. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 1. + + + -9999. + + + + + + + + + Representative SCA-V soil moisture measurement for the Earth based grid cell. + + + cm**3/cm**3 + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0.01999999955 + + + 0.5 + + + -9999. + + + + + + + + + Representative angle between the antenna boresight vector and the normal to the Earth&apos;s surface for all footprints within the cell. + + + degrees + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 90. + + + -9999. + + + + + + + + + Arithmetic average of the acquisition time of all of the brightness temperature footprints with a center that falls within the EASE grid cell in UTC. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + + + + + + + 65534 + + + 1s, 2s, 4s, 8s + + + Bit flags that record the conditions and the quality of the DCA retrieval algorithms that generate soil moisture for the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + Retrieval_recommended Retrieval_attempted Retrieval_success FT_retrieval_success + + + + + + + + + The measured opacity of the vegetation used in the DCA retrieval in the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + -999999.875 + + + 999999.875 + + + -9999. + + + + + + + + + Bit flags that represent the quality of the horizontal polarization brightness temperature within each grid cell + + + 65534 + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s, 512s, 1024s, 2048s, 4096s, 8192s, 16384s, 32768s + + + Horizontal_polarization_quality Horizontal_polarization_range Horizontal_polarization_RFI_detection Horizontal_polarization_RFI_correction Horizontal_polarization_NEDT Horizontal_polarization_direct_sun_correction Horizontal_polarization_reflected_sun_correction Horizontal_polarization_reflected_moon_correction Horizontal_polarization_direct_galaxy_correction Horizontal_polarization_reflected_galaxy_correction Horizontal_polarization_atmosphere_correction Horizontal_polarization_Faraday_rotation_correction Horizontal_polarization_null_value_bit Horizontal_polarization_water_correction Horizontal_polarization_RFI_check Horizontal_polarization_RFI_clean + + + + + + + + + A unitless value that is indicative of bare soil roughness used in DCA within the 36 km grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + + + + + + + + 254 + + + An enumerated type that specifies the most common landcover class in the grid cell based on the IGBP landcover map. The array order is longitude (ascending), followed by latitude (descending), and followed by IGBP land cover type descending dominance (only the first three types are listed) + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + + + + + + + The row index of the 36 km EASE grid cell that contains the associated data. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0 + + + 405 + + + 65534 + + + + + + + + + Horizontal polarization brightness temperature in 36 km Earth grid cell before adjustment for the presence of water bodies. + + + Kelvin + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 330. + + + -9999. + + + + + + + + + Vertical polarization brightness temperature in 36 km Earth grid cell adjusted for the presence of water bodies. + + + Kelvin + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 330. + + + -9999. + + + + + + + + + Fourth stokes parameter for each 36 km grid cell calculated with an adjustment for the presence of water bodies + + + Kelvin + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 330. + + + -9999. + + + + + + + + + Weighted average of the longitude of the center of the brightness temperature footprints that fall within the EASE grid cell. + + + degrees + + + -180. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 179.9989929 + + + -9999. + + + + + + + + + The measured opacity of the vegetation used in the SCA-H retrieval in the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + -999999.875 + + + 999999.875 + + + -9999. + + + + + + + + + 65534 + + + 1s, 2s, 4s, 8s + + + Bit flags that record the conditions and the quality of the SCA-H retrieval algorithms that generate soil moisture for the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + Retrieval_recommended Retrieval_attempted Retrieval_success FT_retrieval_success + + + + + + + + + A unitless value that is indicative of bare soil roughness used in DCA within the 36 km grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + /Soil_Moisture_Retrieval_Data_AM/roughness_coefficient + + + + + + + + + Diffuse reflecting power of the Earth&apos;s surface used in SCA-H within the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + + + + + + + + 0. + + + The fraction of the grid cell that contains the most common land cover in that area based on the IGBP landcover map. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 1. + + + -9999. + + + + + + + + + 0. + + + Diffuse reflecting power of the Earth&apos;s surface used in SCA-V within the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 1. + + + -9999. + + + + + + + + + 65534 + + + 1s, 2s, 4s, 8s + + + Bit flags that record the conditions and the quality of the DCA retrieval algorithms that generate soil moisture for the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + Retrieval_recommended Retrieval_attempted Retrieval_success FT_retrieval_success + + + /Soil_Moisture_Retrieval_Data_AM/retrieval_qual_flag_dca + + + + + + + + + Representative measure of water in the vegetation within the 36 km grid cell. + + + kg/m**2 + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 20. + + + -9999. + + + + + + + + + Vertical polarization brightness temperature in 36 km Earth grid cell before adjustment for the presence of water bodies. + + + Kelvin + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 330. + + + -9999. + + + + + + + + + A unitless value that is indicative of bare soil roughness used in SCA-V within the 36 km grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + + + + + + + Representative SCA-H soil moisture measurement for the Earth based grid cell. + + + cm**3/cm**3 + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0.01999999955 + + + 0.5 + + + -9999. + + + + + + + + + Gain weighted fraction of static water within the radiometer horizontal polarization brightness temperature antenna pattern in 36 km Earth grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + + + + + + + A unitless value that is indicative of bare soil roughness used in SCA-H within the 36 km grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + + + + + + + Representative DCA soil moisture measurement for the Earth based grid cell. + + + cm**3/cm**3 + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0.01999999955 + + + 0.5 + + + -9999. + + + + + + + + + Third stokes parameter for each 36 km grid cell calculated with an adjustment for the presence of water bodies + + + Kelvin + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 330. + + + -9999. + + + + + + + + + 65534 + + + Bit flags that represent the quality of the 3rd Stokes brightness temperature within each grid cell + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s, 512s, 1024s, 4096s, 16384s, 32768s + + + 3rd_Stokes_quality 3rd_Stokes_range 3rd_Stokes_RFI_detection 3rd_Stokes_RFI_correction 3rd_Stokes_NEDT 3rd_Stokes_direct_sun_correction 3rd_Stokes_reflected_sun_correction 3rd_Stokes_reflected_moon_correction 3rd_Stokes_direct_galaxy_correction 3rd_Stokes_reflected_galaxy_correction 3rd_Stokes_atmosphere_correction 3rd_Stokes_null_value_bit 3rd_Stokes_RFI_check 3rd_Stokes_RFI_clean + + + + + + + + + 65534 + + + Bit flags that represent the quality of the 4th Stokes brightness temperature within each grid cell + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s, 512s, 1024s, 4096s, 16384s, 32768s + + + 4th_Stokes_quality 4th_Stokes_range 4th_Stokes_RFI_detection 4th_Stokes_RFI_correction 4th_Stokes_NEDT 4th_Stokes_direct_sun_correction 4th_Stokes_reflected_sun_correction 4th_Stokes_reflected_moon_correction 4th_Stokes_direct_galaxy_correction 4th_Stokes_reflected_galaxy_correction 4th_Stokes_atmosphere_correction 4th_Stokes_null_value_bit 4th_Stokes_RFI_check 4th_Stokes_RFI_clean + + + + + + + + + Horizontal polarization brightness temperature in 36 km Earth grid cell adjusted for the presence of water bodies. + + + Kelvin + + + 0. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 330. + + + -9999. + + + + + + + + + Gain weighted fraction of static water within the radiometer vertical polarization brightness temperature antenna pattern in 36 km Earth grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + + + + + + + Diffuse reflecting power of the Earth&apos;s surface used in DCA within the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + + + + + + + Indicates if the grid point lies on land (0) or water (1). + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0 + + + 1 + + + 65534 + + + + + + + + + Arithmetic average of the acquisition time of all of the brightness temperature footprints with a center that falls within the EASE grid cell in seconds since noon on January 1, 2000 UTC. + + + seconds + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + -999999.90000000002 + + + 940000000. + + + -9999. + + + + + + + + + The measured opacity of the vegetation used in the DCA retrieval in the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + -999999.875 + + + 999999.875 + + + -9999. + + + /Soil_Moisture_Retrieval_Data_AM/vegetation_opacity + + + + + + + + + A unitless value that is indicative of aggregated bulk_density within the 36 km grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 2.650000095 + + + -9999. + + + + + + + + + Net uncertainty measure of soil moisture measure for the Earth based grid cell. - Calculation method is TBD. + + + cm**3/cm**3 + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 0.200000003 + + + -9999. + + + + + + + + + The fraction of the area of the 36 km grid cell that is covered by water based on the radar detection algorithm. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + + + + + + + 65534 + + + Bit flags that represent the quality of the vertical polarization brightness temperature within each grid cell + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s, 512s, 1024s, 2048s, 4096s, 8192s, 16384s, 32768s + + + Vertical_polarization_quality Vertical_polarization_range Vertical_polarization_RFI_detection Vertical_polarization_RFI_correction Vertical_polarization_NEDT Vertical_polarization_direct_sun_correction Vertical_polarization_reflected_sun_correction Vertical_polarization_reflected_moon_correction Vertical_polarization_direct_galaxy_correction Vertical_polarization_reflected_galaxy_correction Vertical_polarization_atmosphere_correction Vertical_polarization_Faraday_rotation_correction Vertical_polarization_null_value_bit Vertical_polarization_water_correction Vertical_polarization_RFI_check Vertical_polarization_RFI_clean + + + + + + + + + Weighted average of the latitude of the center of the brightness temperature footprints that fall within the EASE grid cell. + + + degrees + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + -90. + + + 90. + + + -9999. + + + + + + + + + The column index of the 36 km EASE grid cell that contains the associated data. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0 + + + 963 + + + 65534 + + + + + + + + + Temperature at land surface based on GMAO GEOS-5 data. + + + Kelvins + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 350. + + + -9999. + + + + + + + + + Representative DCA soil moisture measurement for the Earth based grid cell. + + + cm**3/cm**3 + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0.01999999955 + + + 0.5 + + + -9999. + + + /Soil_Moisture_Retrieval_Data_AM/soil_moisture + + + + + + + + + 65534 + + + 1s, 2s, 4s, 8s + + + Bit flags that record the conditions and the quality of the SCA-V retrieval algorithms that generate soil moisture for the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + Retrieval_recommended Retrieval_attempted Retrieval_success FT_retrieval_success + + + + + + + + + Fraction of the 36 km grid cell that is denoted as frozen. Based on binary flag that specifies freeze thaw conditions in each of the component 3 km grid cells. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + + + + + + + Bit flags that record ambient surface conditions for the grid cell + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 65534 + + + 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s, 512s, 1024s, 2048s + + + 36_km_static_water_body 36_km_radar_water_body_detection 36_km_coastal_proximity 36_km_urban_area 36_km_precipitation 36_km_snow_or_ice 36_km_permanent_snow_or_ice 36_km_radiometer_frozen_ground 36_km_model_frozen_ground 36_km_mountainous_terrain 36_km_dense_vegetation 36_km_nadir_region + + + + + + + + + The measured opacity of the vegetation used in the SCA-V retrieval in the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + -999999.875 + + + 999999.875 + + + -9999. + + + + + + + + + Diffuse reflecting power of the Earth&apos;s surface used in DCA within the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + /Soil_Moisture_Retrieval_Data_AM/albedo + + + + + + + + + A unitless value that is indicative of aggregated clay fraction within the 36 km grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + + + + + + + + + A unitless value that is indicative of aggregated bulk density within the 36 km grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 2.650000095 + + + -9999. + + + + + + + Representative angle between the antenna boresight vector and the normal to the Earth&apos;s surface for all footprints within the cell. + + + degrees + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 90. + + + -9999. + + + + + + + The row index of the 36 km EASE grid cell that contains the associated data. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0 + + + 405 + + + 65534 + + + + + + + The fraction of the area of the 36 km grid cell that is covered by static water based on a Digital Elevation Map. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + + + + + Fraction of the 36 km grid cell that is denoted as frozen. Based on binary flag that specifies freeze thaw conditions in each of the component 3 km grid cells. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + + + + + A unitless value that is indicative of bare soil roughness used in DCA retrievals within the 36 km grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + + + + + A unitless value that is indicative of bare soil roughness used in DCA retrievals within the 36 km grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + /Soil_Moisture_Retrieval_Data_PM/roughness_coefficient_dca_pm + + + + + + + Bit flags that record ambient surface conditions for the grid cell + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 65534 + + + 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s, 512s, 1024s, 2048s + + + 36_km_static_water_body 36_km_radar_water_body_detection 36_km_coastal_proximity 36_km_urban_area 36_km_precipitation 36_km_snow_or_ice 36_km_permanent_snow_or_ice 36_km_radar_frozen_ground 36_km_model_frozen_ground 36_km_mountainous_terrain 36_km_dense_vegetation 36_km_nadir_region + + + + + + + 65534 + + + 1s, 2s, 4s, 8s + + + Bit flags that record the conditions and the quality of the DCA retrieval algorithms that generate soil moisture for the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + Retrieval_recommended Retrieval_attempted Retrieval_success FT_retrieval_success + + + + + + + Horizontal polarization brightness temperature in 36 km Earth grid cell adjusted for the presence of water bodies. + + + Kelvin + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 330. + + + -9999. + + + + + + + 65534 + + + 1s, 2s, 4s, 8s + + + Bit flags that record the conditions and the quality of the SCA-V retrieval algorithms that generate soil moisture for the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + Retrieval_recommended Retrieval_attempted Retrieval_success FT_retrieval_success + + + + + + + Representative DCA soil moisture measurement for the Earth based grid cell. + + + cm**3/cm**3 + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0.01999999955 + + + 0.5 + + + -9999. + + + + + + + Diffuse reflecting power of the Earth&apos;s surface used in SCA-V retrievals within the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + + + + + 0. + + + Gain weighted fraction of static water within the radiometer vertical polarization brightness temperature antenna pattern in 36 km Earth grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 1. + + + -9999. + + + + + + + A unitless value that is indicative of bare soil roughness used in SCA-V retrievals within the 36 km grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + + + + + 65534 + + + Bit flags that represent the quality of the 4th Stokes brightness temperature within each grid cell + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s, 512s, 1024s, 4096s, 16384s, 32768s + + + 4th_Stokes_quality 4th_Stokes_range 4th_Stokes_RFI_detection 4th_Stokes_RFI_correction 4th_Stokes_NEDT 4th_Stokes_direct_sun_correction 4th_Stokes_reflected_sun_correction 4th_Stokes_reflected_moon_correction 4th_Stokes_direct_galaxy_correction 4th_Stokes_reflected_galaxy_correction 4th_Stokes_atmosphere_correction 4th_Stokes_null_value_bit 4th_Stokes_RFI_check 4th_Stokes_RFI_clean + + + + + + + Third stokes parameter for each 36 km grid cell calculated with an adjustment for the presence of water bodies + + + Kelvin + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 330. + + + -9999. + + + + + + + 65534 + + + 1s, 2s, 4s, 8s + + + Bit flags that record the conditions and the quality of the DCA retrieval algorithms that generate soil moisture for the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + Retrieval_recommended Retrieval_attempted Retrieval_success FT_retrieval_success + + + /Soil_Moisture_Retrieval_Data_PM/retrieval_qual_flag_pm + + + + + + + Representative SCA-V soil moisture measurement for the Earth based grid cell. + + + cm**3/cm**3 + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0.01999999955 + + + 0.5 + + + -9999. + + + + + + + 65534 + + + 1s, 2s, 4s, 8s + + + Bit flags that record the conditions and the quality of the SCA-H retrieval algorithms that generate soil moisture for the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + Retrieval_recommended Retrieval_attempted Retrieval_success FT_retrieval_success + + + + + + + The measured opacity of the vegetation used in SCA-H retrievals in the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + -999999.875 + + + 999999.875 + + + -9999. + + + + + + + Kelvin + + + 0. + + + Vertical polarization brightness temperature in 36 km Earth grid cell before adjustment for the presence of water bodies. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 330. + + + -9999. + + + + + + + A unitless value that is indicative of bare soil roughness used in SCA-H retrievals within the 36 km grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + + + + + Bit flags that represent the quality of the horizontal polarization brightness temperature within each grid cell + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 65534 + + + 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s, 512s, 1024s, 2048s, 4096s, 8192s, 16384s, 32768s + + + Horizontal_polarization_quality Horizontal_polarization_range Horizontal_polarization_RFI_detection Horizontal_polarization_RFI_correction Horizontal_polarization_NEDT Horizontal_polarization_direct_sun_correction Horizontal_polarization_reflected_sun_correction Horizontal_polarization_reflected_moon_correction Horizontal_polarization_direct_galaxy_correction Horizontal_polarization_reflected_galaxy_correction Horizontal_polarization_atmosphere_correction Horizontal_polarization_Faraday_rotation_correction Horizontal_polarization_null_value_bit Horizontal_polarization_water_correction Horizontal_polarization_RFI_check Horizontal_polarization_RFI_clean + + + + + + + A unitless value that is indicative of aggregated clay fraction within the 36 km grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + + + + + Weighted average of the latitude of the center of the brightness temperature footprints that fall within the EASE grid cell. + + + degrees + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + -90. + + + 90. + + + -9999. + + + + + + + The column index of the 36 km EASE grid cell that contains the associated data. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0 + + + 963 + + + 65534 + + + + + + + Arithmetic average of the acquisition time of all of the brightness temperature footprints with a center that falls within the EASE grid cell in seconds since noon on January 1, 2000 UTC. + + + seconds + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + -999999.90000000002 + + + 940000000. + + + -9999. + + + + + + + Fourth stokes parameter for each 36 km grid cell calculated with an adjustment for the presence of water bodies + + + Kelvin + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 330. + + + -9999. + + + + + + + Net uncertainty measure of soil moisture measure for the Earth based grid cell. - Calculation method is TBD. + + + cm**3/cm**3 + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 0.200000003 + + + -9999. + + + + + + + + 254 + + + An enumerated type that specifies the most common landcover class in the grid cell based on the IGBP landcover map. The array order is longitude (ascending), followed by latitude (descending), and followed by IGBP land cover type descending dominance (only the first three types are listed) + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + + + + + Arithmetic average of the acquisition time of all of the brightness temperature footprints with a center that falls within the EASE grid cell in UTC. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + + + + + Diffuse reflecting power of the Earth&apos;s surface used in DCA retrievals within the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + + + + + Longitude of the center of the Earth based grid cell. + + + degrees_east + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + -180. + + + 179.9989929 + + + -9999. + + + + + + + 65534 + + + Bit flags that represent the quality of the 3rd Stokes brightness temperature within each grid cell + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s, 512s, 1024s, 4096s, 16384s, 32768s + + + 3rd_Stokes_quality 3rd_Stokes_range 3rd_Stokes_RFI_detection 3rd_Stokes_RFI_correction 3rd_Stokes_NEDT 3rd_Stokes_direct_sun_correction 3rd_Stokes_reflected_sun_correction 3rd_Stokes_reflected_moon_correction 3rd_Stokes_direct_galaxy_correction 3rd_Stokes_reflected_galaxy_correction 3rd_Stokes_atmosphere_correction 3rd_Stokes_null_value_bit 3rd_Stokes_RFI_check 3rd_Stokes_RFI_clean + + + + + + + The measured opacity of the vegetation used in DCA retrievals in the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + -999999.875 + + + 999999.875 + + + -9999. + + + + + + + Bit flags that represent the quality of the vertical polarization brightness temperature within each grid cell + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 65534 + + + 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s, 512s, 1024s, 2048s, 4096s, 8192s, 16384s, 32768s + + + Vertical_polarization_quality Vertical_polarization_range Vertical_polarization_RFI_detection Vertical_polarization_RFI_correction Vertical_polarization_NEDT Vertical_polarization_direct_sun_correction Vertical_polarization_reflected_sun_correction Vertical_polarization_reflected_moon_correction Vertical_polarization_direct_galaxy_correction Vertical_polarization_reflected_galaxy_correction Vertical_polarization_atmosphere_correction Vertical_polarization_Faraday_rotation_correction Vertical_polarization_null_value_bit Vertical_polarization_water_correction Vertical_polarization_RFI_check Vertical_polarization_RFI_clean + + + + + + + 0. + + + The fraction of the area of the 36 km grid cell that is covered by water based on the radar detection algorithm. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 1. + + + -9999. + + + + + + + Representative SCA-H soil moisture measurement for the Earth based grid cell. + + + cm**3/cm**3 + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0.01999999955 + + + 0.5 + + + -9999. + + + + + + + Representative DCA soil moisture measurement for the Earth based grid cell. + + + cm**3/cm**3 + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0.01999999955 + + + 0.5 + + + -9999. + + + /Soil_Moisture_Retrieval_Data_PM/soil_moisture_pm + + + + + + + + 0. + + + The fraction of the grid cell that contains the most common land cover in that area based on the IGBP landcover map. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 1. + + + -9999. + + + + + + + The measured opacity of the vegetation used in DCA retrievals in the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + -999999.875 + + + 999999.875 + + + -9999. + + + /Soil_Moisture_Retrieval_Data_PM/vegetation_opacity_dca_pm + + + + + + + 0. + + + Gain weighted fraction of static water within the radiometer horizontal polarization brightness temperature antenna pattern in 36 km Earth grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 1. + + + -9999. + + + + + + + Weighted average of the longitude of the center of the brightness temperature footprints that fall within the EASE grid cell. + + + degrees + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + -180. + + + 179.9989929 + + + -9999. + + + + + + + Diffuse reflecting power of the Earth&apos;s surface used in DCA retrievals within the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 1. + + + -9999. + + + /Soil_Moisture_Retrieval_Data_PM/albedo_dca_pm + + + + + + + Representative measure of water in the vegetation within the 36 km grid cell. + + + kg/m**2 + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 20. + + + -9999. + + + + + + + 0. + + + Diffuse reflecting power of the Earth&apos;s surface used in SCA-H retrievals within the grid cell. + + + 1. + + + -9999. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + + + + + Horizontal polarization brightness temperature in 36 km Earth grid cell before adjustment for the presence of water bodies. + + + Kelvin + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 330. + + + -9999. + + + + + + + Latitude of the center of the Earth based grid cell. + + + degrees_north + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + -90. + + + 90. + + + -9999. + + + + + + + Vertical polarization brightness temperature in 36 km Earth grid cell adjusted for the presence of water bodies. + + + Kelvin + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 330. + + + -9999. + + + + + + + The measured opacity of the vegetation used in SCA-V retrievals in the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + -999999.875 + + + 999999.875 + + + -9999. + + + + + + + Indicates if the grid point lies on land (0) or water (1). + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0 + + + 1 + + + 65534 + + + + + + + Temperature at land surface based on GMAO GEOS-5 data. + + + Kelvins + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 350. + + + -9999. + + + + diff --git a/tests/data/SC_SPL3SMP_008_fake.dmr b/tests/data/SC_SPL3SMP_008_fake.dmr new file mode 100644 index 0000000..bde8bbc --- /dev/null +++ b/tests/data/SC_SPL3SMP_008_fake.dmr @@ -0,0 +1,403 @@ + + + + 3.21.0-428 + + + 3.21.0-428 + + + libdap-3.21.0-103 + + + build_dmrpp -c /tmp/bes_conf_IAud -f /usr/share/hyrax/DATA/SMAP_L3_SM_P_20150331_R18290_002.h5 -r /tmp/dmr__0PmwFd -u OPeNDAP_DMRpp_DATA_ACCESS_URL -M + + + + + + + + Longitude of the center of the Earth based grid cell. + + + degrees_east + + + + + + + Latitude of the center of the Earth based grid cell. + + + degrees_north + + + + + + + north polar grid longitude + + + longitude + + + degrees_east + + + L3B ATM ATBD, Section 2.0, Section 5.0, Table 5. + + + -180. + + + 180. + + + referenceInformation + + + The north polar grid longitude + + + X + + + + + + + north polar grid latitude + + + latitude + + + degrees_north + + + L3B ATM ATBD, Section 2.0, Section 5.0, Table 5. + + + 60. + + + 90. + + + referenceInformation + + + The north polar grid latitude + + + + Y + + + + + + + + 254 + + + An enumerated type that specifies the most common landcover class + in the grid cell based on the IGBP landcover map. The array order is longitude (ascending), followed by latitude (descending), and followed by IGBP land cover type descending dominance (only the first three types are listed) + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + + + + + + + Weighted average of the longitude of the center of the brightness temperature footprints that fall within the EASE grid cell. + + + degrees + + + -180. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 179.9989929 + + + -9999. + + + + + + + + + The measured opacity of the vegetation used in the SCA-H retrieval in the grid cell. + + + -999999.875 + + + 999999.875 + + + -9999. + + + + + + + + 0. + + + The fraction of the grid cell that contains the most common land cover in that area based on the IGBP landcover map. + + + /Soil_Moisture_Retrieval_Data_AM/latitude + + + 1. + + + -9999. + + + + + + + + + + + Representative DCA soil moisture measurement for the Earth based grid cell. + + + cm**3/cm**3 + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0.01999999955 + + + 0.5 + + + -9999. + + + + + + + + + The column index of the 36 km EASE grid cell that contains the associated data. + + + /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0 + + + 963 + + + 65534 + + + + + + + + + Temperature at land surface based on GMAO GEOS-5 data. + + + Kelvins + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 350. + + + -9999. + + + + + + + + + Bit flags that record ambient surface conditions for the grid cell + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 65534 + + + 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s, 512s, 1024s, 2048s + + + 36_km_static_water_body 36_km_radar_water_body_detection 36_km_coastal_proximity 36_km_urban_area 36_km_precipitation 36_km_snow_or_ice 36_km_permanent_snow_or_ice 36_km_radiometer_frozen_ground 36_km_model_frozen_ground 36_km_mountainous_terrain 36_km_dense_vegetation 36_km_nadir_region + + + + + + + + + + + + + Bit flags that record ambient surface conditions for the grid cell + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 65534 + + + 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s, 512s, 1024s, 2048s + + + 36_km_static_water_body 36_km_radar_water_body_detection 36_km_coastal_proximity 36_km_urban_area 36_km_precipitation 36_km_snow_or_ice 36_km_permanent_snow_or_ice 36_km_radar_frozen_ground 36_km_model_frozen_ground 36_km_mountainous_terrain 36_km_dense_vegetation 36_km_nadir_region + + + + + + + Longitude of the center of the Earth based grid cell. + + + degrees_east + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + -180. + + + 179.9989929 + + + -9999. + + + + + + + The measured opacity of the vegetation used in DCA retrievals in the grid cell. + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + -999999.875 + + + 999999.875 + + + -9999. + + + fake_dim_1 + fake_dim_2 + + + + + + + Latitude of the center of the Earth based grid cell. + + + degrees_north + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + -90. + + + 90. + + + -9999. + + + + + + + Temperature at land surface based on GMAO GEOS-5 data. + + + Kelvins + + + /Soil_Moisture_Retrieval_Data_AM/latitude /Soil_Moisture_Retrieval_Data_AM/longitude + + + 0. + + + 350. + + + -9999. + + + + + + + Representative SCA-V soil moisture measurement for the Earth based grid cell. + + + cm**3/cm**3 + + + 0.01999999955 + + + 0.5 + + + -9999. + + + + diff --git a/tests/data/SC_SPL3SMP_008_prefetch.nc4 b/tests/data/SC_SPL3SMP_008_prefetch.nc4 new file mode 100644 index 0000000..f866858 Binary files /dev/null and b/tests/data/SC_SPL3SMP_008_prefetch.nc4 differ diff --git a/tests/unit/test_coordinate_utilities.py b/tests/unit/test_coordinate_utilities.py new file mode 100644 index 0000000..1c7aa04 --- /dev/null +++ b/tests/unit/test_coordinate_utilities.py @@ -0,0 +1,545 @@ +from logging import getLogger +from os.path import exists +from unittest import TestCase +from unittest.mock import ANY, patch + +import numpy as np +from harmony.util import config +from netCDF4 import Dataset +from numpy.testing import assert_array_equal +from varinfo import VarInfoFromDmr + +from hoss.coordinate_utilities import ( + any_absent_dimension_variables, + get_1D_dim_array_data_from_dimvalues, + get_coordinate_array, + get_coordinate_variables, + get_projected_dimension_names, + get_projected_dimension_names_from_coordinate_variables, + get_row_col_sizes_from_coordinate_datasets, + get_valid_indices, + get_variables_with_anonymous_dims, +) +from hoss.exceptions import ( + IncompatibleCoordinateVariables, + InvalidCoordinateDataset, + InvalidCoordinateVariable, + MissingCoordinateVariable, + MissingVariable, +) + + +class TestCoordinateUtilities(TestCase): + """A class for testing functions in the `hoss.coordinate_utilities` + module. + + """ + + @classmethod + def setUpClass(cls): + """Create fixtures that can be reused for all tests.""" + cls.config = config(validate=False) + cls.logger = getLogger('tests') + cls.varinfo = VarInfoFromDmr( + 'tests/data/SC_SPL3SMP_008.dmr', + 'SPL3SMP', + config_file='hoss/hoss_config.json', + ) + cls.test_varinfo = VarInfoFromDmr( + 'tests/data/SC_SPL3SMP_008_fake.dmr', + 'SPL3SMP', + config_file='hoss/hoss_config.json', + ) + cls.nc4file = 'tests/data/SC_SPL3SMP_008_prefetch.nc4' + cls.latitude = '/Soil_Moisture_Retrieval_Data_AM/latitude' + cls.longitude = '/Soil_Moisture_Retrieval_Data_AM/longitude' + + cls.lon_arr = np.array( + [ + [-179.3, -120.2, -60.6, -9999, -9999, -9999, 80.2, 120.6, 150.5, 178.4], + [-179.3, -120.2, -60.6, -999, 999, -9999, 80.2, 120.6, 150.5, 178.4], + [-179.3, -120.2, -60.6, -9999, -9999, -9999, 80.2, 120.6, 150.5, 178.4], + [-179.3, -120.2, -60.6, -9999, -9999, -9999, 80.2, 120.6, 150.5, 178.4], + [-179.3, -120.2, -60.6, -9999, -9999, -9999, 80.2, 120.6, 150.5, 178.4], + ] + ) + + cls.lat_arr = np.array( + [ + [89.3, 89.3, -9999, 89.3, 89.3, 89.3, -9999, 89.3, 89.3, 89.3], + [50.3, 50.3, 50.3, 50.3, 50.3, 50.3, -9999, 50.3, 50.3, 50.3], + [1.3, 1.3, 1.3, 1.3, 1.3, 1.3, -9999, -9999, 1.3, 1.3], + [-9999, -60.2, -60.2, -99, -9999, -9999, -60.2, -60.2, -60.2, -60.2], + [-88.1, -88.1, -88.1, 99, -9999, -9999, -88.1, -88.1, -88.1, -88.1], + ] + ) + + cls.lon_arr_reversed = np.array( + [ + [ + -179.3, + -179.3, + -179.3, + -179.3, + -9999, + -9999, + -179.3, + -179.3, + -179.3, + -179.3, + ], + [ + -120.2, + -120.2, + -120.2, + -9999, + -9999, + -120.2, + -120.2, + -120.2, + -120.2, + -120.2, + ], + [20.6, 20.6, 20.6, 20.6, 20.6, 20.6, 20.6, 20.6, -9999, -9999], + [150.5, 150.5, 150.5, 150.5, 150.5, 150.5, -9999, -9999, 150.5, 150.5], + [178.4, 178.4, 178.4, 178.4, 178.4, 178.4, 178.4, -9999, 178.4, 178.4], + ] + ) + + cls.lat_arr_reversed = np.array( + [ + [89.3, 79.3, -9999, 59.3, 29.3, 2.1, -9999, -59.3, -79.3, -89.3], + [89.3, 79.3, 60.3, 59.3, 29.3, 2.1, -9999, -59.3, -79.3, -89.3], + [89.3, -9999, 60.3, 59.3, 29.3, 2.1, -9999, -9999, -9999, -89.3], + [-9999, 79.3, -60.3, -9999, -9999, -9999, -60.2, -59.3, -79.3, -89.3], + [-89.3, 79.3, -60.3, -9999, -9999, -9999, -60.2, -59.3, -79.3, -9999], + ] + ) + + def setUp(self): + """Create fixtures that should be unique per test.""" + + def tearDown(self): + """Remove per-test fixtures.""" + + def test_get_coordinate_variables(self): + """Ensure that the correct coordinate variables are + retrieved for the reqquested science variable + + """ + requested_science_variables = [ + '/Soil_Moisture_Retrieval_Data_AM/surface_flag', + '/Soil_Moisture_Retrieval_Data_PM/surface_flag_pm', + ] + expected_coordinate_variables = ( + [ + '/Soil_Moisture_Retrieval_Data_AM/latitude', + '/Soil_Moisture_Retrieval_Data_PM/latitude_pm', + ], + [ + '/Soil_Moisture_Retrieval_Data_AM/longitude', + '/Soil_Moisture_Retrieval_Data_PM/longitude_pm', + ], + ) + + with self.subTest('Retrieves expected coordinates for the requested variables'): + actual_coordinate_variables = get_coordinate_variables( + self.varinfo, requested_science_variables + ) + # the order of the results maybe random + self.assertEqual( + len(expected_coordinate_variables), len(actual_coordinate_variables) + ) + self.assertCountEqual( + expected_coordinate_variables[0], actual_coordinate_variables[0] + ) + self.assertCountEqual( + expected_coordinate_variables[0], actual_coordinate_variables[0] + ) + for expected_variable in expected_coordinate_variables[0]: + self.assertIn(expected_variable, actual_coordinate_variables[0]) + + for expected_variable in expected_coordinate_variables[1]: + self.assertIn(expected_variable, actual_coordinate_variables[1]) + + def test_get_1D_dim_array_data_from_dimvalues(self): + """Ensure that the dimension scale generated from the + provided dimension values are accurate for ascending and + descending scales + """ + + with self.subTest('valid ascending dim scale'): + dim_values_asc = np.array([2, 4]) + dim_indices_asc = np.array([0, 1]) + dim_size_asc = 12 + expected_dim_asc = np.array([2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24]) + dim_array_values = get_1D_dim_array_data_from_dimvalues( + dim_values_asc, dim_indices_asc, dim_size_asc + ) + self.assertTrue(np.array_equal(dim_array_values, expected_dim_asc)) + + with self.subTest('valid descending dim scale'): + dim_values_desc = np.array([100, 70]) + dim_indices_desc = np.array([2, 5]) + dim_size_desc = 10 + expected_dim_desc = np.array([120, 110, 100, 90, 80, 70, 60, 50, 40, 30]) + + dim_array_values = get_1D_dim_array_data_from_dimvalues( + dim_values_desc, dim_indices_desc, dim_size_desc + ) + self.assertTrue(np.array_equal(dim_array_values, expected_dim_desc)) + + with self.subTest('invalid dimension values'): + dim_values_invalid = np.array([2, 2]) + dim_indices_asc = np.array([0, 1]) + dim_size_asc = 12 + with self.assertRaises(InvalidCoordinateDataset) as context: + get_1D_dim_array_data_from_dimvalues( + dim_values_invalid, dim_indices_asc, dim_size_asc + ) + self.assertEqual( + context.exception.message, + 'Cannot compute the dimension resolution for ' + 'dim_value: "2" dim_index: "0"', + ) + + with self.subTest('invalid dimension indices'): + dim_values_desc = np.array([100, 70]) + dim_indices_invalid = np.array([5, 5]) + dim_size_desc = 10 + with self.assertRaises(InvalidCoordinateDataset) as context: + get_1D_dim_array_data_from_dimvalues( + dim_values_desc, dim_indices_invalid, dim_size_desc + ) + self.assertEqual( + context.exception.message, + 'Cannot compute the dimension resolution for ' + 'dim_value: "100" dim_index: "5"', + ) + + def test_get_coordinate_array(self): + """Ensures that the expected lat/lon arrays are retrieved + for the coordinate variables + """ + expected_shape = (406, 964) + with self.subTest('Expected latitude array'): + with Dataset(self.nc4file, 'r') as prefetch_dataset: + lat_prefetch_arr = get_coordinate_array( + prefetch_dataset, + self.latitude, + ) + self.assertTupleEqual(lat_prefetch_arr.shape, expected_shape) + np.testing.assert_array_equal( + lat_prefetch_arr, prefetch_dataset[self.latitude][:] + ) + with self.subTest('Expected longitude array'): + with Dataset(self.nc4file, 'r') as prefetch_dataset: + lon_prefetch_arr = get_coordinate_array( + prefetch_dataset, self.longitude + ) + self.assertTupleEqual(lon_prefetch_arr.shape, expected_shape) + np.testing.assert_array_equal( + lon_prefetch_arr, prefetch_dataset[self.longitude][:] + ) + with self.subTest('Missing coordinate'): + with Dataset(self.nc4file, 'r') as prefetch_dataset: + with self.assertRaises(MissingCoordinateVariable) as context: + coord_arr = ( + get_coordinate_array( + prefetch_dataset, + '/Soil_Moisture_Retrieval_Data_AM/longitude_centroid', + ), + ) + self.assertEqual( + context.exception.message, + 'Coordinate: "/Soil_Moisture_Retrieval_Data_AM/latitude_centroid" is ' + 'not present in coordinate prefetch file.', + ) + + def test_get_projected_dimension_names(self): + """Ensure that the expected projected dimension name + is returned for the coordinate variables + """ + + expected_projected_names = [ + '/Soil_Moisture_Retrieval_Data_AM/projected_y', + '/Soil_Moisture_Retrieval_Data_AM/projected_x', + ] + + with self.subTest( + 'Retrieves expected projected dimension names for a science variable' + ): + self.assertListEqual( + get_projected_dimension_names(self.varinfo, self.latitude), + expected_projected_names, + ) + + with self.subTest( + 'Retrieves expected dimension names for the longitude variable' + ): + self.assertEqual( + get_projected_dimension_names(self.varinfo, self.longitude), + expected_projected_names, + ) + + with self.subTest('Raises exception for missing coordinate variable'): + with self.assertRaises(MissingVariable) as context: + get_projected_dimension_names( + self.varinfo, '/Soil_Moisture_Retrieval_Data_AM/random_variable' + ) + self.assertEqual( + context.exception.message, + '"/Soil_Moisture_Retrieval_Data_AM/random_variable" is ' + 'not present in source granule file.', + ) + + def test_get_projected_dimension_names_from_coordinate_variables(self): + """Ensure that the expected projected dimension name + is returned for the coordinate variables + """ + + expected_override_dimensions_AM = [ + '/Soil_Moisture_Retrieval_Data_AM/projected_y', + '/Soil_Moisture_Retrieval_Data_AM/projected_x', + ] + expected_override_dimensions_PM = [ + '/Soil_Moisture_Retrieval_Data_PM/projected_y', + '/Soil_Moisture_Retrieval_Data_PM/projected_x', + ] + + with self.subTest( + 'Retrieves expected override dimensions for the science variable' + ): + self.assertListEqual( + get_projected_dimension_names_from_coordinate_variables( + self.varinfo, '/Soil_Moisture_Retrieval_Data_AM/surface_flag' + ), + expected_override_dimensions_AM, + ) + + with self.subTest( + 'Retrieves expected override dimensions for the longitude variable' + ): + self.assertListEqual( + get_projected_dimension_names_from_coordinate_variables( + self.varinfo, self.longitude + ), + expected_override_dimensions_AM, + ) + + with self.subTest( + 'Retrieves expected override dimensions for the latitude variable' + ): + self.assertListEqual( + get_projected_dimension_names_from_coordinate_variables( + self.varinfo, self.latitude + ), + expected_override_dimensions_AM, + ) + + with self.subTest( + 'Retrieves expected override dimensions science variable with a different grid' + ): + self.assertListEqual( + get_projected_dimension_names_from_coordinate_variables( + self.varinfo, '/Soil_Moisture_Retrieval_Data_PM/surface_flag_pm' + ), + expected_override_dimensions_PM, + ) + with self.subTest( + 'Retrieves empty dimensions list when science variable has no coordinates' + ): + self.assertListEqual( + get_projected_dimension_names_from_coordinate_variables( + self.varinfo, '/Soil_Moisture_Retrieval_Data_PM/surface_flag_pm' + ), + expected_override_dimensions_PM, + ) + + def test_get_row_col_sizes_from_coordinate_datasets(self): + """Ensure that the correct row and column sizes are + returned for the requested coordinates + """ + + with self.subTest('Retrieves the expected row col sizes from the coordinates'): + expected_row_col_sizes = (5, 10) + self.assertEqual( + get_row_col_sizes_from_coordinate_datasets(self.lat_arr, self.lon_arr), + expected_row_col_sizes, + ) + with self.subTest('Retrieves the expected row col sizes for the 1d array'): + self.assertEqual( + get_row_col_sizes_from_coordinate_datasets( + np.array([1, 2, 3, 4]), np.array([5, 6, 7, 8, 9]) + ), + (4, 5), + ) + with self.subTest( + 'Raises an exception when the lat and lon array shapes do not match' + ): + lat_mismatched_array = np.array([[1, 2, 3], [3, 4, 5]]) + lon_mismatched_array = np.array([[6, 7], [8, 9], [10, 11]]) + with self.assertRaises(IncompatibleCoordinateVariables) as context: + get_row_col_sizes_from_coordinate_datasets( + lat_mismatched_array, lon_mismatched_array + ) + self.assertEqual( + context.exception.message, + f'Longitude coordinate shape: "{lon_mismatched_array.shape}"' + f'does not match the latitude coordinate shape: "{lat_mismatched_array.shape}"', + ) + with self.subTest( + 'Raises an exception when Both arrays are 1-D, but latitude has a zero size' + ): + lat_empty_size_array = np.array([]) + with self.assertRaises(IncompatibleCoordinateVariables) as context: + get_row_col_sizes_from_coordinate_datasets( + lat_empty_size_array, np.array([5, 6, 7, 8]) + ) + + with self.subTest( + 'Raises an exception when Both arrays are 1-D, but longitude has a zero size' + ): + lon_empty_size_array = np.array([]) + with self.assertRaises(IncompatibleCoordinateVariables) as context: + get_row_col_sizes_from_coordinate_datasets( + np.array([6, 7, 8, 9]), lon_empty_size_array + ) + + with self.subTest( + 'Raises an exception when latitude array that is zero dimensional' + ): + lat_empty_ndim_array = np.array( + 0, + ) + with self.assertRaises(IncompatibleCoordinateVariables) as context: + get_row_col_sizes_from_coordinate_datasets( + lat_empty_ndim_array, np.array([1, 2, 3, 4]) + ) + + with self.subTest( + 'Raises an exception when longitude array that is zero dimensional' + ): + lon_empty_ndim_array = np.array( + 0, + ) + with self.assertRaises(IncompatibleCoordinateVariables) as context: + get_row_col_sizes_from_coordinate_datasets( + np.array([1, 2, 3, 4]), lon_empty_ndim_array + ) + + def test_get_valid_indices(self): + """Ensure that latitude and longitude values are correctly identified as + ascending or descending. + + """ + expected_valid_indices_lat_arr_with_fill = np.array([1, 2, 3, 4]) + expected_valid_indices_lon_arr_with_fill = np.array([0, 1, 2, 6, 7, 8, 9]) + + expected_valid_indices_lat_arr_over_range = np.array([0, 1, 2]) + expected_valid_indices_lon_arr_over_range = np.array([0, 1, 2, 6, 7, 8, 9]) + + fill_array = np.array([-9999.0, -9999.0, -9999.0, -9999.0]) + + with self.subTest('valid indices for latitude with fill values'): + valid_indices_lat_arr = get_valid_indices( + self.lat_arr[:, 2], self.varinfo.get_variable(self.latitude) + ) + np.testing.assert_array_equal( + valid_indices_lat_arr, expected_valid_indices_lat_arr_with_fill + ) + with self.subTest('valid indices for longitude with fill values'): + valid_indices_lon_arr = get_valid_indices( + self.lon_arr[0, :], self.varinfo.get_variable(self.longitude) + ) + np.testing.assert_array_equal( + valid_indices_lon_arr, expected_valid_indices_lon_arr_with_fill + ) + with self.subTest('latitude values beyond valid range'): + valid_indices_lat_arr = get_valid_indices( + self.lat_arr[:, 3], self.varinfo.get_variable(self.latitude) + ) + np.testing.assert_array_equal( + valid_indices_lat_arr, expected_valid_indices_lat_arr_over_range + ) + with self.subTest('longitude values beyond valid range'): + valid_indices_lon_arr = get_valid_indices( + self.lon_arr[1, :], self.varinfo.get_variable(self.longitude) + ) + np.testing.assert_array_equal( + valid_indices_lon_arr, expected_valid_indices_lon_arr_over_range + ) + with self.subTest('all fill values - no valid indices'): + valid_indices = get_valid_indices( + fill_array, self.varinfo.get_variable(self.longitude) + ) + self.assertEqual(valid_indices.size, 0) + + def test_get_variables_with_anonymous_dims(self): + """Ensure that variables with no dimensions are + retrieved for the requested science variable + + """ + + with self.subTest('Retrieves variables with no dimensions'): + requested_science_variables = { + '/Soil_Moisture_Retrieval_Data_AM/surface_flag', + '/Soil_Moisture_Retrieval_Data_PM/surface_flag_pm', + } + variables_with_anonymous_dims = get_variables_with_anonymous_dims( + self.test_varinfo, requested_science_variables + ) + self.assertSetEqual( + variables_with_anonymous_dims, + requested_science_variables, + ) + with self.subTest('Does not retrieve variables with dimensions'): + variables_with_anonymous_dims = get_variables_with_anonymous_dims( + self.test_varinfo, {'/Soil_Moisture_Retrieval_Data_AM/variable_has_dim'} + ) + self.assertTrue(len(variables_with_anonymous_dims) == 0) + + with self.subTest( + 'Only retrieves variables with anonymous dimensions,' + 'when the request has both' + ): + requested_science_variables_with_dimensions = { + '/Soil_Moisture_Retrieval_Data_AM/variable_has_dim', + '/Soil_Moisture_Retrieval_Data_AM/variable_has_anonymous_dim', + } + variables_with_anonymous_dims = get_variables_with_anonymous_dims( + self.test_varinfo, requested_science_variables_with_dimensions + ) + self.assertSetEqual( + variables_with_anonymous_dims, + {'/Soil_Moisture_Retrieval_Data_AM/variable_has_anonymous_dim'}, + ) + with self.subTest( + 'retrieves variables with fake dimensions,' 'when the request has both' + ): + variables_with_fake_dims = get_variables_with_anonymous_dims( + self.test_varinfo, + {'/Soil_Moisture_Retrieval_Data_PM/variable_with_fake_dims'}, + ) + self.assertSetEqual( + variables_with_fake_dims, + {'/Soil_Moisture_Retrieval_Data_PM/variable_with_fake_dims'}, + ) + + def test_any_absent_dimension_variables(self): + """Ensure that variables with fake dimensions are + detected with a True return value + + """ + + with self.subTest('Returns true for variables with fake dimensions'): + variable_has_fake_dims = any_absent_dimension_variables( + self.test_varinfo, + '/Soil_Moisture_Retrieval_Data_PM/variable_with_fake_dims', + ) + self.assertTrue(variable_has_fake_dims) + with self.subTest('Returns false for variables with dimensions'): + variable_has_fake_dims = any_absent_dimension_variables( + self.test_varinfo, '/Soil_Moisture_Retrieval_Data_AM/variable_has_dim' + ) + self.assertFalse(variable_has_fake_dims)