Exemple #1
0
def test_solar(solar_resource):
    data = solar_resource.data
    for key in ('df', 'dn', 'wspd', 'tdry', 'year', 'month', 'day', 'hour',
                'minute', 'tz'):
        assert (key in data)
    model = pv.default("PVWattsNone")
    model.SolarResource.solar_resource_file = solar_resource.filename
    model.execute(0)
    assert (model.Outputs.annual_energy == approx(9275, 0.1))
    model = pv.default("PVWattsNone")
    model.SolarResource.solar_resource_data = solar_resource.data
    model.execute(1)
    assert (model.Outputs.annual_energy == approx(9275, 0.1))
Exemple #2
0
def test_reopt_sizing_pvwatts(solar_resource):
    round = 0

    tracker = SummaryTracker()
    while round < 25:   # multiple runs required to check for memory leaks
        round += 1

        sys = pv.default("PVWattsBatteryCommercial")
        sys.SolarResource.solar_resource_file = solar_resource
        batt = bt.from_existing(sys, "PVWattsBatteryCommercial")
        sys.SolarResource.solar_resource_data = dict({'lat': 3, 'lon': 3})
        batt.Battery.crit_load = [0] * 8760
        fin = ur.from_existing(sys, "PVWattsBatteryCommercial")

        post = sys.Reopt_size_battery_post()

    assert('Scenario' in post['reopt_post'])
    assert(post['reopt_post']['Scenario']['Site']['latitude'] == pytest.approx(3, 0.1))
    tracker_diff = tracker.diff()
    tracker.print_diff()
Exemple #3
0
def calculate_max_hybrid_aep(site_info: SiteInfo, num_turbines: int,
                             solar_capacity_kw: float) -> dict:
    """
    Calculates the max total pv and solar annual energy output by assuming no wake, gcr or flicker losses.
    All other factors and losses are not adjusted because they remain constant throughout the optimization
    :return: dictionary of "wind", "solar" and "total" max AEPs
    """
    upper_bounds = dict()

    # wind
    wind_model = windpower.default("WindPowerSingleOwner")
    wind_model.Resource.wind_resource_data = site_info.wind_resource.data

    wind_params_orig = wind_model.export()
    wind_model.Farm.wind_farm_xCoordinates = np.zeros(num_turbines)
    wind_model.Farm.wind_farm_yCoordinates = np.zeros(num_turbines)
    wind_model.Farm.system_capacity = num_turbines * max(
        wind_model.Turbine.wind_turbine_powercurve_powerout)
    wind_model.Farm.wind_farm_wake_model = 3  # constant wake loss model which we can set to 0
    wind_model.Losses.wake_int_loss = 0
    wind_model.execute(0)

    # solar
    solar_model = pvwatts.default("PVWattsSingleOwner")
    solar_model.SolarResource.solar_resource_data = site_info.solar_resource.data
    solar_model.SystemDesign.array_type = 2  # single-axis tracking

    solar_params_orig = solar_model.export()
    solar_model.SystemDesign.gcr = 0.01  # lowest possible gcr
    solar_model.SystemDesign.system_capacity = solar_capacity_kw
    solar_model.execute(0)

    upper_bounds['wind'] = wind_model.Outputs.annual_energy / 1000
    upper_bounds['solar'] = solar_model.Outputs.annual_energy / 1000
    upper_bounds['total'] = upper_bounds['wind'] + upper_bounds['solar']

    # restore original parameters
    wind_model.assign(wind_params_orig)
    solar_model.assign(solar_params_orig)

    return upper_bounds
Exemple #4
0
def test_solar():
    solar = str(
        Path(__file__).parent /
        "blythe_ca_33.617773_-114.588261_psmv3_60_tmy.csv")
    data = tools.SAM_CSV_to_solar_data(solar)
    assert (data['lat'] == 33.61)
    assert (data['lon'] == -114.58)
    assert (data['dn'][7] == 262)
    assert (data['df'][7] == 16)
    assert (data['gh'][7] == 27)
    assert (data['tdry'][7] == pytest.approx(8.96, 0.1))
    assert (data['tdew'][7] == pytest.approx(-0.03, 0.1))
    assert (data['rhum'][7] == pytest.approx(25.0, 0.1))
    assert (data['wdir'][7] == pytest.approx(351, 0.1))

    model = pv.default("PVwattsNone")
    model.SolarResource.solar_resource_data = data
    model.execute()
    aep = model.Outputs.annual_energy
    model.SolarResource.solar_resource_file = solar
    model.execute()
    assert (aep == pytest.approx(model.Outputs.annual_energy, 1))
