Exemple #1
0
 def run(self, data, random_seed=None):
     ths = np.logspace(-5, -1, 31)
     ho_error = []
     for th in ths:
         bool_msk = detect_sun(data, th)
         measured = rise_set_rough(bool_msk)
         sunrises = measured["sunrises"]
         sunsets = measured["sunsets"]
         # np.random.seed(random_seed)
         use_set_sr = np.arange(len(sunrises))[~np.isnan(sunrises)]
         use_set_ss = np.arange(len(sunsets))[~np.isnan(sunsets)]
         if (len(use_set_sr) / len(sunrises) > 0.6
                 and len(use_set_ss) / len(sunsets) > 0.6):
             selected_th = th
             break
         else:
             selected_th = None
         #     np.random.shuffle(use_set_sr)
         #     np.random.shuffle(use_set_ss)
         #     split_at_sr = int(len(use_set_sr) * .8)     # 80-20 train test split
         #     split_at_ss = int(len(use_set_ss) * .8)
         #     train_sr = use_set_sr[:split_at_sr]
         #     train_ss = use_set_ss[:split_at_ss]
         #     test_sr = use_set_sr[split_at_sr:]
         #     test_ss = use_set_ss[split_at_ss:]
         #     train_msk_sr = np.zeros_like(sunrises, dtype=np.bool)
         #     train_msk_ss = np.zeros_like(sunsets, dtype=np.bool)
         #     train_msk_sr[train_sr] = True
         #     train_msk_ss[train_ss] = True
         #     test_msk_sr = np.zeros_like(sunrises, dtype=np.bool)
         #     test_msk_ss = np.zeros_like(sunsets, dtype=np.bool)
         #     test_msk_sr[test_sr] = True
         #     test_msk_ss[test_ss] = True
         #     sr_smoothed = local_quantile_regression_with_seasonal(sunrises,
         #                                                           train_msk_sr,
         #                                                           tau=0.05,
         #                                                           solver='MOSEK')
         #     ss_smoothed = local_quantile_regression_with_seasonal(sunsets,
         #                                                           train_msk_ss,
         #                                                           tau=0.95,
         #                                                           solver='MOSEK')
         #     r1 = (sunrises - sr_smoothed)[test_msk_sr]
         #     r2 = (sunsets - ss_smoothed)[test_msk_ss]
         #     ho_resid = np.r_[r1, r2]
         #     ho_error.append(np.sqrt(np.mean(ho_resid ** 2)))
         # else:
         #     ho_error.append(1e6)
         # selected_th = ths[np.argmin(ho_error)]
     bool_msk = detect_sun(data, selected_th)
     measured = rise_set_rough(bool_msk)
     smoothed = rise_set_smoothed(measured,
                                  sunrise_tau=0.05,
                                  sunset_tau=0.95)
     self.sunrise_estimates = smoothed["sunrises"]
     self.sunset_estimates = smoothed["sunsets"]
     self.sunrise_measurements = measured["sunrises"]
     self.sunset_measurements = measured["sunsets"]
     self.sunup_mask = bool_msk
     self.threshold = selected_th
