예제 #1
0
    def test_resume(self):
        model_ab = GR4JCN()
        model_ab.config.rvh.hrus = (GR4JCN.LandHRU(**salmon_land_hru_1), )
        kwargs = dict(params=(0.529, -3.396, 407.29, 1.072, 16.9, 0.947), )
        # Reference run
        model_ab(
            TS,
            run_name="run_ab",
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2001, 1, 1),
            **kwargs,
        )

        model_a = GR4JCN()

        model_a.config.rvh.hrus = (GR4JCN.LandHRU(**salmon_land_hru_1), )
        model_a(
            TS,
            run_name="run_a",
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2000, 7, 1),
            **kwargs,
        )

        # Path to solution file from run A
        rvc = model_a.outputs["solution"]

        # Resume with final state from live model
        model_a.resume()

        model_a(
            TS,
            run_name="run_2",
            start_date=dt.datetime(2000, 7, 1),
            end_date=dt.datetime(2001, 1, 1),
            **kwargs,
        )

        for key in ["Soil Water[0]", "Soil Water[1]"]:
            np.testing.assert_array_almost_equal(
                model_a.storage[key] - model_ab.storage[key], 0, 5)

        # Resume with final state from saved solution file
        model_b = GR4JCN()
        model_b.config.rvh.hrus = (GR4JCN.LandHRU(**salmon_land_hru_1), )
        model_b.resume(
            rvc
        )  # <--------- And this is how you feed it to a brand new model.
        model_b(
            TS,
            run_name="run_2",
            start_date=dt.datetime(2000, 7, 1),
            end_date=dt.datetime(2001, 1, 1),
            **kwargs,
        )

        for key in ["Soil Water[0]", "Soil Water[1]"]:
            np.testing.assert_array_almost_equal(
                model_b.storage[key] - model_ab.storage[key], 0, 5)
예제 #2
0
    def test_hindcasting_GEPS(self, tmpdir):

        # Prepare a RAVEN model run using historical data, GR4JCN in this case.
        # This is a dummy run to get initial states. In a real forecast situation,
        # this run would end on the day before the forecast, but process is the same.
        ts = get_local_testdata(
            "raven-gr4j-cemaneige/Salmon-River-Near-Prince-George_meteo_daily.nc"
        )
        model = GR4JCN(workdir=tmpdir)
        model(
            ts,
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2002, 6, 1),
            area=44250.6,
            elevation=843.0,
            latitude=54.4848,
            longitude=-123.3659,
            params=(0.529, -3.396, 407.29, 1.072, 16.9, 0.947),
        )

        # Extract the final states that will be used as the next initial states
        rvc = model.outputs["solution"]

        ts20 = get_local_testdata("caspar_eccc_hindcasts/geps_watershed.nc")
        nm = 20

        # It is necessary to clean the model state because the input variables of the previous
        # model are not the same as the ones provided in the forecast model. therefore, if we
        # do not clean, the model will simply add the hindcast file to the list of available
        # data provided in the testdata above. Then the dates will not work, and the model errors.
        model = GR4JCN()

        model.rvc.parse(rvc.read_text())

        # And run the model with the forecast data.
        model(
            ts=ts20,
            nc_index=range(nm),
            start_date=dt.datetime(2018, 6, 1),
            end_date=dt.datetime(2018, 6, 10),
            area=44250.6,
            elevation=843.0,
            latitude=54.4848,
            longitude=-123.3659,
            params=(0.529, -3.396, 407.29, 1.072, 16.9, 0.947),
            overwrite=True,
            pr={
                "linear_transform": (1000.0, 0.0),
                "time_shift": -0.25,
                "deaccumulate": True,
            },
            tas={"time_shift": -0.25},
        )

        # The model now has the forecast data generated and it has 10 days of forecasts.
        assert len(model.q_sim.values) == 10

        # Also see if GEPS has 20 members produced.
        assert model.q_sim.values.shape[1] == nm
예제 #3
0
def test_race():
    model1 = GR4JCN()
    model1.config.rvi.suppress_output = True
    model2 = GR4JCN()
    ost = GR4JCN_OST()

    assert model1.config.rvi.suppress_output.startswith(":SuppressOutput")
    assert model2.config.rvi.suppress_output == ""
    assert ost.config.rvi.suppress_output.startswith(":SuppressOutput")
예제 #4
0
    def test_forecasting_GEPS(self):

        # Prepare a RAVEN model run using historical data, GR4JCN in this case.
        # This is a dummy run to get initial states. In a real forecast situation,
        # this run would end on the day before the forecast, but process is the same.
        ts = get_local_testdata(
            "raven-gr4j-cemaneige/Salmon-River-Near-Prince-George_meteo_daily.nc"
        )
        model = GR4JCN()
        model(
            ts,
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2002, 6, 1),
            params=(0.529, -3.396, 407.29, 1.072, 16.9, 0.947),
            hrus=(hru,),
        )

        # Extract the final states that will be used as the next initial states
        rvc = model.outputs["solution"]

        # Collect test forecast data for location and climate model (20 members)
        ts20 = get_local_testdata("eccc_forecasts/geps_watershed.nc")
        nm = 20

        # It is necessary to clean the model state because the input variables of the previous
        # model are not the same as the ones provided in the forecast model. therefore, if we
        # do not clean, the model will simply add the hindcast file to the list of available
        # data provided in the testdata above. Then the dates will not work, and the model errors.

        model = GR4JCN()

        model.config.rvc.parse_solution(rvc.read_text())

        model(
            ts=(ts20,),
            duration=9,
            params=(0.529, -3.396, 407.29, 1.072, 16.9, 0.947),
            hrus=(hru,),
            overwrite=True,
            pr={"time_shift": -0.25, "deaccumulate": True},
            tas={"time_shift": -0.25},
            parallel=dict(nc_index=range(nm)),
        )

        # The model now has the forecast data generated and it has 10 days of forecasts.
        assert len(model.q_sim.values) == 10

        # Also see if GEPS has 20 members produced.
        assert model.q_sim.values.shape[1] == nm

        # Check all members are different (checking snow because data in winter)
        assert len(set(model.storage.Snow.isel(time=-1).values)) == nm
