def test_flexible_time_steps(self):
        """
        - Validation to ensure that upon entering time_steps_per_hour=1 or 4, the results of the analysis
        are as expected (keeping pv and storage off to test wind module's performance)
        - the output csv files dimensions (8760, 35040 etc) must also match time_steps_per_hour given as input
        :return:
        """
        # results for time_steps_per_hour = 1
        d1 = json.load(open(os.path.join("reo", "tests", "outputs_test_flexible_time_steps_one_per_hour.json"), "r"))
        c1 = nested_to_flat(d1)

        # results for time_steps_per_hour = 4
        response2 = self.get_response(data=fts_post_2)
        self.assertHttpCreated(response2)
        r2 = json.loads(response2.content)
        run_uuid2 = r2.get('run_uuid')
        d2 = ModelManager.make_response(run_uuid=run_uuid2)
        c2 = nested_to_flat(d2['outputs'])

        # Seems reasonable that the exact resiliency average will be different due to a great granularity of survival
        # information in a quarter-hourly simulation vs hourly.
        del c1['avoided_outage_costs_us_dollars']
        del c2['avoided_outage_costs_us_dollars']

        try:
            check_common_outputs(self, c1, c2)
        except:
            print("Run {} expected outputs may have changed.".format(run_uuid2))
            print("Error message with ts=1: {}".format(d1['messages']))
            print("Error message with ts=4: {}".format(d2['messages']))
            raise
Example #2
0
    def test_flexible_time_steps(self):
        """
        - Validation to ensure that upon entering time_steps_per_hour = 1 or 4, the results of the analysis
        are as expected (evaluating with only existing PV to keep test fast)
        - the output dimensions (8760, 35040 etc) must also match time_steps_per_hour given as input
        """
        # results for time_steps_per_hour = 4
        response = self.get_response(data=fts_post_2)
        self.assertHttpCreated(response)
        r2 = json.loads(response.content)
        run_uuid2 = r2.get('run_uuid')
        d2 = ModelManager.make_response(run_uuid=run_uuid2)
        c2 = nested_to_flat(d2['outputs'])

        self.assertEqual(len(c2["year_one_grid_to_load_series"]), 35040)

        # results for time_steps_per_hour = 1
        fts_post_2["Scenario"]["time_steps_per_hour"] = 1
        fts_post_2["Scenario"]["Site"]["LoadProfile"]["loads_kw"] = [50] * 8760
        fts_post_2["Scenario"]["Site"]["LoadProfile"][
            "outage_start_time_step"] = 100
        fts_post_2["Scenario"]["Site"]["LoadProfile"][
            "outage_end_time_step"] = 124
        response = self.get_response(data=fts_post_2)
        self.assertHttpCreated(response)
        r = json.loads(response.content)
        run_uuid = r.get('run_uuid')
        d = ModelManager.make_response(run_uuid=run_uuid)
        c1 = nested_to_flat(d['outputs'])

        # Seems reasonable that the exact resiliency average will be different due to a great granularity of survival
        # information in a quarter-hourly simulation vs hourly.
        del c1['avoided_outage_costs_us_dollars']
        del c2['avoided_outage_costs_us_dollars']
        check_common_outputs(self, c1, c2)
