Skip to content

Commit

Permalink
Merge branch 'add-cst-ptc-swh' into add-cst-step1
Browse files Browse the repository at this point in the history
  • Loading branch information
zolanaj committed Sep 24, 2024
2 parents 16f4784 + 8b10e57 commit 4c21590
Show file tree
Hide file tree
Showing 15 changed files with 599 additions and 227 deletions.
6 changes: 3 additions & 3 deletions src/constraints/tech_constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ function add_tech_size_constraints(m, p; _n="")


#Site ground limit for PV and CSP combined; PV max size handled separately if this isn't present
if "ConcentratingSolar" in p.techs.all
if "CST" in p.techs.all
if !isempty(p.s.pvs)
@constraint(m, LandConstraint,
sum(pv.acres_per_kw * (p.pv_to_location[pv.name][:ground] + p.pv_to_location[pv.name][:both]) * m[Symbol("dvSize"*_n)][pv.name] for pv in p.s.pvs)
+ p.s.cst.acres_per_kw * m[Symbol("dvSize"*_n)]["ConcentratingSolar"] <= p.s.site.land_acres
+ p.s.cst.acres_per_kw * m[Symbol("dvSize"*_n)]["CST"] <= p.s.site.land_acres
)
else
@constraint(m, LandConstraint,
p.s.cst.acres_per_kw * m[Symbol("dvSize"*_n)]["ConcentratingSolar"] <= p.s.site.land_acres
p.s.cst.acres_per_kw * m[Symbol("dvSize"*_n)]["CST"] <= p.s.site.land_acres
)
end
end
Expand Down
16 changes: 8 additions & 8 deletions src/constraints/thermal_tech_constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function add_heating_tech_constraints(m, p; _n="")
# Constraint (7_heating_prod_size): Production limit based on size for non-electricity-producing heating techs
if !isempty(setdiff(p.techs.heating, union(p.techs.elec, p.techs.ghp)))
@constraint(m, [t in setdiff(p.techs.heating, union(p.techs.elec, p.techs.ghp)), ts in p.time_steps],
sum(m[Symbol("dvHeatingProduction"*_n)][t,q,ts] for q in p.heating_loads) <= m[Symbol("dvSize"*_n)][t] * p.heating_cf[t][ts]
sum(m[Symbol("dvHeatingProduction"*_n)][t,q,ts] for q in p.heating_loads) <= p.heating_cf[t][ts] * m[Symbol("dvSize"*_n)][t]
)
end
# Constraint (7_heating_load_compatability): Set production variables for incompatible heat loads to zero
Expand Down Expand Up @@ -107,22 +107,22 @@ function add_heating_tech_constraints(m, p; _n="")
end
end

