Exemplo n.º 1
0
    def __init__(self,
                 site: SiteInfo,
                 trough_config: dict):
        """
        Parabolic trough concentrating solar power class based on SSC’s Parabolic trough (physical model)

        :param site: Power source site information
        :param trough_config: CSP configuration with the following keys:

            #. ``cycle_capacity_kw``: float, Power cycle  design turbine gross output [kWe]
            #. ``solar_multiple``: float, Solar multiple [-]
            #. ``tes_hours``: float, Full load hours of thermal energy storage [hrs]
        """
        financial_model = Singleowner.default('PhysicalTroughSingleOwner')

        # set-up param file paths
        # TODO: Site should have dispatch factors consistent across all models
        self.param_files = {'tech_model_params_path': 'tech_model_defaults.json',
                            'cf_params_path': 'construction_financing_defaults.json',
                            'wlim_series_path': 'wlim_series.csv'}
        rel_path_to_param_files = os.path.join('pySSC_daotk', 'trough_data')
        self.param_file_paths(rel_path_to_param_files)

        super().__init__("TroughPlant", 'trough_physical', site, financial_model, trough_config)

        self._dispatch: TroughDispatch = None
Exemplo n.º 2
0
 def default():
     """Get the default PySAM object"""
     pv = DefaultPvwattsv5.default()
     obj = PySamSingleOwner.default('PVWattsSingleOwner')
     obj.SystemOutput.gen = pv.Outputs.ac
     obj.execute()
     return obj
Exemplo n.º 3
0
def test_ReOPT():

    lat = 39.7555
    lon = -105.2211

    # get resource and create model
    site = SiteInfo(flatirons_site)

    load = [1000 * (sin(x) + pi) for x in range(0, 8760)]
    urdb_label = "5ca4d1175457a39b23b3d45e"  # https://openei.org/apps/IURDB/rate/view/5ca3d45ab718b30e03405898

    solar_model = PVPlant(site, {'system_capacity_kw': 20000})
    wind_model = WindPlant(site, {
        'num_turbines': 10,
        "turbine_rating_kw": 2000
    })
    wind_model._system_model.Resource.wind_resource_filename = os.path.join(
        "data", "39.7555_-105.2211_windtoolkit_2012_60min_60m.srw")
    fin_model = so.default("GenericSystemSingleOwner")

    fileout = os.path.join(filepath, "REoptResultsNoExportAboveLoad.json")

    reopt = REopt(lat=lat,
                  lon=lon,
                  load_profile=load,
                  urdb_label=urdb_label,
                  solar_model=solar_model,
                  wind_model=wind_model,
                  fin_model=fin_model,
                  interconnection_limit_kw=20000,
                  fileout=fileout)
    reopt.set_rate_path(os.path.join(filepath, 'data'))

    reopt_site = reopt.post['Scenario']['Site']
    pv = reopt_site['PV']
    assert (pv['dc_ac_ratio'] == pytest.approx(1.3, 0.01))
    wind = reopt_site['Wind']
    assert (wind['pbi_us_dollars_per_kwh'] == pytest.approx(0.015))

    results = reopt.get_reopt_results()
    assert (isinstance(results, dict))
    print(results["outputs"]["Scenario"]["Site"]["Wind"]
          ['year_one_to_grid_series_kw'])
    if 'error' in results['outputs']['Scenario']["status"]:
        if 'error' in results["messages"].keys():
            if 'Optimization exceeded timeout' in results["messages"]['error']:
                assert True
            else:
                print(results["messages"]['error'])
        elif 'warning' in results["messages"].keys():
            print(results["messages"]['warnings'])
            assert True
    else:
        assert (results["outputs"]["Scenario"]["Site"]["Wind"]["size_kw"] >= 0)

    os.remove(fileout)