예제 #5
0
    def test_evaluation(self):
        model = GR4JCN()

        model.config.rvh.hrus = (GR4JCN.LandHRU(**salmon_land_hru_1), )

        model(
            TS,
            area=4250.6,
            elevation=843.0,
            latitude=54.4848,
            longitude=-123.3659,
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2002, 1, 1),
            params=(0.529, -3.396, 407.29, 1.072, 16.9, 0.947),
            suppress_output=False,
            evaluation_metrics=["RMSE", "KLING_GUPTA"],
            evaluation_periods=[
                EvaluationPeriod("period1", "2000-01-01", "2000-12-31"),
                EvaluationPeriod("period2", "2001-01-01", "2001-12-31"),
            ],
        )
        d = model.diagnostics
        assert "DIAG_RMSE" in d
        assert "DIAG_KLING_GUPTA" in d
        assert len(d["DIAG_RMSE"]) == 3  # ALL, period1, period2
예제 #6
0
    def test_simple(self):
        model = GR4JCN(tempfile.mkdtemp())

        model.rvi.start_date = dt.datetime(2000, 1, 1)
        model.rvi.end_date = dt.datetime(2002, 1, 1)
        model.rvi.run_name = "test"

        model.rvh.name = "Salmon"
        model.rvh.area = "4250.6"
        model.rvh.elevation = "843.0"
        model.rvh.latitude = 54.4848
        model.rvh.longitude = -123.3659

        model.rvt.pr.deaccumulate = False

        model.rvp.params = model.params(0.529, -3.396, 407.29, 1.072, 16.9,
                                        0.947)
        assert model.rvi.suppress_output == ""
        model(TS)

        d = model.diagnostics
        # yields NSE=0.???? for full period 1954-2010
        assert model.rvi.calendar == "GREGORIAN"
        # Check parser
        assert 1 in model.solution["HRUStateVariableTable"]["data"]

        np.testing.assert_almost_equal(d["DIAG_NASH_SUTCLIFFE"], -0.117301, 2)

        hds = model.q_sim
        assert hds.attrs["long_name"] == "Simulated outflows"

        # Check attributes
        assert model.hydrograph.attrs["model_id"] == "gr4jcn"
예제 #7
0
    def test_update_soil_water(self):
        params = (0.529, -3.396, 407.29, 1.072, 16.9, 0.947)
        # Reference run
        model = GR4JCN()
        model.config.rvh.hrus = (GR4JCN.LandHRU(**salmon_land_hru_1), )
        model(
            TS,
            run_name="run_a",
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2000, 2, 1),
            params=params,
        )

        s_0 = float(model.storage["Soil Water[0]"].isel(time=-1).values)
        s_1 = float(model.storage["Soil Water[1]"].isel(time=-1).values)

        # hru_state = replace(model.rvc.hru_state, soil0=s_0, soil1=s_1)
        model.config.rvc.hru_states[1] = replace(
            model.config.rvc.hru_states[1], soil0=s_0, soil1=s_1)

        model(
            TS,
            run_name="run_b",
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2000, 2, 1),
            # hru_state=hru_state,
            params=params,
        )

        assert s_0 != model.storage["Soil Water[0]"].isel(time=-1)
        assert s_1 != model.storage["Soil Water[1]"].isel(time=-1)
예제 #8
0
    def test_config_update(self):
        model = GR4JCN()

        # This is a regular attribute member
        model.config.update("run_name", "test")
        assert model.config.rvi.run_name == "test"

        # This is a computed property
        model.config.update("evaporation", "PET_FROMMONTHLY")
        assert model.config.rvi.evaporation == "PET_FROMMONTHLY"

        # Existing property but wrong value (the enum cast should throw an error)
        with pytest.raises(ValueError):
            model.config.update("routing", "WRONG")

        # Non-existing attribute
        with pytest.raises(AttributeError):
            model.config.update("why", "not?")

        # Params

        model.config.update(
            "params", np.array([0.529, -3.396, 407.29, 1.072, 16.9, 0.947]))
        assert model.config.rvp.params.GR4J_X1 == 0.529

        model.config.update("params",
                            [0.529, -3.396, 407.29, 1.072, 16.9, 0.947])
        assert model.config.rvp.params.GR4J_X1 == 0.529

        model.config.update("params",
                            (0.529, -3.396, 407.29, 1.072, 16.9, 0.947))
        assert model.config.rvp.params.GR4J_X1 == 0.529
예제 #9
0
    def test_update_soil_water(self):
        kwargs = dict(
            area=4250.6,
            elevation=843.0,
            latitude=54.4848,
            longitude=-123.3659,
            params=(0.529, -3.396, 407.29, 1.072, 16.9, 0.947),
        )
        # Reference run
        model = GR4JCN()
        model(
            TS,
            run_name="run_a",
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2000, 2, 1),
            **kwargs,
        )

        s_0 = float(model.storage["Soil Water[0]"].isel(time=-1).values)
        s_1 = float(model.storage["Soil Water[1]"].isel(time=-1).values)

        hru_state = replace(model.rvc.hru_state, soil0=s_0, soil1=s_1)

        model(
            TS,
            run_name="run_b",
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2000, 2, 1),
            hru_state=hru_state,
            **kwargs,
        )

        assert s_0 != model.storage["Soil Water[0]"].isel(time=-1)
        assert s_1 != model.storage["Soil Water[1]"].isel(time=-1)