Exemple #5
0
    def _setup_irradiance(self):
        """
        Compute solar azimuth and elevation degrees;
        Compute plane-of-array irradiance for a single-axis tracking PVwatts system
        :return:
        """
        pv_model = pv.default("PVWattsNone")
        pv_model.SystemDesign.array_type = 2
        pv_model.SystemDesign.gcr = .1
        if self.solar_resource_data is None:
            filename = str(self.lat) + "_" + str(self.lon) + "_psmv3_60_2012.csv"
            weather_path = Path(__file__).parent.parent.parent / "resource_files" / "solar" / filename
            if not weather_path.is_file():
                SolarResource(self.lat, self.lon, year=2012)
                if not weather_path.is_file():
                    raise ValueError("resource file does not exist")
            pv_model.SolarResource.solar_resource_file = str(weather_path)
        else:
            pv_model.SolarResource.solar_resource_data = self.solar_resource_data
        pv_model.execute(0)
        self.poa = np.array(pv_model.Outputs.poa)

        logger.info("get_irradiance success")
Exemple #6
0
    def __init__(self,
                 site: SiteInfo,
                 pv_config: dict,
                 detailed_not_simple: bool = False):
        """

        :param pv_config: dict, with keys ('system_capacity_kw', 'layout_params')
            where 'layout_params' is of the SolarGridParameters type
        :param detailed_not_simple:
            Detailed model uses Pvsamv1, simple uses PVWatts
        """
        if 'system_capacity_kw' not in pv_config.keys():
            raise ValueError

        self._detailed_not_simple: bool = detailed_not_simple

        if not detailed_not_simple:
            system_model = Pvwatts.default("PVWattsSingleOwner")
            financial_model = Singleowner.from_existing(system_model, "PVWattsSingleOwner")
        else:
            system_model = Pvsam.default("FlatPlatePVSingleOwner")
            financial_model = Singleowner.from_existing(system_model, "FlatPlatePVSingleOwner")

        super().__init__("SolarPlant", site, system_model, financial_model)

        self._system_model.SolarResource.solar_resource_data = self.site.solar_resource.data

        self.dc_degradation = [0]

        params: Optional[PVGridParameters] = None
        if 'layout_params' in pv_config.keys():
            params: PVGridParameters = pv_config['layout_params']
        self._layout = PVLayout(site, system_model, params)

        self._dispatch: PvDispatch = None

        self.system_capacity_kw: float = pv_config['system_capacity_kw']
    def _setup_simulation(self) -> None:
        """
        Wind simulation
            -> PySAM windpower model

        Solar simulation
            -> Surrogate model of PySAM Pvwatts model since the AEP scales linearly and independently
            w.r.t solar capacity and gcr
        """
        def run_wind_model(windmodel: windpower.Windpower):
            windmodel.Farm.system_capacity = \
                max(windmodel.Turbine.wind_turbine_powercurve_powerout) * len(windmodel.Farm.wind_farm_xCoordinates)
            windmodel.execute(0)
            return windmodel.Outputs.annual_energy

        def run_pv_model(pvmodel: pvwatts.Pvwattsv8):
            cap = pvmodel.SystemDesign.system_capacity
            gcr = pvmodel.SystemDesign.gcr
            est = cap * self._solar_size_aep_multiplier * self.solar_gcr_loss_multiplier(
                gcr)
            # pvmodel.execute()
            # rl = pvmodel.Outputs.annual_energy
            # err = (rl - est)/rl
            # if err > 0.05:
            #     print("High approx error found with {} kwh and {} gcr of {}".format(cap, gcr, err))
            return est

        # create wind model
        self._scenario = dict()
        wind_model = windpower.default("WindPowerSingleOwner")
        wind_model.Resource.wind_resource_data = self.site_info.wind_resource.data
        self.turb_diam = wind_model.Turbine.wind_turbine_rotor_diameter
        wind_model.Farm.wind_farm_wake_model = 2  # use eddy viscosity wake model

        self._scenario['Wind'] = (wind_model, run_wind_model)

        # create pv model
        solar_model = pvwatts.default("PVWattsSingleOwner")
        solar_model.SolarResource.solar_resource_data = self.site_info.solar_resource.data
        solar_model.SystemDesign.array_type = 2  # single-axis tracking
        solar_model.SystemDesign.tilt = 0

        # setup surrogate
        solar_model.execute(0)
        self._solar_size_aep_multiplier = solar_model.Outputs.annual_energy / solar_model.SystemDesign.system_capacity

        solar_model.SystemDesign.gcr = 0.01  # lowest possible gcr
        solar_model.SystemDesign.system_capacity = 1
        solar_model.execute(0)
        if solar_model.Outputs.annual_energy > 0:
            self._solar_gcr_loss_multiplier[
                'unit'] = solar_model.Outputs.annual_energy
        else:
            raise RuntimeError(
                "Solar GCR Loss Multiplier: Setup failed due to 0 for unit value"
            )

        self._scenario['Solar'] = (solar_model, run_pv_model)

        # estimate max AEP
        self.upper_bounds = calculate_max_hybrid_aep(self.site_info,
                                                     self.num_turbines,
                                                     self.solar_capacity_kw)

        logger.info(
            "Setup Wind and Solar models. Max AEP is {} for wind, {} solar, {} total"
            .format(self.upper_bounds['wind'], self.upper_bounds['solar'],
                    self.upper_bounds['total']))