Exemplo n.º 4
0
    def __init__(self, site: SiteInfo, tower_config: dict):
        """
        Tower concentrating solar power class based on SSC’s MSPT (molten salt power tower) model

        :param site: Power source site information
        :param tower_config: CSP configuration with the following keys:

            #. ``cycle_capacity_kw``: float, Power cycle  design turbine gross output [kWe]
            #. ``solar_multiple``: float, Solar multiple [-]
            #. ``tes_hours``: float, Full load hours of thermal energy storage [hrs]
            #. ``optimize_field_before_sim``: (optional, default = True) bool, If True, SolarPilot's field and tower
               height optimization will before system simulation, o.w., SolarPilot will just generate field based on
               inputs.
            #. ``scale_input_params``: (optional, default = True) bool, If True, HOPP will run
               :py:func:`hybrid.tower_source.scale_params` before system simulation.
        """
        financial_model = Singleowner.default('MSPTSingleOwner')

        # set-up param file paths
        self.param_files = {
            'tech_model_params_path': 'tech_model_defaults.json',
            'cf_params_path': 'construction_financing_defaults.json',
            'wlim_series_path': 'wlim_series.csv',
            'helio_positions_path': 'helio_positions.csv'
        }
        rel_path_to_param_files = os.path.join('pySSC_daotk', 'tower_data')
        self.param_file_paths(rel_path_to_param_files)

        super().__init__("TowerPlant", 'tcsmolten_salt', site, financial_model,
                         tower_config)

        self.optimize_field_before_sim = True
        if 'optimize_field_before_sim' in tower_config:
            self.optimize_field_before_sim = tower_config[
                'optimize_field_before_sim']

        # (optionally) adjust ssc input parameters based on tower capacity
        self.is_scale_params = False
        if 'scale_input_params' in tower_config and tower_config[
                'scale_input_params']:
            self.is_scale_params = True
            # Parameters to be scaled before receiver size optimization
            self.scale_params(params_names=[
                'helio_size', 'helio_parasitics', 'tank_heaters', 'tank_height'
            ])

        self._dispatch: TowerDispatch = None
Exemplo n.º 5
0
def test_ReOPT():

    lat = 39.7555
    lon = -105.2211

    # get resource and create model
    site = SiteInfo(flatirons_site)

    load = [1000*(sin(x) + pi)for x in range(0, 8760)]
    urdb_label = "5ca4d1175457a39b23b3d45e" # https://openei.org/apps/IURDB/rate/view/5ca3d45ab718b30e03405898


    solar_model = SolarPlant(site, 20000)
    wind_model = WindPlant(site, 20000)
    wind_model.system_model.Resource.wind_resource_filename = os.path.join(
        "data", "39.7555_-105.2211_windtoolkit_2012_60min_60m.srw")
    fin_model = so.default("GenericSystemSingleOwner")

    reopt = REopt(lat=lat,
                  lon=lon,
                  load_profile=load,
                  urdb_label=urdb_label,
                  solar_model=solar_model,
                  wind_model=wind_model,
                  fin_model=fin_model,
                  interconnection_limit_kw=20000,
                  fileout=os.path.join(filepath, "data", "REoptResultsNoExportAboveLoad.json"))
    reopt.set_rate_path(os.path.join(filepath, 'data'))

    reopt_site = reopt.post['Scenario']['Site']
    pv = reopt_site['PV']
    assert(pv['dc_ac_ratio'] == pytest.approx(1.2, 0.01))
    wind = reopt_site['Wind']
    assert(wind['pbi_us_dollars_per_kwh'] == pytest.approx(0.022))

    results = reopt.get_reopt_results(force_download=True)
    assert(isinstance(results, dict))
    print(results["outputs"]["Scenario"]["Site"]["Wind"]['year_one_to_grid_series_kw'])
    assert (results["outputs"]["Scenario"]["Site"]["Wind"]["size_kw"] == pytest.approx(20000, 1))
    assert(results["outputs"]["Scenario"]["Site"]["Financial"]["lcc_us_dollars"] == pytest.approx(17008573.0, 1))
    assert(results["outputs"]["Scenario"]["Site"]["Financial"]["lcc_bau_us_dollars"] == pytest.approx(15511546.0, 1))
    assert(results["outputs"]["Scenario"]["Site"]["ElectricTariff"]["year_one_export_benefit_us_dollars"] == pytest.approx(-15158711.0, 1))