Exemple #2
0
 def run(self, data, random_seed=None):
     ths = np.logspace(-5, -1, 31)
     ho_error = []
     for th in ths:
         bool_msk = detect_sun(data, th)
         measured = rise_set_rough(bool_msk)
         sunrises = measured["sunrises"]
         sunsets = measured["sunsets"]
         np.random.seed(random_seed)
         use_set_sr = np.arange(len(sunrises))[~np.isnan(sunrises)]
         use_set_ss = np.arange(len(sunsets))[~np.isnan(sunsets)]
         if (len(use_set_sr) / len(sunrises) > 0.6
                 and len(use_set_ss) / len(sunsets) > 0.6):
             np.random.shuffle(use_set_sr)
             np.random.shuffle(use_set_ss)
             # 80-20 train test split
             split_at_sr = int(len(use_set_sr) * 0.8)
             split_at_ss = int(len(use_set_ss) * 0.8)
             train_sr = use_set_sr[:split_at_sr]
             train_ss = use_set_ss[:split_at_ss]
             test_sr = use_set_sr[split_at_sr:]
             test_ss = use_set_ss[split_at_ss:]
             train_msk_sr = np.zeros_like(sunrises, dtype=np.bool)
             train_msk_ss = np.zeros_like(sunsets, dtype=np.bool)
             train_msk_sr[train_sr] = True
             train_msk_ss[train_ss] = True
             test_msk_sr = np.zeros_like(sunrises, dtype=np.bool)
             test_msk_ss = np.zeros_like(sunsets, dtype=np.bool)
             test_msk_sr[test_sr] = True
             test_msk_ss[test_ss] = True
             sr_smoothed = tl1_l2d2p365(sunrises,
                                        train_msk_sr,
                                        tau=0.05,
                                        solver="MOSEK")
             ss_smoothed = tl1_l2d2p365(sunsets,
                                        train_msk_ss,
                                        tau=0.95,
                                        solver="MOSEK")
             r1 = (sunrises - sr_smoothed)[test_msk_sr]
             r2 = (sunsets - ss_smoothed)[test_msk_ss]
             ho_resid = np.r_[r1, r2]
             ho_error.append(np.sqrt(np.mean(ho_resid**2)))
         else:
             ho_error.append(1e6)
     selected_th = ths[np.argmin(ho_error)]
     bool_msk = detect_sun(data, selected_th)
     measured = rise_set_rough(bool_msk)
     smoothed = rise_set_smoothed(measured,
                                  sunrise_tau=0.05,
                                  sunset_tau=0.95)
     self.sunrise_estimates = smoothed["sunrises"]
     self.sunset_estimates = smoothed["sunsets"]
     self.sunrise_measurements = measured["sunrises"]
     self.sunset_measurements = measured["sunsets"]
     self.sunup_mask = bool_msk
     self.threshold = selected_th
Exemple #3
0
def avg_sunrise_sunset(data_in, threshold=0.01):
    """Calculate the sunrise time and sunset time for each day, and use the
    average of these two values as an estimate for solar noon.

    :param data_in: PV power matrix as generated by `make_2d` from `solardatatools.data_transforms`
    :return: A 1-D array, containing the solar noon estimate for each day in the data set
    """
    bool_msk = detect_sun(data_in, threshold=threshold)
    measurements = rise_set_rough(bool_msk)
    return np.average(np.c_[measurements["sunrises"], measurements["sunsets"]], axis=1)
Exemple #4
0
    def calculate_times(self,
                        data,
                        threshold=None,
                        plot=False,
                        figsize=(12, 10),
                        groundtruth=None,
                        zoom_fit=False):
        # print('Calculating times')
        if threshold is None:
            if self.threshold is not None:
                threshold = self.threshold
            else:
                print('Please run optimizer or provide a threshold')
                return
        if groundtruth is not None:
            sr_true = groundtruth[0]
            ss_true = groundtruth[1]
        else:
            sr_true = None
            ss_true = None
        bool_msk = detect_sun(data, threshold)
        measured = rise_set_rough(bool_msk)
        smoothed = rise_set_smoothed(measured, sunrise_tau=.05, sunset_tau=.95)
        self.sunrise_estimates = smoothed['sunrises']
        self.sunset_estimates = smoothed['sunsets']
        self.sunrise_measurements = measured['sunrises']
        self.sunset_measurements = measured['sunsets']
        self.sunup_mask_measured = bool_msk
        data_sampling = int(24 * 60 / data.shape[0])
        num_days = data.shape[1]
        mat = np.tile(np.arange(0, 24, data_sampling / 60), (num_days, 1)).T
        sr_broadcast = np.tile(self.sunrise_estimates, (data.shape[0], 1))
        ss_broadcast = np.tile(self.sunset_estimates, (data.shape[0], 1))
        self.sunup_mask_estimated = np.logical_and(mat >= sr_broadcast,
                                                   mat < ss_broadcast)
        self.threshold = threshold

        if plot:
            fig, ax = plt.subplots(nrows=4, figsize=figsize)
            ylims = []
            ax[0].set_title('Sunrise Times')
            ax[0].plot(self.sunrise_estimates, ls='--', color='blue')
            ylims.append(ax[0].get_ylim())
            ax[0].plot(self.sunrise_measurements,
                       label='measured',
                       marker='.',
                       ls='none',
                       alpha=0.3,
                       color='green')
            ax[0].plot(self.sunrise_estimates,
                       label='estimated',
                       ls='--',
                       color='blue')
            if groundtruth is not None:
                ax[0].plot(sr_true, label='true', color='orange')
            ax[1].set_title('Sunset Times')
            ax[1].plot(self.sunset_estimates, ls='--', color='blue')
            ylims.append(ax[1].get_ylim())
            ax[1].plot(self.sunset_measurements,
                       label='measured',
                       marker='.',
                       ls='none',
                       alpha=0.3,
                       color='green')
            ax[1].plot(self.sunset_estimates,
                       label='estimated',
                       ls='--',
                       color='blue')
            if groundtruth is not None:
                ax[1].plot(ss_true, label='true', color='orange')
            ax[2].set_title('Solar Noon')
            ax[2].plot(np.average(
                [self.sunrise_estimates, self.sunset_estimates], axis=0),
                       ls='--',
                       color='blue')
            ylims.append(ax[2].get_ylim())
            ax[2].plot(np.average(
                [self.sunrise_measurements, self.sunset_measurements], axis=0),
                       label='measured',
                       marker='.',
                       ls='none',
                       alpha=0.3,
                       color='green')
            ax[2].plot(np.average(
                [self.sunrise_estimates, self.sunset_estimates], axis=0),
                       label='estimated',
                       ls='--',
                       color='blue')
            if groundtruth is not None:
                ax[2].plot(np.average([sr_true, ss_true], axis=0),
                           label='true',
                           color='orange')
            ax[3].set_title('Daylight Hours')
            ax[3].plot(self.sunset_estimates - self.sunrise_estimates,
                       ls='--',
                       color='blue')
            ylims.append(ax[3].get_ylim())
            ax[3].plot(self.sunset_measurements - self.sunrise_measurements,
                       label='measured',
                       marker='.',
                       ls='none',
                       alpha=0.3,
                       color='green')
            ax[3].plot(self.sunset_estimates - self.sunrise_estimates,
                       label='estimated',
                       ls='--',
                       color='blue')
            if groundtruth is not None:
                ax[3].plot(ss_true - sr_true, label='true', color='orange')
            for i in range(4):
                ax[i].legend()
            if zoom_fit:
                for ax_it, ylim_it in zip(ax, ylims):
                    ax_it.set_ylim(ylim_it)
            plt.tight_layout()
            return fig
        else:
            return