예제 #10
0
    def test_error(self):
        model = GR4JCN()

        model.config.rvp.params = model.Params(0.529, -3.396, 407.29, 1.072,
                                               16.9, 0.947)

        with pytest.raises(RavenError) as exc:
            model(TS)

        assert "CHydroUnit constructor:: HRU 1 has a negative or zero area" in str(
            exc.value)
예제 #11
0
    def test_overwrite(self):
        model = GR4JCN()

        model.config.rvh.hrus = (GR4JCN.LandHRU(**salmon_land_hru_1), )

        model(
            TS,
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2002, 1, 1),
            params=(0.529, -3.396, 407.29, 1.072, 16.9, 0.947),
        )
        assert model.config.rvi.suppress_output == ""

        qsim1 = model.q_sim.copy(deep=True)
        m1 = qsim1.mean()

        # This is only needed temporarily while we fix this: https://github.com/CSHS-CWRA/RavenPy/issues/4
        # Please remove when fixed!
        model.hydrograph.close()  # Needed with xarray 0.16.1

        model(TS,
              params=(0.5289, -3.397, 407.3, 1.071, 16.89, 0.948),
              overwrite=True)

        qsim2 = model.q_sim.copy(deep=True)
        m2 = qsim2.mean()

        # This is only needed temporarily while we fix this: https://github.com/CSHS-CWRA/RavenPy/issues/4
        # Please remove when fixed!
        model.hydrograph.close()  # Needed with xarray 0.16.1

        assert m1 != m2

        np.testing.assert_almost_equal(m1, m2, 1)

        d = model.diagnostics

        np.testing.assert_almost_equal(d["DIAG_NASH_SUTCLIFFE"], -0.117315, 4)

        model.config.rvc.hru_states[1] = HRUStateVariableTableCommand.Record(
            soil0=0)

        # Set initial conditions explicitly
        model(
            TS,
            end_date=dt.datetime(2001, 2, 1),
            # hru_state=HRUStateVariableTableCommand.Record(soil0=0),
            overwrite=True,
        )
        assert model.q_sim.isel(time=1).values[0] < qsim2.isel(
            time=1).values[0]
예제 #12
0
    def test_assign(self):
        model = GR4JCN()
        model.assign("run_name", "test")
        assert model.rvi.run_name == "test"

        model.assign("params",
                     np.array([0.529, -3.396, 407.29, 1.072, 16.9, 0.947]))
        assert model.rvp.params.GR4J_X1 == 0.529

        model.assign("params", [0.529, -3.396, 407.29, 1.072, 16.9, 0.947])
        assert model.rvp.params.GR4J_X1 == 0.529

        model.assign("params", (0.529, -3.396, 407.29, 1.072, 16.9, 0.947))
        assert model.rvp.params.GR4J_X1 == 0.529
예제 #13
0
    def test_run_new_hrus_param(self):
        model = GR4JCN()

        model(
            TS,
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2002, 1, 1),
            params=(0.529, -3.396, 407.29, 1.072, 16.9, 0.947),
            suppress_output=False,
            hrus=(GR4JCN.LandHRU(**salmon_land_hru_1), ),
        )
        d = model.diagnostics

        np.testing.assert_almost_equal(d["DIAG_NASH_SUTCLIFFE"], -0.117301, 4)
예제 #14
0
    def test_dap(self):
        """Test Raven with DAP link instead of local netCDF file."""
        model = GR4JCN()
        config = dict(
            start_date=dt.datetime(2000, 6, 1),
            end_date=dt.datetime(2000, 6, 10),
            run_name="test",
            hrus=(GR4JCN.LandHRU(**salmon_land_hru_1), ),
            params=model.Params(0.529, -3.396, 407.29, 1.072, 16.9, 0.947),
        )

        ts = (
            f"{TDS}/raven-gr4j-cemaneige/Salmon-River-Near-Prince-George_meteo_daily.nc"
        )
        model(ts, **config)
예제 #15
0
    def test_run(self):
        model = GR4JCN()
        model(
            TS,
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2002, 1, 1),
            area=4250.6,
            elevation=843.0,
            latitude=54.4848,
            longitude=-123.3659,
            params=(0.529, -3.396, 407.29, 1.072, 16.9, 0.947),
            suppress_output=False,
        )
        d = model.diagnostics

        np.testing.assert_almost_equal(d["DIAG_NASH_SUTCLIFFE"], -0.117301, 2)
예제 #16
0
    def test_resume_earlier(self):
        """Check that we can resume a run with the start date set at another date than the time stamp in the
        solution."""
        kwargs = dict(
            area=4250.6,
            elevation=843.0,
            latitude=54.4848,
            longitude=-123.3659,
            params=(0.529, -3.396, 407.29, 1.072, 16.9, 0.947),
        )
        # Reference run
        model = GR4JCN()
        model(
            TS,
            run_name="run_a",
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2000, 2, 1),
            **kwargs,
        )

        s_a = model.storage["Soil Water[0]"].isel(time=-1)

        # Path to solution file from run A
        rvc = model.outputs["solution"]

        # Resume with final state from live model
        # We have two options to do this:
        # 1. Replace model template by solution file as is: model.resume()
        # 2. Replace variable in RVC class by parsed values: model.rvc.parse(rvc.read_text())
        # I think in many cases option 2 will prove simpler.

        model.rvc.parse(rvc.read_text())

        model(
            TS,
            run_name="run_b",
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2000, 2, 1),
            **kwargs,
        )

        s_b = model.storage["Soil Water[0]"].isel(time=-1)
        assert s_a != s_b