Exemplo n.º 6
0
def gcr_func(x, y):
    # set up base
    a = Pvsamv1.default("FlatPlatePVSingleowner")
    a.SolarResource.solar_resource_file = "../sam/deploy/solar_resource/tucson_az_32.116521_-110.933042_psmv3_60_tmy.csv"

    b = Singleowner.default("FlatPlatePVSingleowner")

    # set up shading
    a.Shading.subarray1_shade_mode = 1
    a.Layout.subarray1_nmodx = 12
    a.Layout.subarray1_nmody = 2

    a.SystemDesign.subarray1_gcr = float(x)
    land_area = a.CECPerformanceModelWithModuleDatabase.cec_area * (
        a.SystemDesign.subarray1_nstrings *
        a.SystemDesign.subarray1_modules_per_string) / x * 0.0002471

    a.execute()
    # total_installed_cost = total_direct_cost + permitting_total + engr_total + grid_total + landprep_total + sales_tax_total + land_total
    b.SystemCosts.total_installed_cost += y * land_area * 1000
    b.SystemOutput.gen = a.Outputs.gen
    b.execute()
    return b.Outputs.analysis_period_irr
Exemplo n.º 7
0
    def __init__(
            self,
            site: SiteInfo,
            farm_config: dict,
            rating_range_kw: tuple = (1000, 3000),
    ):
        """
        Set up a WindPlant

        :param farm_config: dict, with keys ('num_turbines', 'turbine_rating_kw', 'rotor_diameter', 'hub_height', 'layout_mode', 'layout_params')
            where layout_mode can be selected from the following:
            - 'boundarygrid': regular grid with boundary turbines, requires WindBoundaryGridParameters as 'params'
            - 'grid': regular grid with dx, dy distance, 0 angle; does not require 'params'

        :param rating_range_kw:
            allowable kw range of turbines, default is 1000 - 3000 kW
        """
        self._rating_range_kw = rating_range_kw

        if 'model_name' in farm_config.keys():
            if farm_config['model_name'] == 'floris':
                print('FLORIS is the system model...')
                system_model = Floris(farm_config,
                                      site,
                                      timestep=farm_config['timestep'])
                financial_model = Singleowner.default("WindPowerSingleOwner")
            else:
                raise NotImplementedError
        else:
            system_model = Windpower.default("WindPowerSingleOwner")
            financial_model = Singleowner.from_existing(
                system_model, "WindPowerSingleOwner")

        super().__init__("WindPlant", site, system_model, financial_model)
        self._system_model.value("wind_resource_data",
                                 self.site.wind_resource.data)

        if 'layout_mode' not in farm_config.keys():
            layout_mode = 'grid'
        else:
            layout_mode = farm_config['layout_mode']

        params: Optional[WindBoundaryGridParameters] = None
        if layout_mode == 'boundarygrid':
            if 'layout_params' not in farm_config.keys():
                raise ValueError(
                    "Parameters of WindBoundaryGridParameters required for boundarygrid layout mode"
                )
            else:
                params: WindBoundaryGridParameters = farm_config[
                    'layout_params']

        self._layout = WindLayout(site, system_model, layout_mode, params)

        self._dispatch: WindDispatch = None

        if 'turbine_rating_kw' not in farm_config.keys():
            raise ValueError("Turbine rating required for WindPlant")

        if 'num_turbines' not in farm_config.keys():
            raise ValueError("Num Turbines required for WindPlant")

        self.turb_rating = farm_config['turbine_rating_kw']
        self.num_turbines = farm_config['num_turbines']
        if 'hub_height' in farm_config.keys():
            self._system_model.Turbine.wind_turbine_hub_ht = farm_config[
                'hub_height']
        if 'rotor_diameter' in farm_config.keys():
            self.rotor_diameter = farm_config['rotor_diameter']