Exemple #5
0
    def run_optimizer(self,
                      data,
                      random_seed=None,
                      search_pts=51,
                      plot=False,
                      figsize=(8, 6),
                      groundtruth=None):
        if groundtruth is not None:
            sr_true = groundtruth[0]
            ss_true = groundtruth[1]
        else:
            sr_true = None
            ss_true = None
        ths = np.logspace(-5, -1, search_pts)
        ho_error = []
        full_error = []
        for th in ths:
            bool_msk = detect_sun(data, th)
            measured = rise_set_rough(bool_msk)
            sunrises = measured['sunrises']
            sunsets = measured['sunsets']
            np.random.seed(random_seed)
            use_set_sr = np.arange(len(sunrises))[~np.isnan(sunrises)]
            use_set_ss = np.arange(len(sunsets))[~np.isnan(sunsets)]
            if len(use_set_sr) / len(sunrises) > 0.6 and len(use_set_ss) / len(
                    sunsets) > 0.6:
                run_ho_errors = []
                num_trials = 1  # if > 1, average over multiple random selections
                for run in range(num_trials):
                    np.random.shuffle(use_set_sr)
                    np.random.shuffle(use_set_ss)
                    split_at_sr = int(len(use_set_sr) *
                                      .8)  # 80-20 train test split
                    split_at_ss = int(len(use_set_ss) * .8)
                    train_sr = use_set_sr[:split_at_sr]
                    train_ss = use_set_ss[:split_at_ss]
                    test_sr = use_set_sr[split_at_sr:]
                    test_ss = use_set_ss[split_at_ss:]
                    train_msk_sr = np.zeros_like(sunrises, dtype=np.bool)
                    train_msk_ss = np.zeros_like(sunsets, dtype=np.bool)
                    train_msk_sr[train_sr] = True
                    train_msk_ss[train_ss] = True
                    test_msk_sr = np.zeros_like(sunrises, dtype=np.bool)
                    test_msk_ss = np.zeros_like(sunsets, dtype=np.bool)
                    test_msk_sr[test_sr] = True
                    test_msk_ss[test_ss] = True
                    sr_smoothed = local_quantile_regression_with_seasonal(
                        sunrises, train_msk_sr, tau=0.05, solver='MOSEK')
                    ss_smoothed = local_quantile_regression_with_seasonal(
                        sunsets, train_msk_ss, tau=0.95, solver='MOSEK')
                    r1 = (sunrises - sr_smoothed)[test_msk_sr]
                    r2 = (sunsets - ss_smoothed)[test_msk_ss]
                    ho_resid = np.r_[r1, r2]
                    #### TESTING
                    # print(th)
                    # plt.plot(ho_resid)
                    # plt.show()
                    #####

                    ### 7/30/20:
                    # Some sites can have "consistent" fit (low holdout error)
                    # that is not the correct estimate. We impose the restriction
                    # that the range of sunrise times and sunset times must be
                    # greater than 15 minutes. Any solution that is less than
                    # that must be non-physical. (See: PVO ID# 30121)
                    cond1 = np.max(sr_smoothed) - np.min(sr_smoothed) > 0.25
                    cond2 = np.max(ss_smoothed) - np.min(ss_smoothed) > 0.25
                    if cond1 and cond2:
                        ### L1-loss instead of L2
                        # L1-loss is better proxy for goodness of fit when using
                        # quantile loss function
                        ###
                        run_ho_errors.append(np.mean(np.abs(ho_resid)))
                    else:
                        run_ho_errors.append(1e2)
                ho_error.append(np.average(run_ho_errors))
                if groundtruth is not None:
                    full_fit = rise_set_smoothed(measured,
                                                 sunrise_tau=0.05,
                                                 sunset_tau=0.95)
                    sr_full = full_fit['sunrises']
                    ss_full = full_fit['sunsets']
                    e1 = (sr_true - sr_full)
                    e2 = (ss_true - ss_full)
                    e_both = np.r_[e1, e2]
                    full_error.append(np.sqrt(np.mean(e_both**2)))
            else:
                ho_error.append(1e2)
                full_error.append(1e2)
        ho_error = np.array(ho_error)
        min_val = np.min(ho_error)
        slct_vals = ho_error < 1.1 * min_val  # everything within 10% of min val
        selected_th = np.min(ths[slct_vals])
        bool_msk = detect_sun(data, selected_th)
        measured = rise_set_rough(bool_msk)
        smoothed = rise_set_smoothed(measured, sunrise_tau=.05, sunset_tau=.95)
        self.sunrise_estimates = smoothed['sunrises']
        self.sunset_estimates = smoothed['sunsets']
        self.sunrise_measurements = measured['sunrises']
        self.sunset_measurements = measured['sunsets']
        self.sunup_mask_measured = bool_msk
        data_sampling = int(24 * 60 / data.shape[0])
        num_days = data.shape[1]
        mat = np.tile(np.arange(0, 24, data_sampling / 60), (num_days, 1)).T
        sr_broadcast = np.tile(self.sunrise_estimates, (data.shape[0], 1))
        ss_broadcast = np.tile(self.sunset_estimates, (data.shape[0], 1))
        self.sunup_mask_estimated = np.logical_and(mat >= sr_broadcast,
                                                   mat < ss_broadcast)
        self.threshold = selected_th
        if groundtruth is not None:
            sr_residual = sr_true - self.sunrise_estimates
            ss_residual = ss_true - self.sunset_estimates
            total_rmse = np.sqrt(np.mean(np.r_[sr_residual, ss_residual]**2))
            self.total_rmse = total_rmse
        else:
            self.total_rmse = None

        if plot:
            fig = plt.figure(figsize=figsize)
            plt.plot(ths, ho_error, marker='.', color='blue', label='HO error')
            plt.yscale('log')
            plt.xscale('log')

            plt.plot(ths[slct_vals],
                     ho_error[slct_vals],
                     marker='.',
                     ls='none',
                     color='red')
            plt.axvline(selected_th,
                        color='blue',
                        ls='--',
                        label='optimized parameter')
            if groundtruth is not None:
                best_th = ths[np.argmin(full_error)]
                plt.plot(ths,
                         full_error,
                         marker='.',
                         color='orange',
                         label='true error')
                plt.axvline(best_th,
                            color='orange',
                            ls='--',
                            label='best parameter')
            plt.legend()
            return fig
        else:
            return