Example #3
0
    def test_soc_incentive(self):
        """
        Test scenario with
        - fixed PV of 100 kW
        - fixed battery of 20kW, 20kWh
        - no other techs
        Toggle on and off the SOC incentive in the objective function and compare average SOC and LCC's.
        """
        nested_data = json.load(open(self.test_post, 'rb'))
        nested_data['Scenario']['add_soc_incentive'] = True
        resp = self.get_response(data=nested_data)
        self.assertHttpCreated(resp)
        r = json.loads(resp.content)
        run_uuid = r.get('run_uuid')
        d = ModelManager.make_response(run_uuid=run_uuid)
        c = nested_to_flat(d['outputs'])
        c['average_soc'] = (sum(d['outputs']['Scenario']['Site']['Storage']['year_one_soc_series_pct']) /
                            len(d['outputs']['Scenario']['Site']['Storage']['year_one_soc_series_pct']))

        print(c['lcc'])
        print(c['average_soc'])

        d_expected = dict()
        d_expected['lcc'] = 350416.0
        d_expected['average_soc'] = 0.7271688516340313

        # try:
        #     check_common_outputs(self, c, d_expected)
        # except:
        #     print("Run {} expected outputs may have changed.".format(run_uuid))
        #     print("Error message: {}".format(d['messages']))
        #     raise

        nested_data['Scenario']['add_soc_incentive'] = False
        resp = self.get_response(data=nested_data)
        self.assertHttpCreated(resp)
        r = json.loads(resp.content)
        run_uuid = r.get('run_uuid')
        d = ModelManager.make_response(run_uuid=run_uuid)
        c = nested_to_flat(d['outputs'])
        c['average_soc'] = (sum(d['outputs']['Scenario']['Site']['Storage']['year_one_soc_series_pct']) /
                            len(d['outputs']['Scenario']['Site']['Storage']['year_one_soc_series_pct']))

        d_expected['lcc'] = 350416.0
        d_expected['average_soc'] = 0.6069393099644554
        print(c['lcc'])
        print(c['average_soc'])

        try:
            check_common_outputs(self, c, d_expected)
        except:
            print("Run {} expected outputs may have changed.".format(run_uuid))
            print("Error message: {}".format(d['messages']))
            raise
    def test_generator_sizing_with_existing_pv(self):
        """
        Test scenario with
        - no existing diesel generator
        - existing PV considered
        - new PV and Wind disabled
        - Unlimited max storage
        - generator doesn't sell energy to grid
        - generator is only allowed to operate during outage hours
        """
        nested_data = json.load(open(self.test_post, 'rb'))
        nested_data['Scenario']['Site']['LoadProfile']['outage_is_major_event'] = True
        nested_data['Scenario']['Site']['PV']['max_kw'] = 0
        nested_data['Scenario']['Site']['Generator']['existing_kw'] = 0
        nested_data['Scenario']['Site']['PV']['existing_kw'] = 100
        resp = self.get_response(data=nested_data)
        self.assertHttpCreated(resp)
        r = json.loads(resp.content)
        run_uuid = r.get('run_uuid')
        d = ModelManager.make_response(run_uuid=run_uuid)
        c = nested_to_flat(d['outputs'])

        d_expected = dict()
        d_expected['lcc'] = 244743.0
        d_expected['npv'] = -3959.0
        d_expected['pv_kw'] = 100.0
        d_expected['batt_kw'] = 0.0
        d_expected['batt_kwh'] = 0.0
        d_expected['gen_kw'] = 5.85713
        d_expected['fuel_used_gal'] = 0.79
        d_expected['avoided_outage_costs_us_dollars'] = 2982.63
        d_expected['microgrid_upgrade_cost_us_dollars'] = 1054.2
        d_expected['gen_total_variable_om_cost_us_dollars'] = 1.0
        d_expected['existing_pv_om_cost_us_dollars'] = 11507.0
        d_expected['net_capital_costs_plus_om'] = 15443.0

        try:
            check_common_outputs(self, c, d_expected)
        except:
            print("Run {} expected outputs may have changed.".format(run_uuid))
            print("Error message: {}".format(d['messages']))
            raise

        critical_load = d['outputs']['Scenario']['Site']['LoadProfile']['critical_load_series_kw']
        generator_to_load = d['outputs']['Scenario']['Site']['Generator']['year_one_to_load_series_kw']
        storage_to_load = d['outputs']['Scenario']['Site']['Storage']['year_one_to_load_series_kw']
        pv_to_load = d['outputs']['Scenario']['Site']['PV']['year_one_to_load_series_kw']
        outage_start = d['inputs']['Scenario']['Site']['LoadProfile']['outage_start_hour']
        outage_end = d['inputs']['Scenario']['Site']['LoadProfile']['outage_end_hour']

        list_to_load = [generator_to_load, storage_to_load, pv_to_load]
        tech_to_load = self.outage_tech_to_load(list_to_load, outage_start, outage_end)
        for x, y in zip(critical_load[outage_start:outage_end], tech_to_load):
            self.assertAlmostEquals(x, y, places=3)
    def test_curtailment(self):
        """
        Validation run for wind scenario with updated WindToolkit data
        Note no tax, no ITC, no MACRS.
        :return:
        """
        post_path = os.path.join('reo', 'tests', 'posts',
                                 'test_curtailment_POST.json')
        with open(post_path, 'r') as fp:
            test_post = json.load(fp)
        d_expected = dict()
        d_expected['lcc'] = 5908025
        d_expected['npv'] = -4961719
        d_expected['average_annual_energy_curtailed_pv'] = 1091859
        d_expected['average_annual_energy_curtailed_wind'] = 2841781
        d_expected['total_pv_export'] = 0
        d_expected['total_wind_export'] = 0
        resp = self.get_response(data=test_post)
        self.assertHttpCreated(resp)
        r = json.loads(resp.content)
        run_uuid = r.get('run_uuid')
        d = ModelManager.make_response(run_uuid=run_uuid)

        err_messages = d['messages'].get('error') or []
        if 'Wind Dataset Timed Out' in err_messages:
            print("Wind Dataset Timed Out")
        else:
            c = nested_to_flat(d['outputs'])
            print(d['outputs']['Scenario']['Site'].keys())
            c['average_annual_energy_curtailed_pv'] = sum(
                d['outputs']['Scenario']['Site']['PV']
                ['year_one_curtailed_production_series_kw'])
            c['average_annual_energy_curtailed_wind'] = sum(
                d['outputs']['Scenario']['Site']['Wind']
                ['year_one_curtailed_production_series_kw'])
            c['total_pv_export'] = sum(d['outputs']['Scenario']['Site']['PV']
                                       ['year_one_to_grid_series_kw'])
            c['total_wind_export'] = sum(
                d['outputs']['Scenario']['Site']['Wind']
                ['year_one_to_grid_series_kw'])
            try:
                check_common_outputs(self, c, d_expected)
            except:
                print("Run {} expected outputs may have changed.".format(
                    run_uuid))
                print("Error message: {}".format(d['messages'].get('error')))
                raise
    def test_complex_incentives(self):
        """
        Tests scenario where: PV has ITC, federal, state, local rebate with maxes, MACRS, bonus, Battery has ITC,
        rebate, MACRS, bonus.
        :return: None
        """
        data = {
        "Scenario": {"webtool_uuid": None, "description": "", "timeout_seconds": 295, "Site": {
            "roof_squarefeet": 5000.0, "longitude": -118.1164613, "address": "", "latitude": 34.5794343,
            "time_steps_per_hour": 1, "user_uuid": None,
            "PV": {"pbi_years": 1.0, "macrs_bonus_pct": 0.4, "max_kw": 1000000000.0, "pbi_max_us_dollars": 1000000000.0, "radius": 0.0, "state_ibi_pct": 0.2, "utility_rebate_max_us_dollars": 10000000000.0, "installed_cost_us_dollars_per_kw": 2000.0, "utility_ibi_max_us_dollars": 10000.0, "tilt": 34.5794343, "federal_rebate_us_dollars_per_kw": 100.0, "gcr": 0.4, "pbi_system_max_kw": 10.0, "utility_ibi_pct": 0.1, "state_ibi_max_us_dollars": 10000.0, "state_rebate_us_dollars_per_kw": 200.0, "macrs_option_years": 5, "state_rebate_max_us_dollars": 10000000000.0, "dc_ac_ratio": 1.1, "federal_itc_pct": 0.3, "pbi_us_dollars_per_kwh": 0.0, "module_type": 1, "array_type": 1, "existing_kw": 0.0, "om_cost_us_dollars_per_kw": 16.0, "utility_rebate_us_dollars_per_kw": 50.0, "min_kw": 0.0, "losses": 0.14, "macrs_itc_reduction": 0.5, "degradation_pct": 0.005, "inv_eff": 0.96, "azimuth": 180.0},
            "Generator": {"pbi_years": 0.0, "macrs_bonus_pct": 0.0, "om_cost_us_dollars_per_kwh": 0.01, "max_kw": 100000000.0, "pbi_max_us_dollars": 0.0, "state_ibi_pct": 0.0, "fuel_intercept_gal_per_hr": 0.0125, "generator_only_runs_during_grid_outage": True, "state_rebate_us_dollars_per_kw": 0.0, "installed_cost_us_dollars_per_kw": 600.0, "utility_ibi_max_us_dollars": 0.0, "fuel_avail_gal": 1000000000.0, "min_turn_down_pct": 0.0, "pbi_system_max_kw": 0.0, "utility_ibi_pct": 0.0, "state_ibi_max_us_dollars": 0.0, "diesel_fuel_cost_us_dollars_per_gallon": 3.0, "fuel_slope_gal_per_kwh": 0.068, "utility_rebate_max_us_dollars": 0.0, "macrs_option_years": 0, "state_rebate_max_us_dollars": 0.0, "federal_itc_pct": 0.0, "existing_kw": 0.0, "pbi_us_dollars_per_kwh": 0.0, "om_cost_us_dollars_per_kw": 10.0, "utility_rebate_us_dollars_per_kw": 0.0, "min_kw": 0.0, "macrs_itc_reduction": 0.0, "federal_rebate_us_dollars_per_kw": 0.0, "generator_sells_energy_back_to_grid": False},
            "LoadProfile": {"critical_loads_kw_is_net": False, "critical_load_pct": 0.5, "loads_kw_is_net": True, "loads_kw": [], "outage_end_hour": None, "monthly_totals_kwh": [], "year": 2017, "outage_start_hour": None, "outage_is_major_event": True, "critical_loads_kw": [], "doe_reference_name": "RetailStore", "annual_kwh": 10000000.0},
            "Storage": {"max_kwh": 1000000.0, "rectifier_efficiency_pct": 0.96, "total_itc_pct": 0.0, "min_kw": 0.0, "max_kw": 1000000.0, "replace_cost_us_dollars_per_kw": 460.0, "replace_cost_us_dollars_per_kwh": 230.0, "min_kwh": 0.0, "installed_cost_us_dollars_per_kw": 1000.0, "total_rebate_us_dollars_per_kw": 100, "installed_cost_us_dollars_per_kwh": 500.0, "inverter_efficiency_pct": 0.96, "macrs_itc_reduction": 0.5, "canGridCharge": True, "macrs_bonus_pct": 0.4, "battery_replacement_year": 10, "macrs_option_years": 5, "internal_efficiency_pct": 0.975, "soc_min_pct": 0.2, "soc_init_pct": 0.5, "inverter_replacement_year": 10}, "land_acres": 1.0, "ElectricTariff": {"add_blended_rates_to_urdb_rate": False, "wholesale_rate_us_dollars_per_kwh": 0.0, "net_metering_limit_kw": 0.0, "interconnection_limit_kw": 100000000.0, "blended_monthly_demand_charges_us_dollars_per_kw": [], "urdb_utility_name": "SouthernCaliforniaEdisonCo", "urdb_label": "", "wholesale_rate_above_site_load_us_dollars_per_kwh": 0.0, "urdb_rate_name": "TimeofUse,GeneralService,DemandMetered,OptionB:GS-2TOUB,SinglePhase", "urdb_response": {"sector": "Commercial", "peakkwcapacitymax": 200, "utility": "SouthernCaliforniaEdisonCo", "peakkwcapacityhistory": 12, "energyratestructure": [[{"rate": 0.0712, "unit": "kWh"}], [{"rate": 0.09368, "unit": "kWh"}], [{"rate": 0.066, "unit": "kWh"}], [{"rate": 0.08888, "unit": "kWh"}], [{"rate": 0.1355, "unit": "kWh"}]], "energyweekendschedule": [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], "demandweekendschedule": [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], "demandrateunit": "kW", "flatdemandstructure": [[{"rate": 13.2}]], "startdate": 1433116800, "phasewiring": "SinglePhase", "eiaid": 17609, "label": "55fc81d7682bea28da64f9ae", "flatdemandunit": "kW", "source": "http://www.sce.com/NR/sc3/tm2/pdf/ce30-12.pdf", "voltagecategory": "Primary", "revisions": [1433408708, 1433409358, 1433516188, 1441198316, 1441199318, 1441199417, 1441199824, 1441199996, 1454521683], "demandweekdayschedule": [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], "voltageminimum": 2000, "description": "-Energytieredcharge=generationcharge+deliverycharge\\r\\n\\r\\n-Timeofdaydemandcharges(generation-based)aretobeaddedtothemonthlydemandcharge(Deliverybased).", "energyattrs": [{"VoltageDiscount(2KV-<50KV)": "$-0.00106/Kwh"}, {"VoltageDiscount(>50KV<220KV)": "$-0.00238/Kwh"}, {"VoltageDiscountat220KV": "$-0.0024/Kwh"}, {"CaliforniaClimatecredit": "$-0.00669/kwh"}], "energyweekdayschedule": [[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2], [2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2], [2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2], [2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2], [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0]], "flatdemandmonths": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "approved": True, "fixedmonthlycharge": 259.2, "enddate": 1451520000, "name": "TimeofUse,GeneralService,DemandMetered,OptionB:GS-2TOUB,SinglePhase", "country": "USA", "uri": "http://en.openei.org/apps/IURDB/rate/view/55fc81d7682bea28da64f9ae", "voltagemaximum": 50000, "peakkwcapacitymin": 20, "demandattrs": [{"FaciltiesVoltageDiscount(2KV-<50KV)": "$-0.18/KW"}, {"FaciltiesVoltageDiscount>50kV-<220kV": "$-5.78/KW"}, {"FaciltiesVoltageDiscount>220kV": "$-9.96/KW"}, {"TimeVoltageDiscount(2KV-<50KV)": "$-0.70/KW"}, {"TimeVoltageDiscount>50kV-<220kV": "$-1.93/KW"}, {"TimeVoltageDiscount>220kV": "$-1.95/KW"}], "demandratestructure": [[{"rate": 0}], [{"rate": 5.3}], [{"rate": 18.11}]]}, "blended_annual_demand_charges_us_dollars_per_kw": 0.0, "blended_annual_rates_us_dollars_per_kwh": 0.0, "blended_monthly_rates_us_dollars_per_kwh": []},
            "Financial": {"escalation_pct": 0.026, "offtaker_discount_pct": 0.081, "value_of_lost_load_us_dollars_per_kwh": 100.0, "analysis_years": 20, "microgrid_upgrade_cost_pct": 0.3, "offtaker_tax_pct": 0.4, "om_cost_escalation_pct": 0.025},
            "Wind": {"max_kw": 0.0}
        }}}
        resp = self.get_response(data=data)
        self.assertHttpCreated(resp)
        r = json.loads(resp.content)
        run_uuid = r.get('run_uuid')
        d = ModelManager.make_response(run_uuid=run_uuid)
        c = nested_to_flat(d['outputs'])

        d_expected = dict()
        d_expected['lcc'] = 10972574
        d_expected['npv'] = 11257165 - d_expected['lcc']
        d_expected['pv_kw'] = 216.667
        d_expected['batt_kw'] = 29.416
        d_expected['batt_kwh'] = 38.8
        d_expected['year_one_utility_kwh'] = 9612970

        try:
            check_common_outputs(self, c, d_expected)
        except:
            print("Run {} expected outputs may have changed.".format(run_uuid))
            print("Error message: {}".format(d['messages'].get('error')))
            raise
