Example #1
0
    def test_kling_gupta_and_nash_sutcliffe(self):
        """
        Test/verify exposure of the kling_gupta and nash_sutcliffe correlation functions

        """

        def np_nash_sutcliffe(o, p):
            return 1 - (np.sum((o - p) ** 2)) / (np.sum((o - np.mean(o)) ** 2))

        c = api.Calendar()
        t0 = c.time(2016, 1, 1)
        dt = api.deltahours(1)
        n = 240
        ta = api.Timeaxis2(t0, dt, n)
        from math import sin, pi
        rad_max = 10 * 2 * pi
        obs_values = api.DoubleVector.from_numpy(np.array([sin(i * rad_max / n) for i in range(n)]))
        mod_values = api.DoubleVector.from_numpy(np.array([0.1 + sin(pi / 10.0 + i * rad_max / n) for i in range(n)]))
        obs_ts = api.Timeseries(ta=ta, values=obs_values, point_fx=api.point_interpretation_policy.POINT_AVERAGE_VALUE)
        mod_ts = api.Timeseries(ta=ta, values=mod_values, point_fx=api.point_interpretation_policy.POINT_AVERAGE_VALUE)

        self.assertAlmostEqual(api.kling_gupta(obs_ts, obs_ts, ta, 1.0, 1.0, 1.0), 1.0, None, "1.0 for perfect match")
        self.assertAlmostEqual(api.nash_sutcliffe(obs_ts, obs_ts, ta), 1.0, None, "1.0 for perfect match")
        # verify some non trivial cases, and compare to numpy version of ns
        mod_inv = obs_ts * -1.0
        kge_inv = obs_ts.kling_gupta(mod_inv)  # also show how to use time-series.method itself to ease use
        ns_inv = obs_ts.nash_sutcliffe(mod_inv)  # similar for nash_sutcliffe, you can reach it directly from a ts
        ns_inv2 = np_nash_sutcliffe(obs_ts.values.to_numpy(), mod_inv.values.to_numpy())
        self.assertLessEqual(kge_inv, 1.0, "should be less than 1")
        self.assertLessEqual(ns_inv, 1.0, "should be less than 1")
        self.assertAlmostEqual(ns_inv, ns_inv2, 4, "should equal numpy calculated value")
        kge_obs_mod = api.kling_gupta(obs_ts, mod_ts, ta, 1.0, 1.0, 1.0)
        self.assertLessEqual(kge_obs_mod, 1.0)
        self.assertAlmostEqual(obs_ts.nash_sutcliffe( mod_ts), np_nash_sutcliffe(obs_ts.values.to_numpy(), mod_ts.values.to_numpy()))
Example #2
0
    def _create_forecast_set(self, n_fc, t0, dt, n_steps, dt_fc, fx):
        """

        Parameters
        ----------
        n_fc : int number of forecasts, e.g. 8
        t0 : utctime start of first forecast
        dt : utctimespan delta t for forecast-ts
        n_steps : number of steps in one forecast-ts
        dt_fc : utctimespan delta t between each forecast, like deltahours(6)
        fx : lambda time_axis:  a function returning a DoubleVector with values for the supplied time-axis

        Returns
        -------
        api.TsVector()

        """
        fc_set = api.TsVector()
        for i in range(n_fc):
            ta = api.Timeaxis2(t0 + i * dt_fc, dt, n_steps)
            ts = api.Timeseries(
                ta=ta,
                values=fx(ta),
                point_fx=api.point_interpretation_policy.POINT_AVERAGE_VALUE)
            fc_set.append(ts)
        return fc_set