예제 #17
0
    def test_parallel_basins(self, input2d):
        ts = input2d
        model = GR4JCN()
        model.config.rvh.hrus = (GR4JCN.LandHRU(**salmon_land_hru_1), )

        model(
            ts,
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2002, 1, 1),
            params=[0.529, -3.396, 407.29, 1.072, 16.9, 0.947],
            # name=["basin1", "basin2"],  # Not sure about this..
            suppress_output=False,
            parallel={"nc_index": [0, 0]},
        )

        assert len(model.diagnostics) == 2
        assert len(model.hydrograph.nbasins) == 2
        np.testing.assert_array_equal(model.hydrograph.basin_name[:],
                                      ["sub_001", "sub_001"])
        z = zipfile.ZipFile(model.outputs["rv_config"])
        assert len(z.filelist) == 10
예제 #18
0
    def test_parallel_params(self):
        model = GR4JCN()
        model.config.rvh.hrus = (GR4JCN.LandHRU(**salmon_land_hru_1), )

        model(
            TS,
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2002, 1, 1),
            suppress_output=False,
            parallel={
                "params": [
                    (0.529, -3.396, 407.29, 1.072, 16.9, 0.947),
                    (0.528, -3.4, 407.3, 1.07, 17, 0.95),
                ]
            },
        )

        assert len(model.diagnostics) == 2
        assert model.hydrograph.dims["params"] == 2
        z = zipfile.ZipFile(model.outputs["rv_config"])
        assert len(z.filelist) == 10
예제 #19
0
    def test_parallel_params(self):
        model = GR4JCN()
        model(
            TS,
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2002, 1, 1),
            area=4250.6,
            elevation=843.0,
            latitude=54.4848,
            longitude=-123.3659,
            params=[
                (0.529, -3.396, 407.29, 1.072, 16.9, 0.947),
                (0.528, -3.4, 407.3, 1.07, 17, 0.95),
            ],
            suppress_output=False,
        )

        assert len(model.diagnostics) == 2
        assert model.hydrograph.dims["params"] == 2
        z = zipfile.ZipFile(model.outputs["rv_config"])
        assert len(z.filelist) == 10
예제 #20
0
    def test_resume_earlier(self):
        """Check that we can resume a run with the start date set at another date than the time stamp in the
        solution."""
        params = (0.529, -3.396, 407.29, 1.072, 16.9, 0.947)
        # Reference run
        model = GR4JCN()
        model.config.rvh.hrus = (GR4JCN.LandHRU(**salmon_land_hru_1), )
        model(
            TS,
            run_name="run_a",
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2000, 2, 1),
            params=params,
        )

        s_a = model.storage["Soil Water[0]"].isel(time=-1)

        # Path to solution file from run A
        rvc = model.outputs["solution"]

        # Resume with final state from live model
        # We have two options to do this:
        # 1. Replace model template by solution file as is: model.resume()
        # 2. Replace variable in RVC class by parsed values: model.rvc.parse(rvc.read_text())
        # I think in many cases option 2 will prove simpler.

        model.config.rvc.parse_solution(rvc.read_text())

        model(
            TS,
            run_name="run_b",
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2000, 2, 1),
            params=params,
        )

        s_b = model.storage["Soil Water[0]"].isel(time=-1)
        assert s_a != s_b
예제 #21
0
    def test_parallel_basins(self, input2d):
        ts = input2d
        model = GR4JCN()
        model(
            ts,
            start_date=dt.datetime(2000, 1, 1),
            end_date=dt.datetime(2002, 1, 1),
            area=4250.6,
            elevation=843.0,
            latitude=54.4848,
            longitude=-123.3659,
            params=[0.529, -3.396, 407.29, 1.072, 16.9, 0.947],
            nc_index=[0, 0],
            name=["basin1", "basin2"],
            suppress_output=False,
        )

        assert len(model.diagnostics) == 2
        assert len(model.hydrograph.nbasins) == 2
        np.testing.assert_array_equal(model.hydrograph.basin_name[:],
                                      ["basin1", "basin2"])
        z = zipfile.ZipFile(model.outputs["rv_config"])
        assert len(z.filelist) == 10
예제 #22
0
 def test_canopex(self):
     CANOPEX_DAP = (
         "https://pavics.ouranos.ca/twitcher/ows/proxy/thredds/dodsC/birdhouse/ets"
         "/Watersheds_5797_cfcompliant.nc")
     model = GR4JCN()
     config = dict(
         start_date=dt.datetime(2010, 6, 1),
         end_date=dt.datetime(2010, 6, 10),
         nc_index=5600,
         run_name="Test_run",
         rain_snow_fraction="RAINSNOW_DINGMAN",
         tasmax={"offset": -273.15},
         tasmin={"offset": -273.15},
         pr={"scale": 86400.0},
         hrus=[
             model.LandHRU(area=3650.47,
                           latitude=49.51,
                           longitude=-95.72,
                           elevation=330.59)
         ],
         params=model.Params(108.02, 2.8693, 25.352, 1.3696, 1.2483,
                             0.30679),
     )
     model(ts=CANOPEX_DAP, **config)