Example #7
0
    def test_wind(self):
        """
        Validation run for wind scenario with updated WindToolkit data
        Note no tax, no ITC, no MACRS.
        :return:
        """
        wind_post = {"Scenario": {"Site": {
            "LoadProfile": {
                "annual_kwh": 10000000,
                "doe_reference_name": "MediumOffice"
            },
            "Storage": {
                "max_kwh": 0,
                "max_kw": 0
            },
            "latitude": 39.91065, "longitude": -105.2348,
            "PV": {
                "max_kw": 0
            },
            "Wind": {
                "installed_cost_us_dollars_per_kw": 1874,
                "om_cost_us_dollars_per_kw": 35,
                "macrs_bonus_pct": 0.0,
                "max_kw": 10000,
                "federal_itc_pct": 0,
                "macrs_option_years": 0,
                "size_class": "large"
            },
            "Financial": {
                "om_cost_escalation_pct": 0.001,
                "escalation_pct": 0.006,
                "offtaker_discount_pct": 0.07,
                "analysis_years": 25,
                "offtaker_tax_pct": 0.0
            },
            "ElectricTariff": {
                "blended_monthly_demand_charges_us_dollars_per_kw": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                                                                     0.0, 0.0],
                "net_metering_limit_kw": 1e6,
                "blended_monthly_rates_us_dollars_per_kwh": [0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2]
            }
        }}}
        d_expected = dict()
        d_expected['lcc'] = 8551172
        d_expected['npv'] = 16159608
        d_expected['wind_kw'] = 3735
        d_expected['average_annual_energy_exported_wind'] = 5844282
        d_expected['net_capital_costs_plus_om'] = 8537480
        resp = self.get_response(data=wind_post)
        self.assertHttpCreated(resp)
        r = json.loads(resp.content)
        run_uuid = r.get('run_uuid')
        d = ModelManager.make_response(run_uuid=run_uuid)

        err_messages = d['messages'].get('error') or []
        if 'Wind Dataset Timed Out' in err_messages:
            print("Wind Dataset Timed Out")
        else:
            c = nested_to_flat(d['outputs'])
            try:
                check_common_outputs(self, c, d_expected)
            except:
                print("Run {} expected outputs may have changed.".format(run_uuid))
                print("Error message: {}".format(d['messages']))
                raise
