Ejemplo n.º 1
0
    def test_getNotRectangle_lon1leLon2Lat1leLat2(self):
        """
        Tests that not_rectangle is True and False in the grid cells expected
        according to the user-specified lon_1, lon_2, lat_1, lat_2
        When lon_1 <= lon_2 and lat_1 <= lat_2, expect not_rectangle to be
        False in a rectangle bounded by these lon/lat values
        Work with integer lon/lat values to keep the testing simple
        """
        # get longxy, latixy that would normally come from an fsurdat file
        # self._get_longxy_latixy will convert -180 to 180 to 0-360 longitudes
        # get cols, rows also
        min_lon = 2  # expects min_lon < max_lon
        min_lat = 3  # expects min_lat < max_lat
        longxy, latixy, cols, rows = self._get_longxy_latixy(
            _min_lon=min_lon, _max_lon=7, _min_lat=min_lat, _max_lat=8)

        # get not_rectangle from user-defined lon_1, lon_2, lat_1, lat_2
        lon_1 = 3
        lon_2 = 5  # lon_1 < lon_2
        lat_1 = 6
        lat_2 = 8  # lat_1 < lat_2
        not_rectangle = ModifyFsurdat._get_not_rectangle(
            lon_1=lon_1, lon_2=lon_2, lat_1=lat_1, lat_2=lat_2,
            longxy=longxy, latixy=latixy)
        compare = np.ones((rows,cols))
        # assert this to confirm intuitive understanding of these matrices
        self.assertEqual(np.size(not_rectangle), np.size(compare))

        # Hardwire where I expect not_rectangle to be False (0)
        # I have chosen the lon/lat ranges to match their corresponding index
        # values to keep this simple
        compare[lat_1-min_lat:lat_2-min_lat+1, lon_1-min_lon:lon_2-min_lon+1] = 0
        np.testing.assert_array_equal(not_rectangle, compare)
Ejemplo n.º 2
0
    def test_getNotRectangle_latsOutOfBounds(self):
        """
        Tests that out-of-bound latitude values abort with message
        Out-of-bound longitudes already tested in test_unit_utils.py
        """
        # get longxy, latixy that would normally come from an fsurdat file
        # self._get_longxy_latixy will convert -180 to 180 to 0-360 longitudes
        # get cols, rows also
        min_lon = 0  # expects min_lon < max_lon
        min_lat = -5  # expects min_lat < max_lat
        longxy, latixy, _, _ = self._get_longxy_latixy(
            _min_lon=min_lon, _max_lon=359, _min_lat=min_lat, _max_lat=5)

        # get not_rectangle from user-defined lon_1, lon_2, lat_1, lat_2
        # I have chosen the lon/lat ranges to match their corresponding index
        # values to keep this simple (see usage below)
        lon_1 = 355
        lon_2 = 5
        lat_1 = -91
        lat_2 = 91
        with self.assertRaisesRegex(SystemExit,
            "lat_1 and lat_2 need to be in the range -90 to 90"):
            _ = ModifyFsurdat._get_not_rectangle(
                lon_1=lon_1, lon_2=lon_2, lat_1=lat_1, lat_2=lat_2,
                longxy=longxy, latixy=latixy)