예제 #23
0
    def test_simple(self):
        model = GR4JCN_OST()
        model.config.rvh.hrus = (GR4JCN.LandHRU(**salmon_land_hru_1), )

        # Parameter bounds
        low = (0.01, -15.0, 10.0, 0.0, 1.0, 0.0)
        high = (2.5, 10.0, 700.0, 7.0, 30.0, 1.0)

        model.configure(
            get_local_testdata("ostrich-gr4j-cemaneige/OstRandomNumbers.txt"))

        model(
            TS,
            start_date=dt.datetime(1954, 1, 1),
            duration=208,
            lowerBounds=low,
            upperBounds=high,
            algorithm="DDS",
            random_seed=0,
            max_iterations=10,
        )

        d = model.diagnostics

        np.testing.assert_almost_equal(d["DIAG_NASH_SUTCLIFFE"], 0.50717, 4)

        # Random number seed: 123
        # Budget:             10
        # Algorithm:          DDS
        # :StartDate          1954-01-01 00:00:00
        # :Duration           208
        opt_para = astuple(model.calibrated_params)
        opt_func = model.obj_func

        np.testing.assert_almost_equal(
            opt_para,
            [2.424726, 3.758972, 204.3856, 5.866946, 16.60408, 0.3728098],
            4,
            err_msg="calibrated parameter set is not matching expected value",
        )

        np.testing.assert_almost_equal(
            opt_func,
            -0.50717,
            4,
            err_msg="calibrated NSE is not matching expected value",
        )

        # # Random number seed: 123
        # # Budget:             50
        # # Algorithm:          DDS
        # # :StartDate          1954-01-01 00:00:00
        # # :Duration           20819
        # np.testing.assert_almost_equal( opt_para, [0.3243268,3.034247,407.2890,2.722774,12.18124,0.9468769], 4,
        #                                 err_msg='calibrated parameter set is not matching expected value')
        # np.testing.assert_almost_equal( opt_func, -0.5779910, 4,
        #                                 err_msg='calibrated NSE is not matching expected value')
        gr4j = GR4JCN()
        gr4j.config.rvh.hrus = (GR4JCN.LandHRU(**salmon_land_hru_1), )

        gr4j(
            TS,
            start_date=dt.datetime(1954, 1, 1),
            duration=208,
            params=model.calibrated_params,
        )

        np.testing.assert_almost_equal(gr4j.diagnostics["DIAG_NASH_SUTCLIFFE"],
                                       d["DIAG_NASH_SUTCLIFFE"])
예제 #24
0
    def test_simple(self):
        model = GR4JCN()
        print(model.workdir)
        model.config.rvi.start_date = dt.datetime(2000, 1, 1)
        model.config.rvi.end_date = dt.datetime(2002, 1, 1)
        model.config.rvi.run_name = "test"

        model.config.rvh.hrus = (GR4JCN.LandHRU(**salmon_land_hru_1), )

        model.config.rvp.params = model.Params(0.529, -3.396, 407.29, 1.072,
                                               16.9, 0.947)

        total_area_in_m2 = model.config.rvh.hrus[0].area * 1000 * 1000
        model.config.rvp.avg_annual_runoff = get_average_annual_runoff(
            TS, total_area_in_m2)

        np.testing.assert_almost_equal(model.config.rvp.avg_annual_runoff,
                                       208.4805694844741)

        assert model.config.rvi.suppress_output == ""

        model(TS)

        # ------------
        # Check quality (diagnostic) of simulated streamflow values
        # ------------
        d = model.diagnostics
        np.testing.assert_almost_equal(d["DIAG_NASH_SUTCLIFFE"], -0.117301, 4)

        # ------------
        # Check simulated streamflow values q_sim
        # ------------
        hds = model.q_sim

        assert hds.attrs["long_name"] == "Simulated outflows"

        assert len(hds.nbasins) == 1  # number of "gauged" basins is 1

        # We only have one SB with gauged=True, so the output has a single column.
        # The number of time steps simulated between (2000, 1, 1) and
        # (2002, 1, 1) is 732.
        assert hds.shape == (732, 1)

        # Check simulated streamflow at first three timesteps and three simulated
        # timesteps in the middle of the simulation period.
        dates = (
            "2000-01-01",
            "2000-01-02",
            "2000-01-03",
            "2001-01-30",
            "2001-01-31",
            "2001-02-01",
        )

        target_q_sim = [
            0.0, 0.165788, 0.559366, 12.374606, 12.33398, 12.293458
        ]

        for t in range(6):
            np.testing.assert_almost_equal(hds.sel(nbasins=0, time=dates[t]),
                                           target_q_sim[t], 4)

        # ------------
        # Check parser
        # ------------

        assert model.config.rvi.calendar == RVI.CalendarOptions.GREGORIAN.value

        # ------------
        # Check saved HRU states saved in RVC
        # ------------
        assert 1 in model.solution.hru_states

        # ------------
        # Check attributes
        # ------------
        assert model.hydrograph.attrs["model_id"] == "gr4jcn"