Example #8
0
    def test_critical_load_bau_can_sustain_outage(self):
        """
        Test scenario with
        - outage_start_hour: 16
        - outage_end_hour: 20
        - existing diesel generator 20 kW
        - existing PV 3 kW
        - available fuel 50 gallons
        """
        test_post = os.path.join('reo', 'tests', 'posts',
                                 'critical_load_bau_can_sustain_outage.json')
        nested_data = json.load(open(test_post, 'rb'))

        resp = self.get_response(data=nested_data)
        self.assertHttpCreated(resp)
        r = json.loads(resp.content)
        run_uuid = r.get('run_uuid')
        d = ModelManager.make_response(run_uuid=run_uuid)
        c = nested_to_flat(d['outputs'])
        load_bau = d['outputs']['Scenario']['Site']['LoadProfile'][
            'year_one_electric_load_series_kw']

        # check first 100 hours
        c['load_bau'] = load_bau[:100]
        c['status'] = d['outputs']['Scenario']['status']
        c['resilience_check_flag'] = d['outputs']['Scenario']['Site'][
            'LoadProfile']['resilience_check_flag']
        c['sustain_hours'] = d['outputs']['Scenario']['Site']['LoadProfile'][
            'sustain_hours']

        load_bau_expected = [
            19.5635, 18.9651, 20.3557, 19.0925, 20.2735, 19.0383, 20.3317,
            13.6065, 9.20435, 7.3294, 7.14286, 5.62887, 5.20491, 4.40816,
            4.21774, 4.21774, 2.13005, 6.20206, 8.43682, 8.63245, 17.7916,
            17.9585, 18.6981, 18.7381, 19.5483, 19.2944, 20.0656, 19.5757,
            20.2466, 19.8914, 24.4053, 32.5911, 39.7653, 47.1892, 47.1892,
            47.1892, 42.9714, 47.1892, 47.1892, 47.1892, 47.1892, 21.2452,
            16.5046, 17.0414, 17.6651, 17.7595, 18.298, 18.2451, 18.8185,
            18.7475, 19.4191, 19.1492, 19.881, 19.5412, 24.4053, 32.5911,
            39.7653, 47.1892, 47.1892, 47.1892, 42.9714, 47.1892, 47.1892,
            47.1892, 47.1892, 21.4645, 16.1414, 16.5509, 16.7304, 17.308,
            17.3808, 17.858, 17.8368, 18.1468, 17.8958, 18.1091, 17.7883,
            18.1495, 24.4053, 32.5911, 39.7653, 47.1892, 47.1892, 47.1892,
            42.9714, 47.1892, 47.1892, 47.1892, 47.1892, 20.9702, 15.1757,
            15.3862, 15.8559, 15.9813, 16.7046, 16.5464, 17.2186, 16.8851,
            17.7358, 17.2112
        ]

        d_expected = dict()
        d_expected['load_bau'] = load_bau_expected
        d_expected['status'] = 'optimal'
        d_expected['total_energy_cost_bau'] = 54201.86
        d_expected['year_one_energy_cost_bau'] = 7472.12
        d_expected['resilience_check_flag'] = True
        d_expected['sustain_hours'] = 4

        try:
            check_common_outputs(self, c, d_expected)
        except:
            print("Run {} expected outputs may have changed.".format(run_uuid))
            print("Error message: {}".format(d['messages']))
            raise