Ejemplo n.º 3
0
    def test_setvarLev(self):
        """
        Tests that setvar_lev0, setvar_lev1, and setvar_lev2 update values of
        variables within a rectangle defined by user-specified
        lon_1, lon_2, lat_1, lat_2
        """
        # get longxy, latixy that would normally come from an fsurdat file
        # self._get_longxy_latixy will convert -180 to 180 to 0-360 longitudes
        # get cols, rows also
        min_lon = 2  # expects min_lon < max_lon
        min_lat = 3  # expects min_lat < max_lat
        longxy, latixy, cols, rows = self._get_longxy_latixy(
            _min_lon=min_lon, _max_lon=10, _min_lat=min_lat, _max_lat=12)

        # get not_rectangle from user-defined lon_1, lon_2, lat_1, lat_2
        lon_1 = 3
        lon_2 = 5  # lon_1 < lon_2
        lat_1 = 5
        lat_2 = 7  # lat_1 < lat_2

        # create xarray dataset containing lev0, lev1, and lev2 variables;
        # the fsurdat_modify tool reads variables like this from fsurdat file
        var_1d = np.arange(cols)
        var_lev2 = var_1d * np.ones((rows,cols,rows,cols))
        var_lev1 = var_1d * np.ones((cols,rows,cols))
        my_data = xr.Dataset(data_vars=dict(
            LONGXY=(["x", "y"], longxy),  # use LONGXY as var_lev0
            LATIXY=(["x", "y"], latixy),  # __init__ expects LONGXY, LATIXY
            var_lev1=(["w", "x", "y"], var_lev1),
            var_lev2=(["v", "w", "x", "y"], var_lev2)))

        # create ModifyFsurdat object
        modify_fsurdat = ModifyFsurdat(my_data=my_data, lon_1=lon_1,
            lon_2=lon_2, lat_1=lat_1, lat_2=lat_2, landmask_file=None)

        # initialize and then modify the comparison matrices
        comp_lev0 = modify_fsurdat.file.LONGXY
        comp_lev1 = modify_fsurdat.file.var_lev1
        comp_lev2 = modify_fsurdat.file.var_lev2
        val_for_rectangle = 1.5
        comp_lev0[lat_1-min_lat:lat_2-min_lat+1,
                  lon_1-min_lon:lon_2-min_lon+1] = val_for_rectangle
        comp_lev1[...,lat_1-min_lat:lat_2-min_lat+1,
                    lon_1-min_lon:lon_2-min_lon+1] = val_for_rectangle
        comp_lev2[...,lat_1-min_lat:lat_2-min_lat+1,
                      lon_1-min_lon:lon_2-min_lon+1] = val_for_rectangle

        # test setvar
        modify_fsurdat.setvar_lev0('LONGXY', val_for_rectangle)
        np.testing.assert_array_equal(modify_fsurdat.file.LONGXY, comp_lev0)

        modify_fsurdat.setvar_lev1('var_lev1', val_for_rectangle, cols-1)
        np.testing.assert_array_equal(modify_fsurdat.file.var_lev1, comp_lev1)

        modify_fsurdat.setvar_lev2('var_lev2', val_for_rectangle, cols-1,
                                                                  rows-1)
        np.testing.assert_array_equal(modify_fsurdat.file.var_lev2, comp_lev2)
Ejemplo n.º 4
0
    def test_getNotRectangle_lonsStraddle0deg(self):
        """
        Tests that not_rectangle is True and False in the grid cells expected
        according to the user-specified lon_1, lon_2, lat_1, lat_2
        When lon_1 > lon_2 and lat_1 > lat_2, expect not_rectangle to be
        False in four rectangles bounded by these lon/lat values, in the
        top left, top right, bottom left, and bottom right of the domain
        Work with integer lon/lat values to keep the testing simple
        """
        # get longxy, latixy that would normally come from an fsurdat file
        # self._get_longxy_latixy will convert -180 to 180 to 0-360 longitudes
        # get cols, rows also
        min_lon = 0  # expects min_lon < max_lon
        min_lat = -5  # expects min_lat < max_lat
        longxy, latixy, cols, rows = self._get_longxy_latixy(_min_lon=min_lon,
                                                             _max_lon=359,
                                                             _min_lat=min_lat,
                                                             _max_lat=5)

        # get not_rectangle from user-defined lon_1, lon_2, lat_1, lat_2
        # I have chosen the lon/lat ranges to match their corresponding index
        # values to keep this simple (see usage below)
        lon_1 = 355
        lon_2 = 5  # lon_1 > lon_2
        lat_1 = -4
        lat_2 = -6  # lat_1 > lat_2
        not_rectangle = ModifyFsurdat._get_not_rectangle(
            lon_1=lon_1,
            lon_2=lon_2,
            lat_1=lat_1,
            lat_2=lat_2,
            longxy=longxy,
            latixy=latixy,
        )
        compare = np.ones((rows, cols))
        # assert this to confirm intuitive understanding of these matrices
        self.assertEqual(np.size(not_rectangle), np.size(compare))

        # Hardwire where I expect not_rectangle to be False (0)
        # I have chosen the lon/lat ranges to match their corresponding index
        # values to keep this simple
        compare[:lat_2 - min_lat + 1, :lon_2 - min_lon + 1] = 0
        compare[:lat_2 - min_lat + 1, lon_1 - min_lon:] = 0
        compare[lat_1 - min_lat:, :lon_2 - min_lon + 1] = 0
        compare[lat_1 - min_lat:, lon_1 - min_lon:] = 0
        np.testing.assert_array_equal(not_rectangle, compare)