예제 #25
0
    def test_simple(self):

        # get timeseries
        ts = get_local_testdata(
            "raven-gr4j-cemaneige/Salmon-River-Near-Prince-George_meteo_daily.nc"
        )

        # set number of members. Using 7 here to make it easier to find and debug.
        n_members = 7

        # Perturbation parameters for the assimilation, keyed by standard_name
        std = {
            "rainfall": 0.30,
            "prsn": 0.30,
            "tasmin": 2.0,
            "tasmax": 2.0,
            "water_volume_transport_in_river_channel": 0.10,
        }

        # Perturbation distribution
        dists = {
            "pr": "gamma",
            "rainfall": "gamma",
            "prsn": "gamma",
            "water_volume_transport_in_river_channel": "rnorm",
        }

        qkey = "water_volume_transport_in_river_channel"
        if qkey not in std:
            raise ValueError(
                "Assimilation requires perturbing the flow variable.")

        # Assimilation variables (from HRUStateVariable)
        assim_var = ("soil0", "soil1")

        # Assimilation period (days between each assimilation step)
        assim_step_days = 3

        # GR4JCN model instance
        model = GR4JCN()

        # set the start and end dates for the first assimilation period, warm-up
        start_date = dt.datetime(1996, 9, 1)
        end_date = dt.datetime(1996, 9, 30)

        # Catchment properties to populate model
        area = 4250.6
        elevation = 843.0
        latitude = 54.4848
        longitude = -123.3659
        params = (0.1353389, -0.005067198, 576.8007, 6.986121, 1.102917,
                  0.9224778)

        # Do the first assimilation pass to get hru_states and basin_states.
        # Can be skipped if there is already this data from a previous run.
        model, xa, hru_states, basin_states = assimilation_initialization(
            model,
            ts,
            start_date=start_date,
            end_date=start_date + dt.timedelta(days=assim_step_days - 1),
            area=area,
            elevation=elevation,
            latitude=latitude,
            longitude=longitude,
            params=params,
            assim_var=assim_var,
            n_members=n_members,
        )

        # Perturb the inputs for the rest of the assimilation
        perturbed = perturb_full_series(
            model,
            std=std,
            start_date=start_date,
            end_date=end_date,
            dists=dists,
            n_members=n_members,
        )

        # Get observed streamflow for computing results later
        q_obs = xr.open_dataset(ts)["qobs"].sel(
            time=slice(start_date, end_date))

        # Create netcdf for the model.
        p_fn = model.workdir / "perturbed_forcing.nc"
        perturbed = xr.Dataset(perturbed)
        perturbed.to_netcdf(p_fn, mode="w")

        # Run the sequential assimilation for the entire period.
        q_assim, hru_states, basin_states = sequential_assimilation(
            model,
            hru_states,
            basin_states,
            p_fn,
            q_obs,
            assim_var,
            start_date=start_date + dt.timedelta(days=assim_step_days),
            end_date=end_date,
            n_members=n_members,
            assim_step_days=assim_step_days,
        )

        # ==== Reference run ====
        model.config.rvi.run_name = "ref"
        model.config.rvi.start_date = start_date
        model.config.rvi.end_date = end_date

        model.config.rvc.hru_states = {}
        model.config.rvc.basin_states = {}
        model.config.rvc.soil0 = None
        model.config.rvc.soil1 = 15

        model([ts])

        # We can now plot everything!
        plt.plot(q_assim.T, "r",
                 label="Assimilated")  # plot the assimilated flows
        plt.plot(q_obs.T, "b", label="Observed")  # plot the observed flows
        plt.plot(model.q_sim, "g", label="Simulated"
                 )  # plot the open_loop (simulation with no assimilation)
        # plt.legend()
        # plt.show()

        # print('RMSE - Assimilated: ' + str(xss.rmse(q_assim.mean(dim='state').T,q_obs[0:q_assim.shape[1]].T).data))
        # print('RMSE - Open-Loop: ' + str(xss.rmse(model.q_sim[0:q_assim.shape[1],0],q_obs[0:q_assim.shape[1]].T).data))

        assert q_assim.shape[0] == n_members
예제 #26
0
    def test_tags(self):
        model = GR4JCN(tempfile.mkdtemp())

        tags = model.tags
        assert "run_name" in tags
예제 #27
0
    def test_simple(self):

        # get timeseries
        ts = get_local_testdata(
            "raven-gr4j-cemaneige/Salmon-River-Near-Prince-George_meteo_daily.nc"
        )

        # set number of members. Using 7 here to make it easier to find and debug.
        n_members = 7

        # Perturbation parameters for the assimilation, keyed by standard_name
        std = {
            "rainfall": 0.30,
            "prsn": 0.30,
            "tasmin": 2.0,
            "tasmax": 2.0,
            "water_volume_transport_in_river_channel": 0.15,
        }

        # Use the same random seed for both tasmin and tasmax
        rs = np.random.SeedSequence(None).generate_state(1)[0]
        seed = {"tasmin": rs, "tasmax": rs}

        # Perturbation distribution
        dists = {
            "pr": "gamma",
            "rainfall": "gamma",
            "prsn": "gamma",
            "water_volume_transport_in_river_channel": "rnorm",
        }

        qkey = "water_volume_transport_in_river_channel"
        if qkey not in std:
            raise ValueError("Assimilation requires perturbing the flow variable.")

        # Assimilation variables (from HRUStateVariable)
        assim_var = ("soil0", "soil1")

        # Assimilation periods
        assim_days = [10] + 4 * [3]

        # GR4JCN model instance
        model = GR4JCN()

        # set the start and end dates for the first assimilation period, warm-up
        start_date = dt.datetime(2000, 6, 1)
        end_date = start_date + dt.timedelta(days=sum(assim_days))

        # Set model options
        model.rvh.name = "Salmon"
        model.rvh.area = "4250.6"
        model.rvh.elevation = "843.0"
        model.rvh.latitude = 54.4848
        model.rvh.longitude = -123.3659

        model.rvp.params = model.params(
            0.1353389, -0.005067198, 576.8007, 6.986121, 1.102917, 0.9224778
        )  # SALMON

        # ==== Initialization (just to get reasonable states) ====
        # Set initialization run options
        model.rvi.run_name = "init"
        model.rvi.start_date = start_date
        # Richard: what is the end date policy for the init run ?
        model.rvi.end_date = start_date + dt.timedelta(days=assim_days[0])

        # Run the model
        model([ts])

        # Extract final model states
        hru_state, basin_state = model.get_final_state()
        xa = n_members * [getattr(hru_state, key) for key in assim_var]
        hru_states = n_members * [hru_state]
        basin_states = n_members * [basin_state]

        # === Create perturbed time series for full assimilation period ====
        perturbed = {}
        for key, s in std.items():
            nc = model.rvt.get(key)

            with xr.open_dataset(nc.path) as ds:
                da = ds.get(nc.var_name).sel(time=slice(start_date, end_date))

                perturbed[key] = perturbation(
                    da,
                    dists.get(key, "norm"),
                    std=s,
                    seed=seed.get(key, None),
                    member=n_members,
                )

                # Save flow for later
                if key == qkey:
                    q_obs = da

        # Write to disk
        p_fn = model.workdir / "perturbed_forcing.nc"
        perturbed = xr.Dataset(perturbed)
        perturbed.to_netcdf(p_fn, mode="w")

        # ==== Assimilation ====
        q_assim = []
        sd = start_date
        for i, ndays in enumerate(assim_days):

            dates = [sd + dt.timedelta(days=x) for x in range(ndays)]
            model.rvi.end_date = dates[-1]
            model.rvi.run_name = f"assim_{i}"

            # Perform the first assimilation step here
            [xa, model] = assimilate(
                model, p_fn, q_obs, assim_var, basin_states, hru_states, dates
            )

            # Save streamflow simulation
            q_assim.append(model.q_sim.isel(nbasins=0))

            # Update the start-time for the next loop
            sd += dt.timedelta(days=ndays)
            model.rvi.start_date = sd

            # Get new initial conditions and feed assimilated values
            hru_states, basin_states = model.get_final_state()
            hru_states = [
                replace(hru_states[i], **dict(zip(assim_var, xa[:, i])))
                for i in range(n_members)
            ]

        q_assim = xr.concat(q_assim, dim="time")

        # ==== Reference run ====
        model.rvi.run_name = "ref"
        model.rvi.start_date = start_date
        model.rvi.end_date = end_date
        model.rvc = RVC(soil0=None, soil1=15, basin_state=BasinIndexCommand())
        model([ts])

        # We can now plot everything!
        plt.plot(q_assim.T, "r", label="Assimilated")  # plot the assimilated flows
        plt.plot(q_obs.T, "b", label="Observed")  # plot the observed flows
        plt.plot(
            model.q_sim, "g", label="Simulated"
        )  # plot the open_loop (simulation with no assimilation)
        # plt.legend()
        # plt.show()

        assert q_assim.shape[0] == n_members
        assert q_assim.shape[1] == sum(assim_days)
