diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index 196aae6bd..16e510202 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -948,7 +948,9 @@ version = "1.11.0" [[deps.REopt]] deps = ["ArchGDAL", "CSV", "CoolProp", "DataFrames", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"] -git-tree-sha1 = "103761fa0f7447377726347af656cde6ab1160cc" +git-tree-sha1 = "277df4545cd30a5fc227bf982897ef65d5ea3520" +repo-rev = "elec_util_usage" +repo-url = "https://github.com/NREL/REopt.jl.git" uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6" version = "0.55.1" diff --git a/julia_src/http.jl b/julia_src/http.jl index 1c9e36d99..b59e8f418 100644 --- a/julia_src/http.jl +++ b/julia_src/http.jl @@ -91,6 +91,7 @@ function reopt(req::HTTP.Request) # Catch handled/unhandled exceptions in data pre-processing, JuMP setup try model_inputs = reoptjl.REoptInputs(d) + @info "Successfully processed REopt inputs." catch e @error "Something went wrong during REopt inputs processing!" exception=(e, catch_backtrace()) error_response["error"] = sprint(showerror, e) @@ -102,6 +103,7 @@ function reopt(req::HTTP.Request) # Catch handled/unhandled exceptions in optimization try results = reoptjl.run_reopt(ms, model_inputs) + @info "Successfully ran REopt optimization." inputs_with_defaults_from_julia_financial = [ :NOx_grid_cost_per_tonne, :SO2_grid_cost_per_tonne, :PM25_grid_cost_per_tonne, :NOx_onsite_fuelburn_cost_per_tonne, :SO2_onsite_fuelburn_cost_per_tonne, :PM25_onsite_fuelburn_cost_per_tonne, @@ -235,6 +237,14 @@ function reopt(req::HTTP.Request) high_temp_storage_dict = Dict(key=>getfield(model_inputs.s.storage.attr["HighTempThermalStorage"], key) for key in inputs_with_defaults_from_julia_high_temp_storage) else high_temp_storage_dict = Dict() + end + if haskey(d, "ElectricTariff") && !isempty(model_inputs.s.electric_tariff.urdb_metadata) + inputs_from_julia_electric_tariff = [ + :urdb_metadata + ] + electric_tariff_dict = Dict(key=>getfield(model_inputs.s.electric_tariff, key) for key in inputs_from_julia_electric_tariff) + else + electric_tariff_dict = Dict() end inputs_with_defaults_set_in_julia = Dict( "Financial" => Dict(key=>getfield(model_inputs.s.financial, key) for key in inputs_with_defaults_from_julia_financial), @@ -251,7 +261,8 @@ function reopt(req::HTTP.Request) "ElectricStorage" => electric_storage_dict, "ColdThermalStorage" => cold_storage_dict, "HotThermalStorage" => hot_storage_dict, - "HighTempThermalStorage" => high_temp_storage_dict + "HighTempThermalStorage" => high_temp_storage_dict, + "ElectricTariff" => electric_tariff_dict ) catch e @error "Something went wrong in REopt optimization!" exception=(e, catch_backtrace()) @@ -272,6 +283,11 @@ function reopt(req::HTTP.Request) if isempty(error_response) @info "REopt model solved with status $(results["status"])." + # These are matrices that need to be vector. + if haskey(results, "ElectricTariff") + results["ElectricTariff"]["year_one_electric_to_load_energy_cost_series_before_tax"] = results["ElectricTariff"]["year_one_electric_to_load_energy_cost_series_before_tax"][:,1] + results["ElectricTariff"]["monthly_facility_demand_cost_series_before_tax"] = results["ElectricTariff"]["monthly_facility_demand_cost_series_before_tax"][:,1] + end response = Dict( "results" => results, "reopt_version" => string(pkgversion(reoptjl)) diff --git a/reoptjl/custom_table_config.py b/reoptjl/custom_table_config.py index e99dc0ea8..a7f979c8f 100644 --- a/reoptjl/custom_table_config.py +++ b/reoptjl/custom_table_config.py @@ -1294,5 +1294,742 @@ "name": "Placeholder Calculation With BAU Reference", "formula": lambda col, bau, headers: f'=({bau["placeholder1_value"]}-{col}{headers["Placeholder2"] + 2})/{bau["placeholder1_value"]}' # This formula calculates the percentage change of Placeholder2 using Placeholder1's BAU value as the reference. + }, + + # ANCCR Calculations -- none of these are used yet because the current in-spreadsheet calcs to not compare BAU to BAU + { + "name": "Year 1 Total Bill Charges Percent Change (%)", + "formula": lambda col, bau, headers: f'= {col}{headers["Placeholder1"] + 2}+{col}{headers["Placeholder2"] + 2}' ### need to update this + # This formula calculates the change in year 1 costs from BAU_alt_rate_x vs. BAU_current_rate + }, + { + "name": "Year 1 Fixed Charges Percent Change (%)", + "formula": lambda col, bau, headers: f'= {col}{headers["Placeholder1"] + 2}+{col}{headers["Placeholder2"] + 2}' ### need to update this + # This formula calculates the change in year 1 costs from BAU_alt_rate_x vs. BAU_current_rate + }, + { + "name": "Year 1 Energy Charges Percent Change (%)", + "formula": lambda col, bau, headers: f'= {col}{headers["Placeholder1"] + 2}+{col}{headers["Placeholder2"] + 2}' ### need to update this + # This formula calculates the change in year 1 costs from BAU_alt_rate_x vs. BAU_current_rate + }, + { + "name": "Year 1 Demand Charges Percent Change (%)", + "formula": lambda col, bau, headers: f'= {col}{headers["Placeholder1"] + 2}+{col}{headers["Placeholder2"] + 2}' ### need to update this + # This formula calculates the change in year 1 costs from BAU_alt_rate_x vs. BAU_current_rate + }, + { + "name": "Year 1 Fixed Charges Percent of Total Bill (%)", + "formula": lambda col, bau, headers: f'= {col}{headers["Placeholder1"] + 2}+{col}{headers["Placeholder2"] + 2}' ### need to update this + # This formula calculates the change in year 1 costs from BAU_alt_rate_x vs. BAU_current_rate + }, + { + "name": "Year 1 Energy Charges Percent of Total Bill (%)", + "formula": lambda col, bau, headers: f'= {col}{headers["Placeholder1"] + 2}+{col}{headers["Placeholder2"] + 2}' ### need to update this + # This formula calculates the change in year 1 costs from BAU_alt_rate_x vs. BAU_current_rate + }, + { + "name": "Year 1 Demand Charges Percent of Total Bill (%)", + "formula": lambda col, bau, headers: f'= {col}{headers["Placeholder1"] + 2}+{col}{headers["Placeholder2"] + 2}' ### need to update this + # This formula calculates the change in year 1 costs from BAU_alt_rate_x vs. BAU_current_rate + } +] + + + +custom_table_anccr = [ +##################################################################################################### +################################ Need to get the RATE NAME to appear in header ################ +##################################################################################################### + + # { + # "label": "Rate Headers", + # "key": "site", + # "bau_value": lambda df: "", + # "scenario_value": lambda df: safe_get(df, "", "") + # }, + +##################################################################################################### +################################ General Information ################################ +##################################################################################################### +# maybe we remove this section since we are missing installation name and site location from REopt results + { + "label": "Installation Name", # Not REopt result; Name based on UTRMS Data -- maybe we delete? + "key": "installation_name", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "Site Location", # Not REopt result; Name based on User input -- maybe we delete? + "key": "site_location", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "Utility Name", + "key": "utility_name", + "bau_value": lambda df: safe_get(df, "inputs.ElectricTariff.urdb_metadata.urdb_utility", ""), + "scenario_value": lambda df: "" + }, + +##################################################################################################### +################################ Rate Analysis Summary ################################ +##################################################################################################### + + { + "label": "Rate Analysis Summary", + "key": "rate_analysis_summary_separator", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "Rate Name", + "key": "rate_name", + "bau_value": lambda df: safe_get(df, "inputs.ElectricTariff.urdb_metadata.urdb_rate_name", ""), + "scenario_value": lambda df: "" + }, + { + "label": "Voltage Level", + "key": "voltage_level", + "bau_value": lambda df: safe_get(df, "inputs.ElectricTariff.urdb_metadata.", ""), + "scenario_value": lambda df: "" + }, + { + "label": "Year 1 Fixed Charges ($)", + "key": "year_1_fixed_charges", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.year_one_fixed_cost_before_tax_bau"), + "scenario_value": lambda df: "" + }, + { + "label": "Year 1 Energy Charges ($)", + "key": "year_1_energy_charges", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.year_one_energy_cost_before_tax_bau"), + "scenario_value": lambda df: "" + }, + { + "label": "Year 1 Demand Charges ($)", + "key": "year_1_demand_charges", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.year_one_demand_cost_before_tax_bau"), + "scenario_value": lambda df: "" + }, + { + "label": "Year 1 Total Bill Charges ($)", + "key": "year_1_total_bill_charges", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.year_one_bill_before_tax_bau"), + "scenario_value": lambda df: "" + }, + # Calculated Value + { + "label": "Year 1 Total Bill Charges Percent Change (%)", # this value will need to be calculated compared to the current rate + "key": "year_1_total_bill_charges_percent_change", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + +##################################################################################################### +################################ Year 1 Annual Costs ################################ +##################################################################################################### + + { + "label": "Year 1 Annual Costs", + "key": "year_1_annual_costs_separator", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "Year 1 Fixed Charges ($)", + "key": "year_1_fixed_charges", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.year_one_fixed_cost_before_tax_bau"), + "scenario_value": lambda df: "" + }, + { + "label": "Year 1 Fixed Charges Percent Change (%)", # this value will need to be calculated compared to the current rate + "key": "year_1_fixed_charges_percent_change", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "Year 1 Energy Charges ($)", + "key": "year_1_energy_charges", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.year_one_energy_cost_before_tax_bau"), + "scenario_value": lambda df: "" + }, + { + "label": "Year 1 Energy Charges Percent Change (%)", # this value will need to be calculated compared to the current rate + "key": "year_1_energy_charges_percent_change", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "Year 1 Demand Charges ($)", + "key": "year_1_demand_charges", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.year_one_demand_cost_before_tax_bau"), + "scenario_value": lambda df: "" + }, + { + "label": "Year 1 Demand Charges Percent Change (%)", # this value will need to be calculated compared to the current rate + "key": "year_1_demand_charges_percent_change", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "Year 1 Total Bill Charges ($)", + "key": "year_1_total_bill_charges", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.year_one_bill_before_tax_bau"), + "scenario_value": lambda df: "" + }, + { + "label": "Year 1 Total Bill Charges Percent Change (%)", # this value will need to be calculated compared to the current rate + "key": "year_1_total_bill_charges_percent_change", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + +##################################################################################################### +################################ Year 1 Annual Costs as a Percent of Total Bill ################################ +##################################################################################################### + + { + "label": "Year 1 Annual Costs as a Percent of Total Bill", + "key": "year_1_annual_costs_percent_of_total_bill_separator", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "Year 1 Fixed Charges Percent of Total Bill (%)", # value will be a calculation + "key": "year_1_fixed_charges_percent_of_total_bill", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "Year 1 Energy Charges Percent of Total Bill (%)", # value will be a calculation + "key": "year_1_energy_charges_percent_of_total_bill", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "Year 1 Demand Charges Percent of Total Bill (%)", # value will be a calculation + "key": "year_1_demand_charges_percent_of_total_bill", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + +##################################################################################################### +################################ Load Metrics ################################ +##################################################################################################### + + { + "label": "Load Metrics", + "key": "year_1_load_metrics_separator", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "Annual Grid Purchases (kWh)", + "key": "annual_grid_purchases_kwh", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.annual_calculated_kwh"), + "scenario_value": lambda df: "" + }, + { + "label": "Year 1 Peak Load (kW)", + "key": "year_1_peak_load_kw", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.annual_peak_kw"), + "scenario_value": lambda df: "" + }, + +##################################################################################################### +################################ URDB Rate Information ################################ +##################################################################################################### + { + "label": "URDB Rate Information", + "key": "urdb_rate_information_separator", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "Rate Name", # Not REopt result; Name based on User input + "key": "rate_name", + "bau_value": lambda df: safe_get(df, "inputs.ElectricTariff.urdb_metadata.urdb_rate_name", ""), + "scenario_value": lambda df: "" + }, + { + "label": "URDB Label", # will need to pull from URDB + "key": "urdb_label", + "bau_value": lambda df: safe_get(df, "inputs.ElectricTariff.urdb_metadata.urdb_label", ""), + "scenario_value": lambda df: "" + }, + { + "label": "Utility Name", # Not REopt result; Name based on User input + "key": "utility_name", + "bau_value": lambda df: safe_get(df, "inputs.ElectricTariff.urdb_metadata.urdb_utility", ""), + "scenario_value": lambda df: "" + }, + { + "label": "Rate Effective Date (latest_update)", # will need to pull from URDB + "key": "rate_effective_date", + "bau_value": lambda df: safe_get(df, "inputs.ElectricTariff.urdb_metadata.urdb_rate_effective_date", ""), + "scenario_value": lambda df: "" + }, + { + "label": "Voltage Level (voltagecategory)", # will need to pull from URDB + "key": "voltage_level_urdb", + "bau_value": lambda df: safe_get(df, "inputs.ElectricTariff.urdb_metadata.urdb_voltage_level", ""), + "scenario_value": lambda df: "" + }, + { + "label": "Peak kW Capacity Min (peakkwcapacitymin)", # will need to pull from URDB + "key": "peak_kw_capacity_min", + "bau_value": lambda df: safe_get(df, "inputs.ElectricTariff.urdb_metadata.urdb_peak_kw_capacity_min", ""), + "scenario_value": lambda df: "" + }, + { + "label": "Peak kW Capacity Max (peakkwcapacitymax)", # will need to pull from URDB + "key": "peak_kw_capacity_max", + "bau_value": lambda df: safe_get(df, "inputs.ElectricTariff.urdb_metadata.urdb_peak_kw_capacity_max", ""), + "scenario_value": lambda df: "" + }, + { + "label": "Rate Description (description)", # will need to pull from URDB + "key": "rate_description", + "bau_value": lambda df: safe_get(df, "inputs.ElectricTariff.urdb_metadata.urdb_rate_description", ""), + "scenario_value": lambda df: "" + }, + { + "label": "Additional Information (basicinformationcomments)", # will need to pull from URDB + "key": "additional_information", + "bau_value": lambda df: safe_get(df, "inputs.ElectricTariff.urdb_metadata.urdb_rate_additional_info", ""), + "scenario_value": lambda df: "" + }, + { + "label": "Energy Comments (energycomments)", # will need to pull from URDB + "key": "energy_comments", + "bau_value": lambda df: safe_get(df, "inputs.ElectricTariff.urdb_metadata.urdb_energy_comments", ""), + "scenario_value": lambda df: "" + }, + { + "label": "Demand Comments (demandcomments)", # will need to pull from URDB + "key": "demand_comments", + "bau_value": lambda df: safe_get(df, "inputs.ElectricTariff.urdb_metadata.urdb_demand_comments", ""), + "scenario_value": lambda df: "" + }, + { + "label": "URDB Link", # will need to pull from URDB + "key": "urdb_link", + "bau_value": lambda df: safe_get(df, "inputs.ElectricTariff.urdb_metadata.urdb_url_link", ""), + "scenario_value": lambda df: "" + }, + +##################################################################################################### +################################ Year 1 Monthly Energy Costs ################################ +##################################################################################################### + + { + "label": "Year 1 Monthly Energy Costs ($)", + "key": "year_1_monthly_energy_costs_separator", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "January Energy Cost ($)", + "key": "january_energy_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_electric_to_load_energy_cost_series_before_tax.0"), + "scenario_value": lambda df: "" + }, + { + "label": "February Energy Cost ($)", + "key": "february_energy_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_electric_to_load_energy_cost_series_before_tax.1"), + "scenario_value": lambda df: "" + }, + { + "label": "March Energy Cost ($)", + "key": "march_energy_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_electric_to_load_energy_cost_series_before_tax.2"), + "scenario_value": lambda df: "" + }, + { + "label": "April Energy Cost ($)", + "key": "april_energy_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_electric_to_load_energy_cost_series_before_tax.3"), + "scenario_value": lambda df: "" + }, + { + "label": "May Energy Cost ($)", + "key": "may_energy_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_electric_to_load_energy_cost_series_before_tax.4"), + "scenario_value": lambda df: "" + }, + { + "label": "June Energy Cost ($)", + "key": "june_energy_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_electric_to_load_energy_cost_series_before_tax.5"), + "scenario_value": lambda df: "" + }, + { + "label": "July Energy Cost ($)", + "key": "july_energy_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_electric_to_load_energy_cost_series_before_tax.6"), + "scenario_value": lambda df: "" + }, + { + "label": "August Energy Cost ($)", + "key": "august_energy_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_electric_to_load_energy_cost_series_before_tax.7"), + "scenario_value": lambda df: "" + }, + { + "label": "September Energy Cost ($)", + "key": "september_energy_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_electric_to_load_energy_cost_series_before_tax.8"), + "scenario_value": lambda df: "" + }, + { + "label": "October Energy Cost ($)", + "key": "october_energy_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_electric_to_load_energy_cost_series_before_tax.9"), + "scenario_value": lambda df: "" + }, + { + "label": "November Energy Cost ($)", + "key": "november_energy_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_electric_to_load_energy_cost_series_before_tax.10"), + "scenario_value": lambda df: "" + }, + { + "label": "December Energy Cost ($)", + "key": "december_energy_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_electric_to_load_energy_cost_series_before_tax.11"), + "scenario_value": lambda df: "" + }, + +##################################################################################################### +################################ Year 1 Monthly Demand Costs ################################ +##################################################################################################### + + { + "label": "Year 1 Monthly Demand Costs ($)", + "key": "year_1_monthly_demand_costs_separator", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "January Demand Cost ($)", + "key": "january_demand_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_gross_tou_demand_cost_series_before_tax.0") + safe_get(df, "outputs.ElectricTariff.monthly_facility_demand_cost_series_before_tax.0"), + "scenario_value": lambda df: "" + }, + { + "label": "February Demand Cost ($)", + "key": "february_demand_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_gross_tou_demand_cost_series_before_tax.1") + safe_get(df, "outputs.ElectricTariff.monthly_facility_demand_cost_series_before_tax.1"), + "scenario_value": lambda df: "" + }, + { + "label": "March Demand Cost ($)", + "key": "march_demand_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_gross_tou_demand_cost_series_before_tax.2") + safe_get(df, "outputs.ElectricTariff.monthly_facility_demand_cost_series_before_tax.2"), + "scenario_value": lambda df: "" + }, + { + "label": "April Demand Cost ($)", + "key": "april_demand_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_gross_tou_demand_cost_series_before_tax.3") + safe_get(df, "outputs.ElectricTariff.monthly_facility_demand_cost_series_before_tax.3"), + "scenario_value": lambda df: "" + }, + { + "label": "May Demand Cost ($)", + "key": "may_demand_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_gross_tou_demand_cost_series_before_tax.4") + safe_get(df, "outputs.ElectricTariff.monthly_facility_demand_cost_series_before_tax.4"), + "scenario_value": lambda df: "" + }, + { + "label": "June Demand Cost ($)", + "key": "june_demand_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_gross_tou_demand_cost_series_before_tax.5") + safe_get(df, "outputs.ElectricTariff.monthly_facility_demand_cost_series_before_tax.5"), + "scenario_value": lambda df: "" + }, + { + "label": "July Demand Cost ($)", + "key": "july_demand_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_gross_tou_demand_cost_series_before_tax.6") + safe_get(df, "outputs.ElectricTariff.monthly_facility_demand_cost_series_before_tax.6"), + "scenario_value": lambda df: "" + }, + { + "label": "August Demand Cost ($)", + "key": "august_demand_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_gross_tou_demand_cost_series_before_tax.7") + safe_get(df, "outputs.ElectricTariff.monthly_facility_demand_cost_series_before_tax.7"), + "scenario_value": lambda df: "" + }, + { + "label": "September Demand Cost ($)", + "key": "september_demand_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_gross_tou_demand_cost_series_before_tax.8") + safe_get(df, "outputs.ElectricTariff.monthly_facility_demand_cost_series_before_tax.8"), + "scenario_value": lambda df: "" + }, + { + "label": "October Demand Cost ($)", + "key": "october_demand_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_gross_tou_demand_cost_series_before_tax.9") + safe_get(df, "outputs.ElectricTariff.monthly_facility_demand_cost_series_before_tax.9"), + "scenario_value": lambda df: "" + }, + { + "label": "November Demand Cost ($)", + "key": "november_demand_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_gross_tou_demand_cost_series_before_tax.10") + safe_get(df, "outputs.ElectricTariff.monthly_facility_demand_cost_series_before_tax.10"), + "scenario_value": lambda df: "" + }, + { + "label": "December Demand Cost ($)", + "key": "december_demand_cost", + "bau_value": lambda df: safe_get(df, "outputs.ElectricTariff.monthly_gross_tou_demand_cost_series_before_tax.11") + safe_get(df, "outputs.ElectricTariff.monthly_facility_demand_cost_series_before_tax.11"), + "scenario_value": lambda df: "" + }, + +##################################################################################################### +################################ Year 1 Monthly Total Bill Costs ################################ +##################################################################################################### + + { + "label": "Year 1 Monthly Total Bill Costs ($)", + "key": "year_1_monthly_total_bill_costs_separator", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "January Total Bill Cost ($)", # not sure if we have a monthly total bill cost output in REopt -- need to ask Bhavesh + "key": "january_total_bill_cost", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "February Total Bill Cost ($)", + "key": "february_total_bill_cost", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "March Total Bill Cost ($)", + "key": "march_total_bill_cost", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "April Total Bill Cost ($)", + "key": "april_total_bill_cost", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "May Total Bill Cost ($)", + "key": "may_total_bill_cost", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "June Total Bill Cost ($)", + "key": "june_total_bill_cost", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "July Total Bill Cost ($)", + "key": "july_total_bill_cost", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "August Total Bill Cost ($)", + "key": "august_total_bill_cost", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "September Total Bill Cost ($)", + "key": "september_total_bill_cost", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "October Total Bill Cost ($)", + "key": "october_total_bill_cost", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "November Total Bill Cost ($)", + "key": "november_total_bill_cost", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "December Total Bill Cost ($)", + "key": "december_total_bill_cost", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + +##################################################################################################### +################################ Monthly Energy Consumption (kWh) ################################ +##################################################################################################### + + { + "label": "Monthly Energy Consumption (kWh)", + "key": "monthly_energy_consumption_separator", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "January Energy Consumption (kWh)", + "key": "january_energy_consumption_kwh", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_calculated_kwh.0"), + "scenario_value": lambda df: "" + }, + { + "label": "February Energy Consumption (kWh)", + "key": "february_energy_consumption_kwh", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_calculated_kwh.1"), + "scenario_value": lambda df: "" + }, + { + "label": "March Energy Consumption (kWh)", + "key": "march_energy_consumption_kwh", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_calculated_kwh.2"), + "scenario_value": lambda df: "" + }, + { + "label": "April Energy Consumption (kWh)", + "key": "april_energy_consumption_kwh", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_calculated_kwh.3"), + "scenario_value": lambda df: "" + }, + { + "label": "May Energy Consumption (kWh)", + "key": "may_energy_consumption_kwh", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_calculated_kwh.4"), + "scenario_value": lambda df: "" + }, + { + "label": "June Energy Consumption (kWh)", + "key": "june_energy_consumption_kwh", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_calculated_kwh.5"), + "scenario_value": lambda df: "" + }, + { + "label": "July Energy Consumption (kWh)", + "key": "july_energy_consumption_kwh", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_calculated_kwh.6"), + "scenario_value": lambda df: "" + }, + { + "label": "August Energy Consumption (kWh)", + "key": "august_energy_consumption_kwh", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_calculated_kwh.7"), + "scenario_value": lambda df: "" + }, + { + "label": "September Energy Consumption (kWh)", + "key": "september_energy_consumption_kwh", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_calculated_kwh.8"), + "scenario_value": lambda df: "" + }, + { + "label": "October Energy Consumption (kWh)", + "key": "october_energy_consumption_kwh", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_calculated_kwh.9"), + "scenario_value": lambda df: "" + }, + { + "label": "November Energy Consumption (kWh)", + "key": "november_energy_consumption_kwh", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_calculated_kwh.10"), + "scenario_value": lambda df: "" + }, + { + "label": "December Energy Consumption (kWh)", + "key": "december_energy_consumption_kwh", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_calculated_kwh.11"), + "scenario_value": lambda df: "" + }, + +##################################################################################################### +################################ Monthly Peak Load (kW) ################################ +##################################################################################################### + + { + "label": "Monthly Peak Load (kW)", + "key": "monthly_peak_load_separator", + "bau_value": lambda df: "", + "scenario_value": lambda df: "" + }, + { + "label": "January Peak Load (kW)", + "key": "january_peak_load_kw", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_peak_kw.0"), + "scenario_value": lambda df: "" + }, + { + "label": "February Peak Load (kW)", + "key": "february_peak_load_kw", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_peak_kw.1"), + "scenario_value": lambda df: "" + }, + { + "label": "March Peak Load (kW)", + "key": "march_peak_load_kw", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_peak_kw.2"), + "scenario_value": lambda df: "" + }, + { + "label": "April Peak Load (kW)", + "key": "april_peak_load_kw", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_peak_kw.3"), + "scenario_value": lambda df: "" + }, + { + "label": "May Peak Load (kW)", + "key": "may_peak_load_kw", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_peak_kw.4"), + "scenario_value": lambda df: "" + }, + { + "label": "June Peak Load (kW)", + "key": "june_peak_load_kw", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_peak_kw.5"), + "scenario_value": lambda df: "" + }, + { + "label": "July Peak Load (kW)", + "key": "july_peak_load_kw", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_peak_kw.6"), + "scenario_value": lambda df: "" + }, + { + "label": "August Peak Load (kW)", + "key": "august_peak_load_kw", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_peak_kw.7"), + "scenario_value": lambda df: "" + }, + { + "label": "September Peak Load (kW)", + "key": "september_peak_load_kw", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_peak_kw.8"), + "scenario_value": lambda df: "" + }, + { + "label": "October Peak Load (kW)", + "key": "october_peak_load_kw", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_peak_kw.9"), + "scenario_value": lambda df: "" + }, + { + "label": "November Peak Load (kW)", + "key": "november_peak_load_kw", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_peak_kw.10"), + "scenario_value": lambda df: "" + }, + { + "label": "December Peak Load (kW)", + "key": "december_peak_load_kw", + "bau_value": lambda df: safe_get(df, "outputs.ElectricLoad.monthly_peak_kw.11"), + "scenario_value": lambda df: "" } -] \ No newline at end of file +] diff --git a/reoptjl/migrations/0106_electricloadoutputs_annual_peak_kw_and_more.py b/reoptjl/migrations/0106_electricloadoutputs_annual_peak_kw_and_more.py new file mode 100644 index 000000000..8add1d9cb --- /dev/null +++ b/reoptjl/migrations/0106_electricloadoutputs_annual_peak_kw_and_more.py @@ -0,0 +1,164 @@ +# Generated by Django 4.2.24 on 2025-10-06 17:06 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0105_alter_chpoutputs_initial_capital_costs_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='electricloadoutputs', + name='annual_peak_kw', + field=models.FloatField(blank=True, help_text='Annual peak energy demand determined from load_series_kw. Does not include electric load for any new heating or cooling techs.', null=True), + ), + migrations.AddField( + model_name='electricloadoutputs', + name='monthly_calculated_kwh', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Monthly energy consumption calculated by summing up load_series_kw. Does not include electric load for any new heating or cooling techs.', size=None), + ), + migrations.AddField( + model_name='electricloadoutputs', + name='monthly_peak_kw', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Monthly peak energy demand determined from load_series_kw. Does not include electric load for any new heating or cooling techs.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='EXC_electric_to_grid_series_kw', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of power exports to the grid above net metering limit for each timestep.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='EXC_export_rate_series', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of compensation rate in each timestep for exporting power to grid beyond net metering limit.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='EXC_monthly_export_cost_benefit_before_tax', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of monthly monetary benefit from exporting power to grid above net metering limit.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='EXC_monthly_export_series_kwh', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of monthly energy exports to grid above net metering limit.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='NEM_electric_to_grid_series_kw', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of power exports to the grid up to net metering limit for each timestep.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='NEM_export_rate_series', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of compensation rate in each timestep for exporting power to grid using systems sized up to net metering limit.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='NEM_monthly_export_cost_benefit_before_tax', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Year one cost of electricity consumed in each month.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='NEM_monthly_export_series_kwh', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Year one cost of electricity consumed in each month.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='WHL_electric_to_grid_series_kw', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of power exports to the grid for wholesale in each timestep.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='WHL_export_rate_series', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of compensation rate in each timestep for exporting power to grid for wholesale.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='WHL_monthly_export_cost_benefit_before_tax', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of monthly monetary benefit from exporting power to grid at wholesale rate.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='WHL_monthly_export_series_kwh', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of monthly energy exports to grid under wholesale benefit.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='monthly_electric_to_load_energy_cost_series_before_tax', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of monthly cost of power purchased from grid to serve loads.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='monthly_electric_to_storage_energy_cost_series_before_tax', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of monthly cost of power purchased from grid to charge battery storage system.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='monthly_facility_demand_cost_series_before_tax', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of total monthly facility demand charges by month.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='monthly_gross_tou_demand_cost_series_before_tax', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of total time of use demand charges by month.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='monthly_tou_demand_cost_series_before_tax', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of total time of use demand charges for each month.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='tou_demand_metrics', + field=models.JSONField(blank=True, null=True), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='year_one_billed_demand_rate_series', + field=models.JSONField(blank=True, null=True), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='year_one_billed_energy_rate_series', + field=models.JSONField(blank=True, help_text='Series of billed energy rates for each timestep in year one.', null=True), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='year_one_billed_energy_rate_tier_limits', + field=models.JSONField(blank=True, null=True), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='year_one_billed_facilitydemand_monthly_rate_series', + field=models.JSONField(blank=True, null=True), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='year_one_billed_facilitydemand_monthly_rate_tier_limits', + field=models.JSONField(blank=True, null=True), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='year_one_billed_tou_demand_rate_tier_limits', + field=models.JSONField(blank=True, null=True), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='year_one_electric_to_load_energy_cost_series_before_tax', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of cost of power purchased from grid to serve load in each timestep.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='year_one_electric_to_storage_energy_cost_series_before_tax', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Series of cost of power purchased from grid to charge battery storage system in each timestep.', size=None), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='year_one_monthly_fixed_cost', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, help_text='Year one fixed utility costs for each month.', size=None), + ), + ] diff --git a/reoptjl/migrations/0107_electrictariffoutputs_urdb_demand_comments_and_more.py b/reoptjl/migrations/0107_electrictariffoutputs_urdb_demand_comments_and_more.py new file mode 100644 index 000000000..7dddf6eb4 --- /dev/null +++ b/reoptjl/migrations/0107_electrictariffoutputs_urdb_demand_comments_and_more.py @@ -0,0 +1,73 @@ +# Generated by Django 4.2.24 on 2025-10-16 20:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0106_electricloadoutputs_annual_peak_kw_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='electrictariffoutputs', + name='urdb_demand_comments', + field=models.TextField(blank=True, help_text='Business as usual life cycle utility minimum charge adder, after-tax', null=True), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='urdb_energy_comments', + field=models.TextField(blank=True, help_text='Business as usual life cycle utility minimum charge adder, after-tax', null=True), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='urdb_label', + field=models.TextField(blank=True, help_text='Business as usual life cycle utility minimum charge adder, after-tax', null=True), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='urdb_peak_kw_capacity_max', + field=models.FloatField(blank=True, help_text='Business as usual life cycle utility minimum charge adder, after-tax', null=True), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='urdb_peak_kw_capacity_min', + field=models.FloatField(blank=True, help_text='Business as usual life cycle utility minimum charge adder, after-tax', null=True), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='urdb_rate_additional_info', + field=models.TextField(blank=True, help_text='Business as usual life cycle utility minimum charge adder, after-tax', null=True), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='urdb_rate_description', + field=models.TextField(blank=True, help_text='Business as usual life cycle utility minimum charge adder, after-tax', null=True), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='urdb_rate_effective_date', + field=models.TextField(blank=True, help_text='Business as usual life cycle utility minimum charge adder, after-tax', null=True), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='urdb_rate_name', + field=models.TextField(blank=True, help_text='Business as usual life cycle utility minimum charge adder, after-tax', null=True), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='urdb_url_link', + field=models.TextField(blank=True, help_text='Business as usual life cycle utility minimum charge adder, after-tax', null=True), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='urdb_utility', + field=models.TextField(blank=True, help_text='Business as usual life cycle utility minimum charge adder, after-tax', null=True), + ), + migrations.AddField( + model_name='electrictariffoutputs', + name='urdb_voltage_level', + field=models.TextField(blank=True, help_text='Business as usual life cycle utility minimum charge adder, after-tax', null=True), + ), + ] diff --git a/reoptjl/migrations/0110_merge_20251022_1752.py b/reoptjl/migrations/0110_merge_20251022_1752.py new file mode 100644 index 000000000..cb76a4e89 --- /dev/null +++ b/reoptjl/migrations/0110_merge_20251022_1752.py @@ -0,0 +1,14 @@ +# Generated by Django 4.2.25 on 2025-10-22 17:52 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0107_electrictariffoutputs_urdb_demand_comments_and_more'), + ('reoptjl', '0109_remove_ghpoutputs_iterations_auto_guess_and_more'), + ] + + operations = [ + ] diff --git a/reoptjl/migrations/0111_remove_electrictariffoutputs_urdb_demand_comments_and_more.py b/reoptjl/migrations/0111_remove_electrictariffoutputs_urdb_demand_comments_and_more.py new file mode 100644 index 000000000..f4eeb281d --- /dev/null +++ b/reoptjl/migrations/0111_remove_electrictariffoutputs_urdb_demand_comments_and_more.py @@ -0,0 +1,66 @@ +# Generated by Django 4.2.25 on 2025-10-22 22:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0110_merge_20251022_1752'), + ] + + operations = [ + migrations.RemoveField( + model_name='electrictariffoutputs', + name='urdb_demand_comments', + ), + migrations.RemoveField( + model_name='electrictariffoutputs', + name='urdb_energy_comments', + ), + migrations.RemoveField( + model_name='electrictariffoutputs', + name='urdb_label', + ), + migrations.RemoveField( + model_name='electrictariffoutputs', + name='urdb_peak_kw_capacity_max', + ), + migrations.RemoveField( + model_name='electrictariffoutputs', + name='urdb_peak_kw_capacity_min', + ), + migrations.RemoveField( + model_name='electrictariffoutputs', + name='urdb_rate_additional_info', + ), + migrations.RemoveField( + model_name='electrictariffoutputs', + name='urdb_rate_description', + ), + migrations.RemoveField( + model_name='electrictariffoutputs', + name='urdb_rate_effective_date', + ), + migrations.RemoveField( + model_name='electrictariffoutputs', + name='urdb_rate_name', + ), + migrations.RemoveField( + model_name='electrictariffoutputs', + name='urdb_url_link', + ), + migrations.RemoveField( + model_name='electrictariffoutputs', + name='urdb_utility', + ), + migrations.RemoveField( + model_name='electrictariffoutputs', + name='urdb_voltage_level', + ), + migrations.AddField( + model_name='electrictariffinputs', + name='urdb_metadata', + field=models.JSONField(blank=True, help_text='Utility rate meta data from Utility Rate Database API', null=True), + ), + ] diff --git a/reoptjl/migrations/0112_rename_nem_electric_to_grid_series_kw_electrictariffoutputs_net_metering_electric_to_grid_series_kw_.py b/reoptjl/migrations/0112_rename_nem_electric_to_grid_series_kw_electrictariffoutputs_net_metering_electric_to_grid_series_kw_.py new file mode 100644 index 000000000..5b99f0d3c --- /dev/null +++ b/reoptjl/migrations/0112_rename_nem_electric_to_grid_series_kw_electrictariffoutputs_net_metering_electric_to_grid_series_kw_.py @@ -0,0 +1,73 @@ +# Generated by Django 4.2.25 on 2025-10-22 22:21 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0111_remove_electrictariffoutputs_urdb_demand_comments_and_more'), + ] + + operations = [ + migrations.RenameField( + model_name='electrictariffoutputs', + old_name='NEM_electric_to_grid_series_kw', + new_name='net_metering_electric_to_grid_series_kw', + ), + migrations.RenameField( + model_name='electrictariffoutputs', + old_name='EXC_electric_to_grid_series_kw', + new_name='net_metering_excess_electric_to_grid_series_kw', + ), + migrations.RenameField( + model_name='electrictariffoutputs', + old_name='EXC_export_rate_series', + new_name='net_metering_excess_export_rate_series', + ), + migrations.RenameField( + model_name='electrictariffoutputs', + old_name='EXC_monthly_export_cost_benefit_before_tax', + new_name='net_metering_excess_monthly_export_cost_benefit_before_tax', + ), + migrations.RenameField( + model_name='electrictariffoutputs', + old_name='EXC_monthly_export_series_kwh', + new_name='net_metering_excess_monthly_export_series_kwh', + ), + migrations.RenameField( + model_name='electrictariffoutputs', + old_name='NEM_export_rate_series', + new_name='net_metering_export_rate_series', + ), + migrations.RenameField( + model_name='electrictariffoutputs', + old_name='NEM_monthly_export_cost_benefit_before_tax', + new_name='net_metering_monthly_export_cost_benefit_before_tax', + ), + migrations.RenameField( + model_name='electrictariffoutputs', + old_name='NEM_monthly_export_series_kwh', + new_name='net_metering_monthly_export_series_kwh', + ), + migrations.RenameField( + model_name='electrictariffoutputs', + old_name='WHL_electric_to_grid_series_kw', + new_name='wholesale_electric_to_grid_series_kw', + ), + migrations.RenameField( + model_name='electrictariffoutputs', + old_name='WHL_export_rate_series', + new_name='wholesale_export_rate_series', + ), + migrations.RenameField( + model_name='electrictariffoutputs', + old_name='WHL_monthly_export_cost_benefit_before_tax', + new_name='wholesale_monthly_export_cost_benefit_before_tax', + ), + migrations.RenameField( + model_name='electrictariffoutputs', + old_name='WHL_monthly_export_series_kwh', + new_name='wholesale_monthly_export_series_kwh', + ), + ] diff --git a/reoptjl/migrations/0113_rename_monthly_peak_kw_electricloadoutputs_monthly_peaks_kw.py b/reoptjl/migrations/0113_rename_monthly_peak_kw_electricloadoutputs_monthly_peaks_kw.py new file mode 100644 index 000000000..625c6323c --- /dev/null +++ b/reoptjl/migrations/0113_rename_monthly_peak_kw_electricloadoutputs_monthly_peaks_kw.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.25 on 2025-10-23 22:33 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0112_rename_nem_electric_to_grid_series_kw_electrictariffoutputs_net_metering_electric_to_grid_series_kw_'), + ] + + operations = [ + migrations.RenameField( + model_name='electricloadoutputs', + old_name='monthly_peak_kw', + new_name='monthly_peaks_kw', + ), + ] diff --git a/reoptjl/models.py b/reoptjl/models.py index d535fe252..b46c33482 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -1546,6 +1546,24 @@ class ElectricLoadOutputs(BaseModel, models.Model): null=True, blank=True, help_text="Annual energy consumption calculated by summing up load_series_kw. Does not include electric load for any new heating or cooling techs." ) + monthly_calculated_kwh = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Monthly energy consumption calculated by summing up load_series_kw. Does not include electric load for any new heating or cooling techs." + ) + monthly_peaks_kw = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Monthly peak energy demand determined from load_series_kw. Does not include electric load for any new heating or cooling techs." + ) + annual_peak_kw = models.FloatField( + null=True, blank=True, + help_text="Annual peak energy demand determined from load_series_kw. Does not include electric load for any new heating or cooling techs." + ) annual_electric_load_with_thermal_conversions_kwh = models.FloatField( null=True, blank=True, help_text="Total end-use electrical load, including electrified heating and cooling end-use load." @@ -1720,6 +1738,10 @@ class ElectricTariffInputs(BaseModel, models.Model): help_text=("Optional coincident peak demand charge that is applied to the max load during the time_steps " "specified in coincident_peak_load_active_time_steps") ) + urdb_metadata = models.JSONField( + null=True, blank=True, + help_text=("Utility rate meta data from Utility Rate Database API") + ) def clean(self): error_messages = {} @@ -2603,6 +2625,184 @@ class ElectricTariffOutputs(BaseModel, models.Model): related_name="ElectricTariffOutputs", primary_key=True ) + year_one_monthly_fixed_cost = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Year one fixed utility costs for each month." + ) + + year_one_electric_to_load_energy_cost_series_before_tax = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Series of cost of power purchased from grid to serve load in each timestep." + ) + monthly_electric_to_load_energy_cost_series_before_tax = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Series of monthly cost of power purchased from grid to serve loads." + ) + year_one_electric_to_storage_energy_cost_series_before_tax = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Series of cost of power purchased from grid to charge battery storage system in each timestep." + ) + monthly_electric_to_storage_energy_cost_series_before_tax = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Series of monthly cost of power purchased from grid to charge battery storage system." + ) + monthly_facility_demand_cost_series_before_tax = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Series of total monthly facility demand charges by month." + ) + monthly_gross_tou_demand_cost_series_before_tax = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Series of total time of use demand charges by month." + ) + net_metering_export_rate_series = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Series of compensation rate in each timestep for exporting power to grid using systems sized up to net metering limit." + ) + net_metering_electric_to_grid_series_kw = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Series of power exports to the grid up to net metering limit for each timestep." + ) + net_metering_monthly_export_series_kwh = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Year one cost of electricity consumed in each month." + ) + net_metering_monthly_export_cost_benefit_before_tax = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Year one cost of electricity consumed in each month." + ) + wholesale_export_rate_series = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Series of compensation rate in each timestep for exporting power to grid for wholesale." + ) + wholesale_electric_to_grid_series_kw = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Series of power exports to the grid for wholesale in each timestep." + ) + wholesale_monthly_export_series_kwh = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Series of monthly energy exports to grid under wholesale benefit." + ) + wholesale_monthly_export_cost_benefit_before_tax = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Series of monthly monetary benefit from exporting power to grid at wholesale rate." + ) + net_metering_excess_export_rate_series = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Series of compensation rate in each timestep for exporting power to grid beyond net metering limit." + ) + net_metering_excess_electric_to_grid_series_kw = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Series of power exports to the grid above net metering limit for each timestep." + ) + net_metering_excess_monthly_export_series_kwh = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Series of monthly energy exports to grid above net metering limit." + ) + net_metering_excess_monthly_export_cost_benefit_before_tax = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Series of monthly monetary benefit from exporting power to grid above net metering limit." + ) + + year_one_billed_energy_rate_series = models.JSONField( + null=True, blank=True, + help_text="Series of billed energy rates for each timestep in year one." + ) + + monthly_tou_demand_cost_series_before_tax = ArrayField( + models.FloatField( + null=True, blank=True + ), + default=list, + help_text="Series of total time of use demand charges for each month." + ) + + tou_demand_metrics = models.JSONField( + null=True, blank=True, + help_text="" + ) + + year_one_billed_facilitydemand_monthly_rate_tier_limits = models.JSONField( + null=True, blank=True, + help_text="" + ) + + year_one_billed_facilitydemand_monthly_rate_series = models.JSONField( + null=True, blank=True, + help_text="" + ) + + year_one_billed_tou_demand_rate_tier_limits = models.JSONField( + null=True, blank=True, + help_text="" + ) + + year_one_billed_energy_rate_tier_limits = models.JSONField( + null=True, blank=True, + help_text="" + ) + + year_one_billed_demand_rate_series = models.JSONField( + null=True, blank=True, + help_text="" + ) + year_one_energy_cost_before_tax = models.FloatField( null=True, blank=True, help_text="Optimal year one utility energy cost" diff --git a/reoptjl/src/process_results.py b/reoptjl/src/process_results.py index 20b26e209..22bf0a292 100644 --- a/reoptjl/src/process_results.py +++ b/reoptjl/src/process_results.py @@ -9,7 +9,7 @@ SteamTurbineOutputs, GHPInputs, GHPOutputs, ExistingChillerInputs, \ ElectricHeaterOutputs, ASHPSpaceHeaterOutputs, ASHPWaterHeaterOutputs, \ SiteInputs, ASHPSpaceHeaterInputs, ASHPWaterHeaterInputs, CSTInputs, CSTOutputs, PVInputs, \ - HighTempThermalStorageInputs, HighTempThermalStorageOutputs + HighTempThermalStorageInputs, HighTempThermalStorageOutputs, ElectricTariffInputs import numpy as np import sys import traceback as tb @@ -179,6 +179,9 @@ def update_inputs_in_database(inputs_to_update: dict, run_uuid: str) -> None: if inputs_to_update.get("HighTempThermalStorage") is not None: prune_update_fields(HighTempThermalStorageInputs, inputs_to_update["HighTempThermalStorage"]) HighTempThermalStorageInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["HighTempThermalStorage"]) + if inputs_to_update.get("ElectricTariff"): + prune_update_fields(ElectricTariffInputs, inputs_to_update["ElectricTariff"]) + ElectricTariffInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["ElectricTariff"]) except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() debug_msg = "exc_type: {}; exc_value: {}; exc_traceback: {}".format( diff --git a/reoptjl/test/posts/sector_defaults_post.json b/reoptjl/test/posts/sector_defaults_post.json index 1df5abbae..ca174468f 100644 --- a/reoptjl/test/posts/sector_defaults_post.json +++ b/reoptjl/test/posts/sector_defaults_post.json @@ -23,44 +23,24 @@ "ExistingBoiler": { "fuel_cost_per_mmbtu": 10.0 }, - "CHP": { - "prime_mover": "recip_engine", - "max_kw": 100, - "fuel_cost_per_mmbtu": 10.0, - "can_supply_steam_turbine": true - }, "PV": { "min_kw": 1000.0, "max_kw": 1000.0, "federal_itc_fraction": 0.2 }, - "SteamTurbine":{ - "min_kw": 100, - "max_kw": 100 - }, - "HighTempThermalStorage": { - "min_kwh": 10, - "max_kwh": 10, - "thermal_decay_rate_fraction": 0.0 - }, "Wind": { "min_kw": 100, "max_kw": 100 }, - "GHP": { - "require_ghp_purchase": true, - "building_sqft": 50000.0, - "can_serve_dhw": false, - "space_heating_efficiency_thermal_factor": 0.85, - "cooling_efficiency_thermal_factor": 0.6, - "ghpghx_inputs": [{ - "borehole_depth_ft": 400.0, - "simulation_years": 20, - "solver_eft_tolerance_f": 2.0, - "ghx_model": "TESS", - "tess_ghx_minimum_timesteps_per_hour": 1, - "max_sizing_iterations": 10, - "init_sizing_factor_ft_per_peak_ton": 300.0 - }] + "ElectricStorage": { + "min_kw": 50, + "max_kw": 50, + "min_kwh": 100, + "max_kwh": 100 + }, + "Boiler": { + "fuel_cost_per_mmbtu": 10.0, + "min_mmbtu_per_hour": 0.5, + "max_mmbtu_per_hour": 0.5 } } \ No newline at end of file diff --git a/reoptjl/test/test_job_endpoint.py b/reoptjl/test/test_job_endpoint.py index 6f74663aa..f880ba921 100644 --- a/reoptjl/test/test_job_endpoint.py +++ b/reoptjl/test/test_job_endpoint.py @@ -178,8 +178,6 @@ def test_sector_defaults_from_julia(self): self.assertEqual(saved_model_inputs.get(input_key), post[model_name][input_key]) else: # Check that default got assigned consistent with /sector_defaults - if model_name == "SteamTurbine" and input_key == "federal_itc_fraction": - continue #ST doesn't have federal_itc_fraction input self.assertEqual(saved_model_inputs.get(input_key), default_input_val) def test_chp_defaults_from_julia(self):