Exemplo n.º 8
0
def run_reopt(site, scenario, load, interconnection_limit_kw, critical_load_factor, useful_life,
              battery_can_grid_charge,
              storage_used, run_reopt_flag):

    # kw_continuous = forced_system_size  # 5 MW continuous load - equivalent to 909kg H2 per hr at 55 kWh/kg electrical intensity

    urdb_label = "5ca4d1175457a39b23b3d45e"  # https://openei.org/apps/IURDB/rate/view/5ca3d45ab718b30e03405898
    pv_config = {'system_capacity_kw': 20000}
    solar_model = PVPlant(site, pv_config)
    # ('num_turbines', 'turbine_rating_kw', 'rotor_diameter', 'hub_height', 'layout_mode', 'layout_params')
    wind_config = {'num_turbines': np.floor(scenario['Wind Size MW'] / scenario['Turbine Rating']),
                       'rotor_dimeter': scenario['Rotor Diameter'], 'hub_height': scenario['Tower Height'],
                   'turbine_rating_kw': scenario['Turbine Rating']}

    wind_model = WindPlant(site, wind_config)
    fin_model = so.default("GenericSystemSingleOwner")
    filepath = os.path.dirname(os.path.abspath(__file__))
    fileout = 'reopt_result_test_intergration.json'
    # site = SiteInfo(sample_site, hub_height=tower_height)
    count = 1
    reopt = REopt(lat=scenario['Lat'],
                  lon=scenario['Long'],
                  load_profile=load,
                  urdb_label=urdb_label,
                  solar_model=solar_model,
                  wind_model=wind_model,
                  fin_model=fin_model,
                  interconnection_limit_kw=interconnection_limit_kw,
                  off_grid=True,
                  fileout=fileout)

    reopt.set_rate_path(os.path.join(filepath, '../data'))

    reopt.post['Scenario']['Site']['Wind']['installed_cost_us_dollars_per_kw'] = scenario['Wind Cost KW']  # ATB
    reopt.post['Scenario']['Site']['PV']['installed_cost_us_dollars_per_kw'] = scenario['Solar Cost KW']
    reopt.post['Scenario']['Site']['Storage'] = {'min_kw': 0.0, 'max_kw': 0.99e9, 'min_kwh': 0.0,
                                                 'max_kwh': 0.99e9,
                                                 'internal_efficiency_pct': 0.975, 'inverter_efficiency_pct': 0.96,
                                                 'rectifier_efficiency_pct': 0.96, 'soc_min_pct': 0.2,
                                                 'soc_init_pct': 0.5,
                                                 'canGridCharge': battery_can_grid_charge,
                                                 'installed_cost_us_dollars_per_kw': scenario['Storage Cost KW'],
                                                 'installed_cost_us_dollars_per_kwh': scenario['Storage Cost KWh'],
                                                 'replace_cost_us_dollars_per_kw': scenario['Storage Cost KW'],
                                                 'replace_cost_us_dollars_per_kwh': scenario['Storage Cost KWh'],
                                                 'inverter_replacement_year': 10,
                                                 'battery_replacement_year': 10, 'macrs_option_years': 7,
                                                 'macrs_bonus_pct': 1.0, 'macrs_itc_reduction': 0.5,
                                                 'total_itc_pct': 0.0,
                                                 'total_rebate_us_dollars_per_kw': 0,
                                                 'total_rebate_us_dollars_per_kwh': 0}

    reopt.post['Scenario']['Site']['Financial']['analysis_years'] = useful_life
    if not storage_used:
        reopt.post['Scenario']['Site']['Storage']['max_kw'] = 0
    if scenario['PTC Available']:
        reopt.post['Scenario']['Site']['Wind']['pbi_us_dollars_per_kwh'] = 0.022
    else:
        reopt.post['Scenario']['Site']['Wind']['pbi_us_dollars_per_kwh'] = 0.0
    if scenario['ITC Available']:
        reopt.post['Scenario']['Site']['PV']['federal_itc_pct'] = 0.26
    else:
        reopt.post['Scenario']['Site']['PV']['federal_itc_pct'] = 0.0

    # reopt.post['Scenario']['Site']['LoadProfile']['doe_reference_name'] = "FlatLoad"
    # reopt.post['Scenario']['Site']['LoadProfile']['annual_kwh'] = load #8760 * kw_continuous
    reopt.post['Scenario']['Site']['LoadProfile']['loads_kw'] = load
    reopt.post['Scenario']['Site']['LoadProfile']['critical_load_pct'] = critical_load_factor

    off_grid = False
    reopt.post['Scenario']['optimality_tolerance_techs'] = 0.05

    if off_grid == True:
        # reopt.post['Scenario']['Site'].pop('Wind')
        # reopt.post['Scenario']['Site']['Wind']['min_kw'] = 10000
        dictforstuff = {"off_grid_flag": True}
        reopt.post['Scenario'].update(dictforstuff)
        reopt.post['Scenario']['optimality_tolerance_techs'] = 0.05
        reopt.post['Scenario']["timeout_seconds"] = 3600
        # reopt.post['Scenario']['Site']['LoadProfile'].pop('annual kwh')
        reopt.post['Scenario']['Site'].pop('ElectricTariff')
        reopt.post['Scenario']['Site']['LoadProfile']['critical_load_pct'] = 1.0
        f = open('massproducer_offgrid (1).json')
        data_for_post = json.load(f)
        reopt.post['Scenario']['Site']['Financial'] = data_for_post['Scenario']['Site']['Financial']
    else:
        reopt.post['Scenario']['Site']['ElectricTariff']['wholesale_rate_us_dollars_per_kwh'] = 0.01
        reopt.post['Scenario']['Site']['ElectricTariff']['wholesale_rate_above_site_load_us_dollars_per_kwh'] = 0.01
        reopt.post['Scenario']['Site']['LoadProfile']['outage_start_hour'] = 10
        reopt.post['Scenario']['Site']['LoadProfile']['outage_end_hour'] = 11

    from pathlib import Path
    post_path = 'results/reopt_precomputes/reopt_post'
    post_path_abs = Path(__file__).parent / post_path
    if not os.path.exists(post_path_abs.parent):
        os.mkdir(post_path_abs.parent)
    with open(post_path_abs, 'w') as outfile:
        json.dump(reopt.post, outfile)
    # mass_producer_dict = {
    #     "mass_units": "kg",
    #     "time_units": "hr",
    #     "min_mass_per_time": 10.0,
    #     "max_mass_per_time": 10.0,
    #     "electric_consumed_to_mass_produced_ratio_kwh_per_mass": 71.7,
    #     "thermal_consumed_to_mass_produced_ratio_kwh_per_mass": 0.0,
    #     "feedstock_consumed_to_mass_produced_ratio": 0.0,
    #     "installed_cost_us_dollars_per_mass_per_time": 10.0,
    #     "om_cost_us_dollars_per_mass_per_time": 1.5,
    #     "om_cost_us_dollars_per_mass": 0.0,
    #     "mass_value_us_dollars_per_mass": 5.0,
    #     "feedstock_cost_us_dollars_per_mass": 0.0,
    #     "macrs_option_years": 0,
    #     "macrs_bonus_pct": 0
    # }
    # reopt.post['Scenario']['Site']['MassProducer'] = mass_producer_dict

    if run_reopt_flag:
        #NEW METHOD
        load_dotenv()
        result = reopt.get_reopt_results()

        #BASIC INITIAL TEST FOR NEW METHOD
        # result = post_and_poll.get_api_results(data_for_post, NREL_API_KEY, 'https://offgrid-electrolyzer-reopt-dev-api.its.nrel.gov/v1',
        #                       'reopt_result_test_intergration.json')

        # f = open('massproducer_offgrid (1).json')
        # data_for_post = json.load(f)

        #OLD METHOD
        # result = reopt.get_reopt_results(force_download=True)


        pickle.dump(result, open("results/reopt_precomputes/results_{}_{}_{}.p".format(
            scenario['Site Name'], scenario['Scenario Name'], critical_load_factor), "wb"))

    else:
        print("Not running reopt. Loading Dummy data")
        precompute_path = 'results/reopt_precomputes/'
        precompute_path_abs = Path(__file__).parent / precompute_path
        result = pickle.load(
            open(os.path.join(precompute_path_abs, "results_ATB_moderate_2020_IOWA_0.9.p"), "rb"))

    if result['outputs']['Scenario']['Site']['PV']['size_kw']:
        solar_size_mw = result['outputs']['Scenario']['Site']['PV']['size_kw'] / 1000

    if result['outputs']['Scenario']['Site']['Wind']['size_kw']:
        wind_size_mw = result['outputs']['Scenario']['Site']['Wind']['size_kw'] / 1000

    if result['outputs']['Scenario']['Site']['Storage']['size_kw']:
        storage_size_mw = result['outputs']['Scenario']['Site']['Storage']['size_kw'] / 1000
        storage_size_mwh = result['outputs']['Scenario']['Site']['Storage']['size_kwh'] / 1000
        storage_hours = storage_size_mwh / storage_size_mw

    reopt_site_result = result['outputs']['Scenario']['Site']
    generated_date = pd.date_range(start='1/1/2018 00:00:00', end='12/31/2018 23:00:00', periods=8760)
    if reopt_site_result['Wind']['size_kw'] == 0:

        reopt_site_result['Wind']['year_one_power_production_series_kw'] = np.zeros(8760)
        reopt_site_result['Wind']['year_one_to_grid_series_kw'] = np.zeros(8760)
        reopt_site_result['Wind']['year_one_to_load_series_kw'] = np.zeros(8760)
        reopt_site_result['Wind']['year_one_to_battery_series_kw'] = np.zeros(8760)
        reopt_site_result['Wind']['year_one_curtailed_production_series_kw'] = np.zeros(8760)
        wind_size_mw = 0

    if reopt_site_result['PV']['size_kw'] == 0:
        reopt_site_result['PV']['year_one_power_production_series_kw'] = np.zeros(8760)
        reopt_site_result['PV']['year_one_to_grid_series_kw'] = np.zeros(8760)
        reopt_site_result['PV']['year_one_to_load_series_kw'] = np.zeros(8760)
        reopt_site_result['PV']['year_one_to_battery_series_kw'] = np.zeros(8760)
        reopt_site_result['PV']['year_one_curtailed_production_series_kw'] = np.zeros(8760)
        solar_size_mw = 0

    if reopt_site_result['Storage']['size_kw'] == 0:
        reopt_site_result['Storage']['year_one_soc_series_pct'] = np.zeros(8760)
        reopt_site_result['Storage']['year_one_to_massproducer_series_kw'] = np.zeros(8760)
        storage_size_mw = 0
        storage_size_mwh = 0
        storage_hours = 0

    combined_pv_wind_power_production = [x + y for x, y in
                                         zip(reopt_site_result['PV']['year_one_power_production_series_kw']
                                             , reopt_site_result['Wind']['year_one_power_production_series_kw'])]
    combined_pv_wind_storage_power_production = [x + y for x, y in zip(combined_pv_wind_power_production,
                                                                       reopt_site_result['Storage'][
                                                                           'year_one_to_load_series_kw'])]
    energy_shortfall = [y - x for x, y in zip(combined_pv_wind_storage_power_production, load)]
    energy_shortfall = [x if x > 0 else 0 for x in energy_shortfall]

    combined_pv_wind_curtailment = [x + y for x, y in
                                    zip(reopt_site_result['PV']['year_one_curtailed_production_series_kw']
                                        , reopt_site_result['Wind']['year_one_curtailed_production_series_kw'])]


    reopt_result_dict = {'Date':
                             generated_date,
                         'pv_power_production':
                             reopt_site_result['PV']
                             ['year_one_power_production_series_kw'],
                         'pv_power_to_grid':
                             reopt_site_result['PV']
                             ['year_one_to_grid_series_kw'],
                         'pv_power_to_load':
                             reopt_site_result['PV']['year_one_to_load_series_kw'],
                         'pv_power_to_battery':
                             reopt_site_result['PV']['year_one_to_battery_series_kw'],
                         'pv_power_curtailed':
                             reopt_site_result['PV']['year_one_curtailed_production_series_kw'],
                         'wind_power_production':
                             reopt_site_result['Wind']
                             ['year_one_power_production_series_kw'],
                         'wind_power_to_grid':
                             reopt_site_result['Wind']
                             ['year_one_to_grid_series_kw'],
                         'wind_power_to_load':
                             reopt_site_result['Wind']['year_one_to_load_series_kw'],
                         'wind_power_to_battery':
                             reopt_site_result['Wind']['year_one_to_battery_series_kw'],
                         'wind_power_curtailed':
                             reopt_site_result['Wind']['year_one_curtailed_production_series_kw'],
                         'combined_pv_wind_power_production':
                             combined_pv_wind_power_production,
                         'combined_pv_wind_storage_power_production':
                             combined_pv_wind_storage_power_production,
                         'storage_power_to_load':
                             reopt_site_result['Storage']['year_one_to_load_series_kw'],
                         'storage_power_to_grid':
                             reopt_site_result['Storage']['year_one_to_grid_series_kw'],
                         'battery_soc_pct':
                             reopt_site_result['Storage']['year_one_soc_series_pct'],
                         'energy_shortfall':
                             energy_shortfall,
                         'combined_pv_wind_curtailment':
                             combined_pv_wind_curtailment
                         }

    REoptResultsDF = pd.DataFrame(reopt_result_dict)

    return wind_size_mw, solar_size_mw, storage_size_mw, storage_size_mwh, storage_hours, result, REoptResultsDF