예제 #28
0
    def test_simple(self):
        model = GR4JCN_OST()
        params = (0.529, -3.396, 407.29, 1.072, 16.9, 0.053)
        low = (0.01, -15.0, 10.0, 0.0, 1.0, 0.0)
        high = (2.5, 10.0, 700.0, 7.0, 30.0, 1.0)

        model(
            TS,
            start_date=dt.datetime(1954, 1, 1),
            duration=208,
            area=4250.6,
            elevation=843.0,
            latitude=54.4848,
            longitude=-123.3659,
            params=params,
            lowerBounds=low,
            upperBounds=high,
            algorithm="DDS",
            random_seed=0,
            max_iterations=10,
        )

        d = model.diagnostics

        np.testing.assert_almost_equal(d["DIAG_NASH_SUTCLIFFE"], 0.50717, 4)

        # Random number seed: 123
        # Budget:             10
        # Algorithm:          DDS
        # :StartDate          1954-01-01 00:00:00
        # :Duration           208
        opt_para = model.calibrated_params
        opt_func = model.obj_func

        np.testing.assert_almost_equal(
            opt_para,
            [2.424726, 3.758972, 204.3856, 5.866946, 16.60408, 0.3728098],
            4,
            err_msg="calibrated parameter set is not matching expected value",
        )
        np.testing.assert_almost_equal(
            opt_func,
            -0.50717,
            4,
            err_msg="calibrated NSE is not matching expected value",
        )

        # # Random number seed: 123
        # # Budget:             50
        # # Algorithm:          DDS
        # # :StartDate          1954-01-01 00:00:00
        # # :Duration           20819
        # np.testing.assert_almost_equal( opt_para, [0.3243268,3.034247,407.2890,2.722774,12.18124,0.9468769], 4,
        #                                 err_msg='calibrated parameter set is not matching expected value')
        # np.testing.assert_almost_equal( opt_func, -0.5779910, 4,
        #                                 err_msg='calibrated NSE is not matching expected value')
        gr4j = GR4JCN()
        gr4j(
            TS,
            start_date=dt.datetime(1954, 1, 1),
            duration=208,
            area=4250.6,
            elevation=843.0,
            latitude=54.4848,
            longitude=-123.3659,
            params=model.calibrated_params,
        )
        np.testing.assert_almost_equal(gr4j.diagnostics["DIAG_NASH_SUTCLIFFE"],
                                       d["DIAG_NASH_SUTCLIFFE"])
예제 #29
0
    def test_version(self):
        model = Raven()
        assert model.raven_version == "3.0.4"

        model = GR4JCN()
        assert model.raven_version == "3.0.4"