if "ConcentratingSolar" in p.techs.electric_heater
if "CST" in p.techs.electric_heater
@constraint(m, CSTHeatProduction[ts in p.time_steps],
sum(m[Symbol("dvHeatingProduction"*_n)]["ConcentratingSolar",q,ts] for q in p.heating_loads) == p.heating_cf["ConcentratingSolar"][ts] * m[Symbol("dvSize"*_n)]["ConcentratingSolar"]
sum(m[Symbol("dvHeatingProduction"*_n)]["CST",q,ts] for q in p.heating_loads) == p.heating_cf["CST"][ts] * m[Symbol("dvSize"*_n)]["CST"]
)
if p.s.cst.charge_storage_only
#assume sensible TES first, and hot water otherwise.
if "HotSensibleTes" in p.s.storage.types.hot
@constraint(m, ConcentratingSolarToStorageOnly[q in p.heating_loads, ts in p.time_steps],
m[Symbol("dvHeatingProduction"*_n)]["ConcentratingSolar",q,ts] == m[Symbol("dvProductionToWaste"*_n)]["ConcentratingSolar",q,ts] + m[Symbol("dvHeatToStorage"*_n)]["HotSensibleTes","ConcentratingSolar",q,ts]
@constraint(m, CSTToStorageOnly[q in p.heating_loads, ts in p.time_steps],
m[Symbol("dvHeatingProduction"*_n)]["CST",q,ts] == m[Symbol("dvProductionToWaste"*_n)]["CST",q,ts] + m[Symbol("dvHeatToStorage"*_n)]["HotSensibleTes","CST",q,ts]
)
elseif "HotThermalStorage" in p.s.storage.types.hot
@constraint(m, ConcentratingSolarToStorageOnly[q in p.heating_loads, ts in p.time_steps],
m[Symbol("dvHeatingProduction"*_n)]["ConcentratingSolar",q,ts] == m[Symbol("dvProductionToWaste"*_n)]["ConcentratingSolar",q,ts] + m[Symbol("dvHeatToStorage"*_n)]["HotThermalStorage","ConcentratingSolar",q,ts]
@constraint(m, CSTToStorageOnly[q in p.heating_loads, ts in p.time_steps],
m[Symbol("dvHeatingProduction"*_n)]["CST",q,ts] == m[Symbol("dvProductionToWaste"*_n)]["CST",q,ts] + m[Symbol("dvHeatToStorage"*_n)]["HotThermalStorage","CST",q,ts]
)
else
@warn "ConcentratingSolar.charge_storage_only is set to True, but no hot storage technologies exist."
@warn "CST.charge_storage_only is set to True, but no hot storage technologies exist."
end
end
end
Expand Down
26 changes: 13 additions & 13 deletions src/core/cst.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
struct ConcentratingSolar <: AbstractThermalTech
struct CST <: AbstractThermalTech
min_kw::Real
max_kw::Real
capacity_factor_series::AbstractVector{<:Real}
Expand All @@ -22,14 +22,14 @@ struct ConcentratingSolar <: AbstractThermalTech
end

"""
ConcentratingSolar
CST
If a user provides the `ConcentratingSolar` key then the optimal scenario has the option to purchase this new
`ConcentratingSolar` technology to meet compatible heating loads in addition to using the `ExistingBoiler`
If a user provides the `CST` key then the optimal scenario has the option to purchase this new
`CST` technology to meet compatible heating loads in addition to using the `ExistingBoiler`
to meet the heating load(s).
```julia
function ConcentratingSolar(;
function CST(;
min_mmbtu_per_hour::Real = 0.0, # Minimum thermal power size
max_mmbtu_per_hour::Real = BIG_NUMBER, # Maximum thermal power size
capacity_factor_series::AbstractVector{<:Real} = Float64[], production factor
Expand All @@ -45,15 +45,15 @@ function ConcentratingSolar(;
can_serve_dhw::Bool = true # If Boiler can supply heat to the domestic hot water load
can_serve_space_heating::Bool = true # If Boiler can supply heat to the space heating load
can_serve_process_heat::Bool = true # If Boiler can supply heat to the process heating load
charge_storage_only::Bool = true # If ConcentratingSolar can only supply hot TES (i.e., cannot meet load directly)
charge_storage_only::Bool = true # If CST can only supply hot TES (i.e., cannot meet load directly)
emissions_factor_lb_CO2_per_mmbtu::Real = get(FUEL_DEFAULTS["emissions_factor_lb_CO2_per_mmbtu"],fuel_type,0)
emissions_factor_lb_NOx_per_mmbtu::Real = get(FUEL_DEFAULTS["emissions_factor_lb_NOx_per_mmbtu"],fuel_type,0)
emissions_factor_lb_SO2_per_mmbtu::Real = get(FUEL_DEFAULTS["emissions_factor_lb_SO2_per_mmbtu"],fuel_type,0)
emissions_factor_lb_PM25_per_mmbtu::Real = get(FUEL_DEFAULTS["emissions_factor_lb_PM25_per_mmbtu"],fuel_type,0)
)
```
"""
function ConcentratingSolar(;
function CST(;
min_kw::Real = 0.0,
max_kw::Real = BIG_NUMBER,
capacity_factor_series::AbstractVector{<:Real} = Float64[],
Expand All @@ -77,15 +77,15 @@ function ConcentratingSolar(;
)

if isnothing(tech_type)
throw(@error("ConcentratingSolar.tech_type is a required input but not provided."))
throw(@error("CST.tech_type is a required input but not provided."))
elseif !(tech_type in CST_TYPES)
throw(@error("ConcentratingSolar.tech_type value is invalid."))
throw(@error("CST.tech_type value is invalid."))
end
if isempty(capacity_factor_series)
throw(@error("ConcentratingSolar.capacity_factor_series is a required input when modeling a heating load which is served by the ConcentratedSolar system in the optimal case"))
throw(@error("CST.capacity_factor_series is a required input when modeling a heating load which is served by the ConcentratedSolar system in the optimal case"))
end
if isempty(elec_consumption_factor_series)
throw(@error("ConcentratingSolar.elec_consumption_factor_series is a required input when modeling a heating load which is served by the ConcentratedSolar system in the optimal case"))
throw(@error("CST.elec_consumption_factor_series is a required input when modeling a heating load which is served by the ConcentratedSolar system in the optimal case"))
end

defaults = get_cst_defaults(tech_type)
Expand Down Expand Up @@ -123,7 +123,7 @@ function ConcentratingSolar(;
charge_storage_only = defaults["charge_storage_only"]
end

ConcentratingSolar(
CST(
min_kw,
max_kw,
capacity_factor_series,
Expand Down Expand Up @@ -157,7 +157,7 @@ inputs
tech_type::String -- identifier of CST technology type
returns
cst_defaults::Dict -- Dictionary containing defaults for ConcentratingSolar technology type
cst_defaults::Dict -- Dictionary containing defaults for CST technology type
"""
function get_cst_defaults(tech_type::String="")
if !(tech_type in CST_TYPES)
Expand Down
80 changes: 48 additions & 32 deletions src/core/cst_ssc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -97,19 +97,25 @@ function get_weatherdata(lat::Float64,lon::Float64,debug::Bool)
return weatherdata
end

function normalize_response(response,case_data,rated_power)
model = case_data["ConcentratingSolar"]["tech_type"]
function normalize_response(thermal_power_produced,case_data)
model = case_data["CST"]["tech_type"]
if model =="ptc"
heat_sink = case_data["ConcentratingSolar"]["q_pb_design"]
SM = 2.5
return response ./ (SM * heat_sink)
heat_sink = case_data["CST"]["SSC_Inputs"]["q_pb_design"]
rated_power_per_area = 39.37 / 60000.0 # MWt / m2, TODO: update with median values from SAM params
if case_data["CST"]["SSC_Inputs"]["use_solar_mult_or_aperture_area"] > 0
rated_power = rated_power_per_area * case_data["CST"]["SSC_Inputs"]["specified_total_aperture"]
else
rated_power = 3.0 * heat_sink
end
end

thermal_power_produced_norm = thermal_power_produced ./ (rated_power)
thermal_power_produced_norm[thermal_power_produced_norm .< 0] .= 0 # remove negative values
return thermal_power_produced_norm
end

# function run_ssc(model::String,lat::Float64,lon::Float64,inputs::Dict,outputs::Vector)
function run_ssc(case_data::Dict)
model = case_data["ConcentratingSolar"]["tech_type"]
model = case_data["CST"]["tech_type"]
### Maps STEP 1 model names to specific SSC modules
model_ssc = Dict(
"mst" => "tcsmolten_salt",
Expand All @@ -130,13 +136,13 @@ function run_ssc(case_data::Dict)
"mst" => ["T_htf_cold_des","T_htf_hot_des","q_pb_design","dni_des","csp.pt.sf.fixed_land_area","land_max","land_min","h_tower","rec_height","rec_htf","cold_tank_Thtr","hot_tank_Thtr"]
)
# First set user defined inputs to default just in case
defaults_file = joinpath(@__DIR__,"sam","defaults","defaults_step1_" * model * ".json") ## TODO update this to step 1 default jsons once they're ready
defaults_file = joinpath(@__DIR__,"sam","defaults","defaults_" * model_ssc[model] * ".json") ## TODO update this to step 1 default jsons once they're ready
defaults = JSON.parsefile(defaults_file)
for i in user_defined_inputs_list[model]
if (i == "tilt") || (i == "lat")
user_defined_inputs[i] = lat
else
user_defined_inputs[i] = case_data["ConcentratingSolar"]["SSC_Inputs"][i]
user_defined_inputs[i] = case_data["CST"]["SSC_Inputs"][i]
end
end

Expand All @@ -148,15 +154,15 @@ function run_ssc(case_data::Dict)
else
### Setup SSC
global hdl = nothing
libfile = "ssc.dll"
libfile = "ssc_new.dll"
global hdl = joinpath(@__DIR__, "sam", libfile)
ssc_module = @ccall hdl.ssc_module_create(model_ssc[model]::Cstring)::Ptr{Cvoid}
data = @ccall hdl.ssc_data_create()::Ptr{Cvoid} # data pointer
@ccall hdl.ssc_module_exec_set_print(1::Cint)::Cvoid # change to 1 to print outputs/errors (for debugging)

### Import defaults
# defaults_file = joinpath(@__DIR__,"sam","defaults","defaults_" * model_ssc[model] * "_step1.json")
defaults_file = joinpath(@__DIR__,"sam","defaults","defaults_step1_" * model * ".json")
defaults_file = joinpath(@__DIR__,"sam","defaults","defaults_" * model_ssc[model] * "_step1.json")
defaults = JSON.parsefile(defaults_file)
set_ssc_data_from_dict(defaults,model,data)

Expand All @@ -171,14 +177,15 @@ function run_ssc(case_data::Dict)
### Execute simulation
test = @ccall hdl.ssc_module_exec(ssc_module::Ptr{Cvoid}, data::Ptr{Cvoid})::Cint
print(test)

### Retrieve results
### SSC output names for the thermal production and electrical consumption profiles, thermal power rating and solar multiple
outputs_dict = Dict(
"mst" => ["Q_thermal","P_tower_pump","q_pb_design","solarm"], # locked in [W]
"lf" => ["q_dot_to_heat_sink"], # locked in [W]
"ptc" => ["q_dot_htf_sf_out","P_loss","q_pb_design",2.5], # locked in [MWt]
"swh_flatplate" => ["Q_useful","P_pump","system_capacity",1.0], # kW, kW, kW
"swh_evactube" => ["Q_useful","P_pump","system_capacity",1.0] # kW, kW, kW
"mst" => ["Q_thermal","P_tower_pump",0.0,"q_pb_design","solarm"], # locked in [W]
"lf" => ["q_dot_to_heat_sink","W_dot_heat_sink_pump","W_dot_parasitic_tot","q_pb_design",1.0], # locked in [W]
"ptc" => ["q_dot_htf_sf_out","P_loss",0.0,"q_pb_design",3.0], # locked in [MWt]
"swh_flatplate" => ["Q_useful","P_pump",0.0,"system_capacity",1.0], # kW, kW, kW
"swh_evactube" => ["Q_useful","P_pump",0.0,"system_capacity",1.0] # kW, kW, kW
)
thermal_conversion_factor = Dict(
"mst" => 1, # locked in [W]
Expand All @@ -199,38 +206,46 @@ function run_ssc(case_data::Dict)
len = 8760
len_ref = Ref(len)
thermal_production_response = @ccall hdl.ssc_data_get_array(data::Ptr{Cvoid}, outputs[1]::Cstring, len_ref::Ptr{Cvoid})::Ptr{Float64}
electrical_consumption_response = @ccall hdl.ssc_data_get_array(data::Ptr{Cvoid}, outputs[2]::Cstring, len_ref::Ptr{Cvoid})::Ptr{Float64}
# electrical_consumption_response = @ccall hdl.ssc_data_get_array(data::Ptr{Cvoid}, outputs[2]::Cstring, len_ref::Ptr{Cvoid})::Ptr{Float64}
thermal_production = []
elec_consumption = []
# elec_consumption = []
for i in 1:8760
push!(thermal_production,unsafe_load(thermal_production_response,i)) # For array type outputs
push!(elec_consumption,unsafe_load(electrical_consumption_response,i)) # For array type outputs
# push!(elec_consumption,unsafe_load(electrical_consumption_response,i)) # For array type outputs
end
if outputs[3] in keys(user_defined_inputs)
tpow = user_defined_inputs[outputs[3]]
# if typeof(outputs[3]) == String
# secondary_consumption_response = @ccall hdl.ssc_data_get_array(data::Ptr{Cvoid}, outputs[3]::Cstring, len_ref::Ptr{Cvoid})::Ptr{Float64}
# for i in 1:8760
# elec_consumption[i] += unsafe_load(secondary_consumption_response, i)
# end
# end
if outputs[4] in keys(user_defined_inputs)
tpow = user_defined_inputs[outputs[4]]
else
tpow = defaults[outputs[3]]
tpow = defaults[outputs[4]]
end
if typeof(outputs[4]) != String
mult = outputs[4]
elseif outputs[4] in keys(user_defined_inputs)
mult = user_defined_inputs[outputs[4]]
if typeof(outputs[5]) != String
mult = outputs[5]
elseif outputs[5] in keys(user_defined_inputs)
mult = user_defined_inputs[outputs[5]]
else
mult = defaults[outputs[4]]
mult = defaults[outputs[5]]
end
println("tpow ", tpow, " mult ", mult)
# println("tpow ", tpow, " mult ", mult)
rated_power = tpow * mult

tcf = thermal_conversion_factor[model]
ecf = elec_conversion_factor[model]
#c_response = @ccall hdl.ssc_data_get_number(data::Ptr{Cvoid}, k::Cstring, len_ref::Ptr{Cvoid})::Ptr{Float64}
# print(c_response)
thermal_production_norm = thermal_production .* tcf ./ rated_power # normalize_response(thermal_production_response,case_data,rated_power)
electric_consumption_norm = elec_consumption .* ecf ./ rated_power
if model == "ptc"
thermal_production_norm = normalize_response(thermal_production, case_data)
else
thermal_production_norm = thermal_production .* tcf ./ rated_power
end
electric_consumption_norm = zeros(8760) #elec_consumption .* ecf ./ rated_power
# R[k] = response_norm
# end
println(thermal_production_norm[1:100])
println(electric_consumption_norm[1:100])
### Free SSC
@ccall hdl.ssc_module_free(ssc_module::Ptr{Cvoid})::Cvoid
@ccall hdl.ssc_data_free(data::Ptr{Cvoid})::Cvoid
Expand All @@ -243,6 +258,7 @@ function run_ssc(case_data::Dict)
end
R["error"] = error
#return R

end
return R
end
16 changes: 8 additions & 8 deletions src/core/reopt_inputs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ function setup_tech_inputs(s::AbstractScenario, time_steps)
cooling_cf["GHP"] = ones(length(time_steps))
end

if "ConcentratingSolar" in techs.all
if "CST" in techs.all
setup_cst_inputs(s, max_sizes, min_sizes, cap_cost_slope, om_cost_per_kw, heating_cop, heating_cf)
end

Expand Down Expand Up @@ -944,14 +944,14 @@ function setup_electric_heater_inputs(s, max_sizes, min_sizes, cap_cost_slope, o
end

function setup_cst_inputs(s, max_sizes, min_sizes, cap_cost_slope, om_cost_per_kw, heating_cop, heating_cf)
max_sizes["ConcentratingSolar"] = s.cst.max_kw
min_sizes["ConcentratingSolar"] = s.cst.min_kw
om_cost_per_kw["ConcentratingSolar"] = s.cst.om_cost_per_kw
heating_cop["ConcentratingSolar"] = 1000*ones(s.settings.time_steps_per_hour*8760) # TODO: merge ASHP developments to make heating_cop a time series, and import the electrical consumption from SAM.
heating_cf["ConcentratingSolar"] = s.cst.capacity_factor_series
max_sizes["CST"] = s.cst.max_kw
min_sizes["CST"] = s.cst.min_kw
om_cost_per_kw["CST"] = s.cst.om_cost_per_kw
heating_cop["CST"] = 1000*ones(s.settings.time_steps_per_hour*8760) # TODO: merge ASHP developments to make heating_cop a time series, and import the electrical consumption from SAM.
heating_cf["CST"] = s.cst.capacity_factor_series

if s.cst.macrs_option_years in [5, 7]
cap_cost_slope["ConcentratingSolar"] = effective_cost(;
cap_cost_slope["CST"] = effective_cost(;
itc_basis = s.cst.installed_cost_per_kw,
replacement_cost = 0.0,
replacement_year = s.financial.analysis_years,
Expand All @@ -964,7 +964,7 @@ function setup_cst_inputs(s, max_sizes, min_sizes, cap_cost_slope, om_cost_per_k
rebate_per_kw = 0.0
)
else
cap_cost_slope["ConcentratingSolar"] = s.cst.installed_cost_per_kw
cap_cost_slope["CST"] = s.cst.installed_cost_per_kw
end

end
Expand Down
50 changes: 0 additions & 50 deletions src/core/sam/defaults/defaults_step1_swh_flatplate.json

This file was deleted.

Loading

0 comments on commit 4c21590

Please sign in to comment.