Ejemplo n.º 5
0
def fsurdat_modifier(cfg_path):
    """Implementation of fsurdat_modifier command"""
    # read the .cfg (config) file
    config = ConfigParser()
    config.read(cfg_path)
    section = config.sections()[0]  # name of the first section

    # required: user must set these in the .cfg file
    fsurdat_in = get_config_value(config=config, section=section,
        item='fsurdat_in', file_path=cfg_path)
    fsurdat_out = get_config_value(config=config, section=section,
        item='fsurdat_out', file_path=cfg_path)

    # required but fallback values available for variables omitted
    # entirely from the .cfg file
    idealized = get_config_value(config=config, section=section,
        item='idealized', file_path=cfg_path, convert_to_type=bool)
    zero_nonveg = get_config_value(config=config, section=section,
        item='zero_nonveg', file_path=cfg_path, convert_to_type=bool)

    lnd_lat_1 = get_config_value(config=config, section=section,
        item='lnd_lat_1', file_path=cfg_path, convert_to_type=float)
    lnd_lat_2 = get_config_value(config=config, section=section,
        item='lnd_lat_2', file_path=cfg_path, convert_to_type=float)
    lnd_lon_1 = get_config_value(config=config, section=section,
        item='lnd_lon_1', file_path=cfg_path, convert_to_type=float)
    lnd_lon_2 = get_config_value(config=config, section=section,
        item='lnd_lon_2', file_path=cfg_path, convert_to_type=float)

    landmask_file = get_config_value(config=config, section=section,
        item='landmask_file', file_path=cfg_path, can_be_unset=True)

    # not required: user may set these in the .cfg file
    dom_nat_pft = get_config_value(config=config, section=section,
        item='dom_nat_pft', file_path=cfg_path,
        allowed_values=range(15),  # integers from 0 to 14
        convert_to_type=int, can_be_unset=True)

    lai = get_config_value(config=config, section=section, item='lai',
        file_path=cfg_path, is_list=True,
        convert_to_type=float, can_be_unset=True)
    sai = get_config_value(config=config, section=section, item='sai',
        file_path=cfg_path, is_list=True,
        convert_to_type=float, can_be_unset=True)
    hgt_top = get_config_value(config=config, section=section,
        item='hgt_top', file_path=cfg_path, is_list=True,
        convert_to_type=float, can_be_unset=True)
    hgt_bot = get_config_value(config=config, section=section,
        item='hgt_bot', file_path=cfg_path, is_list=True,
        convert_to_type=float, can_be_unset=True)

    soil_color = get_config_value(config=config, section=section,
        item='soil_color', file_path=cfg_path,
        allowed_values=range(1, 21),  # integers from 1 to 20
        convert_to_type=int, can_be_unset=True)

    std_elev = get_config_value(config=config, section=section,
        item='std_elev', file_path=cfg_path,
        convert_to_type=float, can_be_unset=True)
    max_sat_area = get_config_value(config=config, section=section,
        item='max_sat_area', file_path=cfg_path,
        convert_to_type=float, can_be_unset=True)

    # Create ModifyFsurdat object
    modify_fsurdat = ModifyFsurdat.init_from_file(fsurdat_in,
        lnd_lon_1, lnd_lon_2, lnd_lat_1, lnd_lat_2, landmask_file)

    # ------------------------------
    # modify surface data properties
    # ------------------------------

    # Set fsurdat variables in a rectangle that could be global (default).
    # Note that the land/ocean mask gets specified in the domain file for
    # MCT or the ocean mesh files for NUOPC. Here the user may specify
    # fsurdat variables inside a box but cannot change which points will
    # run as land and which as ocean.
    if idealized:
        modify_fsurdat.set_idealized()  # set 2D variables
        # set 3D and 4D variables pertaining to natural vegetation
        modify_fsurdat.set_dom_nat_pft(dom_nat_pft=0, lai=[], sai=[],
                                       hgt_top=[], hgt_bot=[])

    if dom_nat_pft is not None:  # overwrite "idealized" value
        modify_fsurdat.set_dom_nat_pft(dom_nat_pft=dom_nat_pft,
                                       lai=lai, sai=sai,
                                       hgt_top=hgt_top, hgt_bot=hgt_bot)

    if max_sat_area is not None:  # overwrite "idealized" value
        modify_fsurdat.setvar_lev0('FMAX', max_sat_area)

    if std_elev is not None:  # overwrite "idealized" value
        modify_fsurdat.setvar_lev0('STD_ELEV', std_elev)

    if soil_color is not None:  # overwrite "idealized" value
        modify_fsurdat.setvar_lev0('SOIL_COLOR', soil_color)

    if zero_nonveg:
        modify_fsurdat.zero_nonveg()

    # ----------------------------------------------
    # Output the now modified CTSM surface data file
    # ----------------------------------------------
    modify_fsurdat.write_output(fsurdat_in, fsurdat_out)