예제 #30
0
    def test_routing(self):
        """We need at least 2 subbasins to activate routing."""
        model = GR4JCN()

        ts_2d = get_local_testdata(
            "raven-gr4j-cemaneige/Salmon-River-Near-Prince-George_meteo_daily_2d.nc"
        )

        #########
        # R V I #
        #########

        model.config.rvi.start_date = dt.datetime(2000, 1, 1)
        model.config.rvi.end_date = dt.datetime(2002, 1, 1)
        model.config.rvi.run_name = "test_gr4jcn_routing"
        model.config.rvi.routing = "ROUTE_DIFFUSIVE_WAVE"

        #########
        # R V H #
        #########

        # Here we assume that we have two subbasins. The first one (subbasin_id=10)
        # has a lake (hru_id=2; area-100km2) and the rest is covered by land (hru_id=1;
        # area=4250.6km2). The second subbasin (subbasin_id=20) does not contain a
        # lake and is hence only land (hru_id=3; area=2000km2).
        #
        # Later the routing product will tell us which basin flows into which. Here
        # we assume that the first subbasin (subbasin_id=10) drains into the second
        # (subbasin_id=20). At the outlet of this second one we have an observation
        # station (see :ObservationData in RVT). We will compare these observations
        # with the simulated streamflow. That is the reason why "gauged=True" for
        # the second basin.

        # HRU IDs are 1 to 3
        model.config.rvh.hrus = (
            GR4JCN.LandHRU(hru_id=1, subbasin_id=10, **salmon_land_hru_1),
            GR4JCN.LakeHRU(hru_id=2, subbasin_id=10, **salmon_lake_hru_1),
            GR4JCN.LandHRU(hru_id=3, subbasin_id=20, **salmon_land_hru_2),
        )

        # Sub-basin IDs are 10 and 20 (not 1 and 2), to help disambiguate
        model.config.rvh.subbasins = (
            # gauged = False:
            # Usually this output would only be written for user's convenience.
            # There is usually no observation of streamflow available within
            # catchments; only at the outlet. That's most commonly the reason
            # why a catchment is defined as it is defined.
            Sub(
                name="upstream",
                subbasin_id=10,
                downstream_id=20,
                profile="chn_10",
                gauged=False,
            ),
            # gauged = True:
            # Since this is the outlet, this would usually be what we calibrate
            # against (i.e. we try to match this to Qobs).
            Sub(
                name="downstream",
                subbasin_id=20,
                downstream_id=-1,
                profile="chn_20",
                gauged=True,
            ),
        )

        model.config.rvh.land_subbasin_property_multiplier = (
            SBGroupPropertyMultiplierCommand("Land", "MANNINGS_N", 1.0))
        model.config.rvh.lake_subbasin_property_multiplier = (
            SBGroupPropertyMultiplierCommand("Lakes", "RESERVOIR_CREST_WIDTH",
                                             1.0))

        #########
        # R V T #
        #########

        gws = GridWeightsCommand(
            number_hrus=3,
            number_grid_cells=1,
            # Here we have a special case: station is 0 for every row because the example NC
            # has only one region/station (which is column 0)
            data=((1, 0, 1.0), (2, 0, 1.0), (3, 0, 1.0)),
        )
        # These will be shared (inline) to all the StationForcing commands in the RVT
        model.config.rvt.grid_weights = gws

        #########
        # R V P #
        #########

        model.config.rvp.params = model.Params(0.529, -3.396, 407.29, 1.072,
                                               16.9, 0.947)

        total_area_in_km2 = sum(hru.area for hru in model.config.rvh.hrus)
        total_area_in_m2 = total_area_in_km2 * 1000 * 1000
        model.config.rvp.avg_annual_runoff = get_average_annual_runoff(
            ts_2d, total_area_in_m2)

        np.testing.assert_almost_equal(model.config.rvp.avg_annual_runoff,
                                       139.5407534171111)

        # These channel profiles describe the geometry of the actual river crossection.
        # The eight points (x) to describe the following geometry are given in each
        # profile:
        #
        # ----x                                     x---
        #      \           FLOODPLAIN             /
        #       x----x                     x----x
        #             \                  /
        #               \   RIVERBED   /
        #                 x----------x
        #
        model.config.rvp.channel_profiles = [
            ChannelProfileCommand(
                name="chn_10",
                bed_slope=7.62066e-05,
                survey_points=[
                    (0, 463.647),
                    (16.0, 459.647),
                    (90.9828, 459.647),
                    (92.9828, 458.647),
                    (126.4742, 458.647),
                    (128.4742, 459.647),
                    (203.457, 459.647),
                    (219.457, 463.647),
                ],
                roughness_zones=[
                    (0, 0.0909167),
                    (90.9828, 0.035),
                    (128.4742, 0.0909167),
                ],
            ),
            ChannelProfileCommand(
                name="chn_20",
                bed_slope=9.95895e-05,
                survey_points=[
                    (0, 450.657),
                    (16.0, 446.657),
                    (85.0166, 446.657),
                    (87.0166, 445.657),
                    (117.5249, 445.657),
                    (119.5249, 446.657),
                    (188.54149999999998, 446.657),
                    (204.54149999999998, 450.657),
                ],
                roughness_zones=[
                    (0, 0.0915769),
                    (85.0166, 0.035),
                    (119.5249, 0.0915769),
                ],
            ),
        ]

        #############
        # Run model #
        #############

        model(ts_2d)

        ###########
        # Verify  #
        ###########

        hds = model.q_sim

        assert len(hds.nbasins) == 1  # number of "gauged" basins is 1

        # We only have one SB with gauged=True, so the output has a single column.
        # The number of time steps simulated between (2000, 1, 1) and
        # (2002, 1, 1) is 732.
        assert hds.shape == (732, 1)

        # Check simulated streamflow at first three timesteps and three simulated
        # timesteps in the middle of the simulation period.
        dates = (
            "2000-01-01",
            "2000-01-02",
            "2000-01-03",
            "2001-01-30",
            "2001-01-31",
            "2001-02-01",
        )

        target_q_sim = [
            0.0, 0.304073, 0.980807, 17.54049, 17.409493, 17.437954
        ]

        for t in range(6):
            np.testing.assert_almost_equal(hds.sel(nbasins=0, time=dates[t]),
                                           target_q_sim[t], 4)

        # For lumped GR4J model we have 1 subbasin and 1 HRU as well as no routing, no
        # channel profiles, and the area of the entire basin is 4250.6 [km2]. Comparison
        # of simulated and observed streamflow at outlet yielded:
        # np.testing.assert_almost_equal(d["DIAG_NASH_SUTCLIFFE"], -0.116971, 4)
        #
        # This is now a different value due to:
        # - basin we have here is larger (4250.6 [km2] + 100 [km2] + 2000.0 [km2])
        # - we do routing: so water from subbasin 1 needs some time to arrive at the
        #   outlet of subbasin 2
        d = model.diagnostics
        np.testing.assert_almost_equal(d["DIAG_NASH_SUTCLIFFE"], -0.0141168, 4)