Exemple #6
0
    def calculate_times(
            self,
            data,
            threshold=None,
            plot=False,
            figsize=(12, 10),
            groundtruth=None,
            zoom_fit=False,
            solver=None,
    ):
        # print('Calculating times')
        if threshold is None:
            if self.threshold is not None:
                threshold = self.threshold
            else:
                print("Please run optimizer or provide a threshold")
                return
        if groundtruth is not None:
            sr_true = groundtruth[0]
            ss_true = groundtruth[1]
        else:
            sr_true = None
            ss_true = None
        bool_msk = detect_sun(data, threshold)
        measured = rise_set_rough(bool_msk)
        smoothed = rise_set_smoothed(measured,
                                     sunrise_tau=0.05,
                                     sunset_tau=0.95,
                                     solver=solver)
        self.sunrise_estimates = smoothed["sunrises"]
        self.sunset_estimates = smoothed["sunsets"]
        self.sunrise_measurements = measured["sunrises"]
        self.sunset_measurements = measured["sunsets"]
        self.sunup_mask_measured = bool_msk
        data_sampling = int(24 * 60 / data.shape[0])
        num_days = data.shape[1]
        mat = np.tile(np.arange(0, 24, data_sampling / 60), (num_days, 1)).T
        sr_broadcast = np.tile(self.sunrise_estimates, (data.shape[0], 1))
        ss_broadcast = np.tile(self.sunset_estimates, (data.shape[0], 1))
        self.sunup_mask_estimated = np.logical_and(mat >= sr_broadcast,
                                                   mat < ss_broadcast)
        self.threshold = threshold

        if plot:
            fig, ax = plt.subplots(nrows=4, figsize=figsize, sharex=True)
            ylims = []
            ax[0].set_title("Sunrise Times")
            ax[0].plot(self.sunrise_estimates, ls="--", color="blue")
            ylims.append(ax[0].get_ylim())
            ax[0].plot(
                self.sunrise_measurements,
                label="measured",
                marker=".",
                ls="none",
                alpha=0.3,
                color="green",
            )
            ax[0].plot(self.sunrise_estimates,
                       label="estimated",
                       ls="--",
                       color="blue")
            if groundtruth is not None:
                ax[0].plot(sr_true, label="true", color="orange")
            ax[1].set_title("Sunset Times")
            ax[1].plot(self.sunset_estimates, ls="--", color="blue")
            ylims.append(ax[1].get_ylim())
            ax[1].plot(
                self.sunset_measurements,
                label="measured",
                marker=".",
                ls="none",
                alpha=0.3,
                color="green",
            )
            ax[1].plot(self.sunset_estimates,
                       label="estimated",
                       ls="--",
                       color="blue")
            if groundtruth is not None:
                ax[1].plot(ss_true, label="true", color="orange")
            ax[2].set_title("Solar Noon")
            ax[2].plot(
                np.average([self.sunrise_estimates, self.sunset_estimates],
                           axis=0),
                ls="--",
                color="blue",
            )
            ylims.append(ax[2].get_ylim())
            ax[2].plot(
                np.average(
                    [self.sunrise_measurements, self.sunset_measurements],
                    axis=0),
                label="measured",
                marker=".",
                ls="none",
                alpha=0.3,
                color="green",
            )
            ax[2].plot(
                np.average([self.sunrise_estimates, self.sunset_estimates],
                           axis=0),
                label="estimated",
                ls="--",
                color="blue",
            )
            if groundtruth is not None:
                ax[2].plot(np.average([sr_true, ss_true], axis=0),
                           label="true",
                           color="orange")
            ax[3].set_title("Daylight Hours")
            ax[3].plot(self.sunset_estimates - self.sunrise_estimates,
                       ls="--",
                       color="blue")
            ylims.append(ax[3].get_ylim())
            ax[3].plot(
                self.sunset_measurements - self.sunrise_measurements,
                label="measured",
                marker=".",
                ls="none",
                alpha=0.3,
                color="green",
            )
            ax[3].plot(
                self.sunset_estimates - self.sunrise_estimates,
                label="estimated",
                ls="--",
                color="blue",
            )
            if groundtruth is not None:
                ax[3].plot(ss_true - sr_true, label="true", color="orange")
            for i in range(4):
                ax[i].legend(loc=1)
            if zoom_fit:
                for ax_it, ylim_it in zip(ax, ylims):
                    ax_it.set_ylim(ylim_it)
            # plt.tight_layout()
            return fig
        else:
            return