Example #3
0
    def test_percentiles(self):
        c = api.Calendar()
        t0 = c.time(2016, 1, 1)
        dt = api.deltahours(1)
        n = 240
        ta = api.Timeaxis(t0, dt, n)
        timeseries = api.TsVector()

        for i in range(10):
            timeseries.append(
                api.Timeseries(ta=ta, fill_value=i, point_fx=api.point_interpretation_policy.POINT_AVERAGE_VALUE))

        wanted_percentiles = api.IntVector([0, 10, 50, -1, 70, 100])
        ta_day = api.Timeaxis(t0, dt * 24, n // 24)
        ta_day2 = api.Timeaxis2(t0, dt * 24, n // 24)
        percentiles = api.percentiles(timeseries, ta_day, wanted_percentiles)
        percentiles2 = timeseries.percentiles(ta_day2, wanted_percentiles)  # just to verify it works with alt. syntax

        self.assertEqual(len(percentiles2), len(percentiles))

        for i in range(len(ta_day)):
            self.assertAlmostEqual(0.0, percentiles[0].value(i), 3, "  0-percentile")
            self.assertAlmostEqual(0.9, percentiles[1].value(i), 3, " 10-percentile")
            self.assertAlmostEqual(4.5, percentiles[2].value(i), 3, " 50-percentile")
            self.assertAlmostEqual(4.5, percentiles[3].value(i), 3, "   -average")
            self.assertAlmostEqual(6.3, percentiles[4].value(i), 3, " 70-percentile")
            self.assertAlmostEqual(9.0, percentiles[5].value(i), 3, "100-percentile")
Example #4
0
    def _create_geo_ts_grid(self, nx, ny, dx, fx):
        """Create a geo_ts_grid of TemperatureSources,
           We just create a terrain model starting at 0 and increasing to max_elevation at max x and y.
           parameters
           ----------
           nx : int
            number of grid-cells in x direction
           ny : int
            number of grid-cells in y direction
           dx : float
             distance in meters for one of the sides in the grid
           fx : lambda z : 1.0
             a function that takes elevation z as input and generates a np.array len(self.ta) of float64 as time-series
           returns
           -------
           a TemperatureSourceVector() filled with geo-ts representing the grid.

        """
        arome_grid = api.TemperatureSourceVector()
        for i in range(nx):
            for j in range(ny):
                z = self.max_elevation * (i + j) / (nx + ny)
                ts = api.Timeseries(ta=self.ta,
                                    values=fx(z),
                                    point_fx=api.point_interpretation_policy.
                                    POINT_AVERAGE_VALUE)
                geo_ts = api.TemperatureSource(api.GeoPoint(i * dx, j * dx, z),
                                               ts)
                arome_grid.append(geo_ts)
        return arome_grid
Example #5
0
 def _make_fc_from_obs(self, obs_set, bias):
     fc_set = api.TemperatureSourceVector()
     bias_ts = api.Timeseries(self.ta, fill_value=bias)
     for obs in obs_set:
         geo_ts = api.TemperatureSource(obs.mid_point(), obs.ts + bias_ts)
         fc_set.append(geo_ts)
     return fc_set
Example #6
0
    def test_timeseries_vector(self):
        c = api.Calendar()
        t0 = api.utctime_now()
        dt = api.deltahours(1)
        n = 240
        ta = api.Timeaxis(t0, dt, n)

        a = api.Timeseries(ta=ta, fill_value=3.0, point_fx=api.point_interpretation_policy.POINT_AVERAGE_VALUE)
        b = api.Timeseries(ta=ta, fill_value=2.0, point_fx=api.point_interpretation_policy.POINT_AVERAGE_VALUE)

        v = api.TsVector()
        v.append(a)
        v.append(b)

        self.assertEqual(len(v), 2)
        self.assertAlmostEqual(v[0].value(0), 3.0, "expect first ts to be 3.0")
        aa = api.Timeseries(ta=a.time_axis, values=a.values,
                            point_fx=api.point_interpretation_policy.POINT_AVERAGE_VALUE)  # copy construct (really copy the values!)
        a.fill(1.0)
        self.assertAlmostEqual(v[0].value(0), 1.0, "expect first ts to be 1.0, because the vector keeps a reference ")
        self.assertAlmostEqual(aa.value(0), 3.0)
Example #7
0
 def _create_obs_set(self, geo_points):
     obs_set = api.TemperatureSourceVector()
     fx = lambda z: [15 for x in range(self.nt)]
     ts = api.Timeseries(
         ta=self.ta,
         values=fx(self.ta),
         point_fx=api.point_interpretation_policy.POINT_AVERAGE_VALUE)
     for gp in geo_points:
         # Add only one TS per GP, but should be several
         geo_ts = api.TemperatureSource(gp, ts)
         obs_set.append(geo_ts)
     return obs_set
Example #8
0
    def test_basic_timeseries_math_operations(self):
        """
        Test that timeseries functionality is exposed, and briefly verify correctness
        of operators (the  shyft core do the rest of the test job, not repeated here).
        """
        c = api.Calendar()
        t0 = api.utctime_now()
        dt = api.deltahours(1)
        n = 240
        ta = api.Timeaxis2(t0, dt, n)

        a = api.Timeseries(ta=ta, fill_value=3.0, point_fx=api.point_interpretation_policy.POINT_AVERAGE_VALUE)
        b = api.Timeseries(ta=ta, fill_value=1.0)
        b.fill(2.0)  # demo how to fill a point ts
        c = a + b * 3.0 - a / 2.0  # operator + * - /
        d = -a  # unary minus
        e = a.average(ta)  # average
        f = api.max(c, 300.0)
        g = api.min(c, -300.0)
        h = a.max(c, 300)
        k = a.min(c, -300)

        self.assertEqual(a.size(), n)
        self.assertEqual(b.size(), n)
        self.assertEqual(c.size(), n)
        self.assertAlmostEqual(c.value(0), 3.0 + 2.0 * 3.0 - 3.0 / 2.0)  # 7.5
        for i in range(n):
            self.assertAlmostEqual(c.value(i), a.value(i) + b.value(i) * 3.0 - a.value(i) / 2.0, delta=0.0001)
            self.assertAlmostEqual(d.value(i), - a.value(i), delta=0.0001)
            self.assertAlmostEqual(e.value(i), a.value(i), delta=0.00001)
            self.assertAlmostEqual(f.value(i), +300.0, delta=0.00001)
            self.assertAlmostEqual(h.value(i), +300.0, delta=0.00001)
            self.assertAlmostEqual(g.value(i), -300.0, delta=0.00001)
            self.assertAlmostEqual(k.value(i), -300.0, delta=0.00001)
        # now some more detailed tests for setting values
        b.set(0, 3.0)
        self.assertAlmostEqual(b.value(0), 3.0)
        #  3.0 + 3 * 3 - 3.0/2.0
        self.assertAlmostEqual(c.value(1), 7.5, delta=0.0001)  # 3 + 3*3  - 1.5 = 10.5
        self.assertAlmostEqual(c.value(0), 10.5, delta=0.0001)  # 3 + 3*3  - 1.5 = 10.5
Example #9
0
 def test_accumulate(self):
     c = api.Calendar()
     t0 = c.time(2016, 1, 1)
     dt = api.deltahours(1)
     n = 240
     ta = api.Timeaxis2(t0, dt, n)
     ts0 = api.Timeseries(ta=ta, fill_value=1.0, point_fx=api.point_interpretation_policy.POINT_AVERAGE_VALUE)
     ts1 = ts0.accumulate(ts0.get_time_axis())  # ok, maybe we should make method that does time-axis implicit ?
     ts1_values = ts1.values
     for i in range(n):
         expected_value = i * dt * 1.0
         self.assertAlmostEqual(expected_value, ts1.value(i), 3, "expect integral f(t)*dt")
         self.assertAlmostEqual(expected_value, ts1_values[i], 3, "expect value vector equal as well")
Example #10
0
 def _create_geo_precipitation_grid(self, nx, ny, dx, fx):
     arome_grid = api.PrecipitationSourceVector()
     for i in range(nx):
         for j in range(ny):
             z = self.max_elevation * (i + j) / (nx + ny)
             ts = api.Timeseries(ta=self.ta,
                                 values=fx(z),
                                 point_fx=api.point_interpretation_policy.
                                 POINT_AVERAGE_VALUE)
             geo_ts = api.PrecipitationSource(
                 api.GeoPoint(i * dx, j * dx, z), ts)
             arome_grid.append(geo_ts)
     return arome_grid
Example #11
0
    def test_time_shift(self):
        c = api.Calendar()
        t0 = c.time(2016, 1, 1)
        t1 = c.time(2017, 1, 1)
        dt = api.deltahours(1)
        n = 240
        ta = api.Timeaxis(t0, dt, n)
        ts0 = api.Timeseries(ta=ta, fill_value=3.0, point_fx=api.point_interpretation_policy.POINT_AVERAGE_VALUE)
        ts1 = api.time_shift(ts0, t1 - t0)
        ts2 = 2.0 * ts1.time_shift(t0 - t1)  # just to verify it still can take part in an expression

        for i in range(ts0.size()):
            self.assertAlmostEqual(ts0.value(i), ts1.value(i), 3, "expect values to be equal")
            self.assertAlmostEqual(ts0.value(i) * 2.0, ts2.value(i), 3, "expect values to be double value")
            self.assertEqual(ts0.time(i) + (t1 - t0), ts1.time(i), "expect time to be offset delta_t different")
            self.assertEqual(ts0.time(i), ts2.time(i), "expect time to be equal")
Example #12
0
    def test_bias_predictor(self):
        """
        Verify that if we feed forecast[n] and observation into the bias-predictor
        it will create the estimated bias offsets
        """
        f = api.KalmanFilter()
        bp = api.KalmanBiasPredictor(f)
        self.assertIsNotNone(bp)
        self.assertEqual(bp.filter.parameter.n_daily_observations, 8)

        n_fc = 8
        utc = api.Calendar()
        t0 = utc.time(2016, 1, 1)
        dt = api.deltahours(1)
        n_fc_steps = 36  # e.g. like arome 36 hours
        fc_dt = api.deltahours(6)
        fc_fx = lambda time_axis: self._create_fc_values(
            time_axis, 2.0)  # just return a constant 2.0 deg C for now
        fc_set = self._create_geo_forecast_set(n_fc, t0, dt, n_fc_steps, fc_dt,
                                               fc_fx)
        n_obs = 24
        obs_ta = api.Timeaxis2(t0, dt, n_obs)
        obs_ts = api.Timeseries(obs_ta, fill_value=0.0)
        kalman_dt = api.deltahours(
            3)  # suitable average for prediction temperature
        kalman_ta = api.Timeaxis2(t0, kalman_dt, 8)
        bp.update_with_forecast(
            fc_set, obs_ts, kalman_ta
        )  # here we feed in forecast-set and observation into kalman
        fc_setv = self._create_forecast_set(n_fc, t0, dt, n_fc_steps, fc_dt,
                                            fc_fx)
        bp.update_with_forecast(
            fc_setv, obs_ts,
            kalman_ta)  # also verify we can feed in a pure TsVector
        bias_pattern = bp.state.x  # the bp.state.x is now the best estimates fo the bias between fc and observation
        self.assertEqual(len(bias_pattern), 8)
        for i in range(len(bias_pattern)):
            self.assertLess(abs(bias_pattern[i] - 2.0),
                            0.2)  # bias should iterate to approx 2.0 degC now.
Example #13
0
    def test_partition_by(self):
        """
        verify/demo exposure of the .partition_by function that can
        be used to produce yearly percentiles statistics for long historical
        time-series

        """
        c = api.Calendar()
        t0 = c.time(1930, 9, 1)
        dt = api.deltahours(1)
        n = c.diff_units(t0, c.time(2016, 9, 1), dt)

        ta = api.Timeaxis2(t0, dt, n)
        pattern_values = api.DoubleVector.from_numpy(np.arange(len(ta))) # increasing values

        src_ts = api.Timeseries(ta=ta, values=pattern_values, point_fx=api.point_interpretation_policy.POINT_AVERAGE_VALUE)

        partition_t0 = c.time(2016, 9, 1)
        n_partitions = 80
        partition_interval = api.Calendar.YEAR
        # get back TsVector,
        # where all TsVector[i].index_of(partition_t0)
        # is equal to the index ix for which the TsVector[i].value(ix) correspond to start value of that particular partition.
        ts_partitions = src_ts.partition_by(c, t0, partition_interval, n_partitions, partition_t0)
        self.assertEqual(len(ts_partitions),n_partitions)
        ty = t0
        for ts in ts_partitions:
            ix = ts.index_of(partition_t0)
            vix = ts.value(ix)
            expected_value = c.diff_units(t0, ty, dt)
            self.assertEqual(vix, expected_value)
            ty = c.add(ty, partition_interval, 1)

        # Now finally, try percentiles on the partitions
        wanted_percentiles = [0, 10, 25, -1, 50, 75, 90, 100]
        ta_percentiles = api.Timeaxis2(partition_t0, api.deltahours(24), 365)
        percentiles = api.percentiles(ts_partitions,ta_percentiles,wanted_percentiles)
        self.assertEqual(len(percentiles), len(wanted_percentiles))
Example #14
0
    def test_can_run_bayesian_kriging_from_observation_sites_to_1km_grid(self):
        """
        Somewhat more complex test, first do kriging of 1 timeseries out to grid (expect same values flat)
        then do kriging of 3 time-series out to the grid (expect different values, no real verification here since this is done elsewhere

        """
        # arrange the test with a btk_parameter, a source grid and a destination grid
        btk_parameter = api.BTKParameter(temperature_gradient=-0.6,
                                         temperature_gradient_sd=0.25,
                                         sill=25.0,
                                         nugget=0.5,
                                         range=20000.0,
                                         zscale=20.0)
        fx = lambda z: api.DoubleVector.from_numpy(np.zeros(self.n))

        grid_1km_1 = self._create_geo_point_grid(self.mnx, self.mny,
                                                 self.dx_model)
        grid_1km_3 = self._create_geo_point_grid(self.mnx, self.mny,
                                                 self.dx_model)

        observation_sites = api.TemperatureSourceVector()
        ta_obs = api.Timeaxis(self.t, self.d * 3, int(self.n / 3))
        ta_grid = api.Timeaxis(self.t, self.d, self.n)

        ts_site_1 = api.Timeseries(
            ta_obs,
            values=api.DoubleVector.from_numpy(
                (20.0 - 0.6 * 5.0 / 100) + 3.0 * np.sin(
                    np.arange(start=0, stop=ta_obs.size(), step=1) * 2 *
                    np.pi / 8.0 - np.pi / 2.0)))
        ts_site_2 = api.Timeseries(
            ta_obs,
            values=api.DoubleVector.from_numpy(
                (20.0 - 0.6 * 500.0 / 100) + 3.0 * np.sin(
                    np.arange(start=0, stop=ta_obs.size(), step=1) * 2 *
                    np.pi / 8.0 - np.pi / 2.0)))
        ts_site_3 = api.Timeseries(
            ta_obs,
            values=api.DoubleVector.from_numpy(
                (20.0 - 0.6 * 1050.0 / 100) + 3.0 * np.sin(
                    np.arange(start=0, stop=ta_obs.size(), step=1) * 2 *
                    np.pi / 8.0 - np.pi / 2.0)))

        observation_sites.append(
            api.TemperatureSource(api.GeoPoint(50.0, 50.0, 5.0), ts_site_1))

        # act 1: just one time-series put into the system, should give same ts (true-averaged) in all the grid-1km_ts (which can be improved using std.gradient..)
        grid_1km_1ts = api.bayesian_kriging_temperature(
            observation_sites, grid_1km_1, ta_grid, btk_parameter)

        # assert 1:
        self.assertEqual(len(grid_1km_1ts), self.mnx * self.mny)
        expected_grid_1ts_values = ts_site_1.average(
            api.Timeaxis2(ta_grid)).values.to_numpy()

        for gts in grid_1km_1ts:
            self.assertEqual(gts.ts.size(), ta_grid.size())
            self.assertTrue(
                np.allclose(expected_grid_1ts_values,
                            gts.ts.values.to_numpy()))

        observation_sites.append(
            api.TemperatureSource(api.GeoPoint(9000.0, 500.0, 500), ts_site_2))
        observation_sites.append(
            api.TemperatureSource(api.GeoPoint(9000.0, 12000.0, 1050.0),
                                  ts_site_3))

        grid_1km_3ts = api.bayesian_kriging_temperature(
            observation_sites, grid_1km_3, ta_grid, btk_parameter)

        self.assertEqual(len(grid_1km_3ts), self.mnx * self.mny)

        for gts in grid_1km_3ts:
            self.assertEqual(gts.ts.size(), ta_grid.size())
            self.assertFalse(
                np.allclose(expected_grid_1ts_values,
                            gts.ts.values.to_numpy()))