Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creation of ActorPowerNeeds initial commit #251

Merged
merged 12 commits into from
Mar 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/FUSE.jl
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ include(joinpath("actors", "limits_actor.jl"))

include(joinpath("actors", "balance_plant", "heat_transfer_actor.jl"))
include(joinpath("actors", "balance_plant", "thermal_cycle_actor.jl"))
include(joinpath("actors", "balance_plant", "power_needs_actor.jl"))
include(joinpath("actors", "balance_plant", "balance_of_plant_actor.jl"))
include(joinpath("actors", "balance_plant", "balance_of_plant_plot.jl"))

Expand Down
98 changes: 11 additions & 87 deletions src/actors/balance_plant/balance_of_plant_actor.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
Base.@kwdef mutable struct FUSEparameters__ActorBalanceOfPlant{T} <: ParametersActor where {T<:Real}
_parent::WeakRef = WeakRef(Nothing)
_name::Symbol = :not_set
needs_model::Switch{Symbol} = Switch(Symbol, [:gasc, :EU_DEMO], "-", "Power plant electrical needs model"; default=:EU_DEMO)
generator_conversion_efficiency::Entry{T} = Entry(T, "-", "Efficiency of the steam cycle, thermal to electric"; default=0.9)
generator_conversion_efficiency::Entry{T} = Entry(T, "-", "Efficiency of the generator"; default=0.95) # Appl. Therm. Eng. 76 (2015) 123–133, https://doi.org/10.1016/j.applthermaleng.2014.10.093
do_plot::Entry{Bool} = Entry(Bool, "-", "plot"; default=false)
end

Expand All @@ -15,16 +14,13 @@ mutable struct ActorBalanceOfPlant <: FacilityAbstractActor
act::ParametersAllActors
thermal_cycle_actor::ActorThermalCycle
IHTS_actor::ActorHeatTransfer
power_needs_actor::ActorPowerNeeds
end

"""
ActorBalanceOfPlant(dd::IMAS.dd, act::ParametersAllActors; kw...)

Balance of plant actor that estimates the net electrical power output by comparing the balance of plant electrical needs with the electricity generated from the thermal cycle.

* `needs_model = :gasc` simply assumes that the power to balance a plant is 7% of the electricity generated.
* `needs_model = :EU_DEMO` subdivides the power plant electrical needs to [:cryostat, :tritium_handling, :pumping] using EU-DEMO numbers.

!!! note
Stores data in `dd.balance_of_plant`
"""
Expand All @@ -45,9 +41,10 @@ function ActorBalanceOfPlant(dd::IMAS.dd, par::FUSEparameters__ActorBalanceOfPla

breeder_hi_temp, breeder_low_temp, cycle_tmax = ihts_specs(act.ActorThermalCycle.power_cycle_type)

IHTS_actor = ActorHeatTransfer(dd, act; breeder_hi_temp, breeder_low_temp)
thermal_cycle_actor = ActorThermalCycle(dd, act; Tmax=cycle_tmax, rp=3.0)
return ActorBalanceOfPlant(dd, par, act, thermal_cycle_actor, IHTS_actor)
IHTS_actor = ActorHeatTransfer(dd, act.ActorHeatTransfer, act; breeder_hi_temp, breeder_low_temp)
thermal_cycle_actor = ActorThermalCycle(dd, act.ActorThermalCycle, act; Tmax=cycle_tmax, rp=3.0)
power_needs_actor = ActorPowerNeeds(dd, act.ActorPowerNeeds, act)
return ActorBalanceOfPlant(dd, par, act, thermal_cycle_actor, IHTS_actor, power_needs_actor)
end

function ihts_specs(power_cycle_type::Symbol)
Expand All @@ -67,38 +64,11 @@ function _step(actor::ActorBalanceOfPlant)
bop = dd.balance_of_plant

bop_thermal = bop.thermal_cycle
bop_thermal.generator_conversion_efficiency = par.generator_conversion_efficiency .* ones(length(bop.time))
bop_thermal.power_electric_generated = bop_thermal.net_work .* par.generator_conversion_efficiency .* ones(length(bop.time))

@ddtime(bop_thermal.total_useful_heat_power = @ddtime(bop.heat_transfer.wall.heat_delivered) + @ddtime(bop.heat_transfer.divertor.heat_delivered) + @ddtime(bop.heat_transfer.breeder.heat_delivered))

bop_electric = bop.power_electric_plant_operation

## heating and current drive systems
sys = resize!(bop_electric.system, "name" => "H&CD", "index" => 1)
sys.power = zeros(length(bop.time))
for (idx, hcd_system) in enumerate(intersect([:nbi, :ec_launchers, :ic_antennas, :lh_antennas], keys(dd)))
sub_sys = resize!(sys.subsystem, "name" => string(hcd_system), "index" => idx)
sub_sys.power = electricity(getproperty(dd, hcd_system), bop.time)
sys.power .+= sub_sys.power
end

## balance of plant systems
if par.needs_model == :gasc
sys = resize!(bop_electric.system, "name" => "BOP_gasc", "index" => 2)
sys.power = 0.07 .* bop_thermal.power_electric_generated

elseif par.needs_model == :EU_DEMO
# More realistic DEMO numbers
bop_systems = [:cryostat, :tritium_handling, :pumping, :pf_active] # index 2 : 5
for (idx, system) in enumerate(bop_systems)
sys = resize!(bop_electric.system, "name" => string(system), "index" => (idx + 1))
sys.power = electricity(system, bop.time)
end
else
error("ActorBalanceOfPlant: par.needs_model = $(par.needs_model) not recognized")
end
@ddtime(bop_thermal.generator_conversion_efficiency = par.generator_conversion_efficiency)

finalize(step(actor.IHTS_actor))
finalize(step(actor.thermal_cycle_actor))
finalize(step(actor.power_needs_actor))

if par.do_plot
core = sys_coords(dd)
Expand Down Expand Up @@ -175,50 +145,4 @@ function _step(actor::ActorBalanceOfPlant)
end

return actor
end

function heating_and_current_drive_calc(system_unit, time_array::Vector{<:Real})
power_electric_total = zeros(length(time_array))
for item_unit in system_unit
efficiency = prod([getproperty(item_unit.efficiency, i) for i in keys(item_unit.efficiency)])
power_electric_total .+= IMAS.get_time_array(item_unit.power_launched, :data, time_array, :constant) ./ efficiency
end
return power_electric_total
end

function electricity(nbi::IMAS.nbi, time_array::Vector{<:Real})
return heating_and_current_drive_calc(nbi.unit, time_array)
end

function electricity(ec_launchers::IMAS.ec_launchers, time_array::Vector{<:Real})
return heating_and_current_drive_calc(ec_launchers.beam, time_array)
end

function electricity(ic_antennas::IMAS.ic_antennas, time_array::Vector{<:Real})
return heating_and_current_drive_calc(ic_antennas.antenna, time_array)
end

function electricity(lh_antennas::IMAS.lh_antennas, time_array::Vector{<:Real})
return heating_and_current_drive_calc(lh_antennas.antenna, time_array)
end

function electricity(symbol::Symbol, time_array::Vector{<:Real})
return electricity(Val{symbol}, time_array)
end

# Dummy functions values taken from DEMO 2017 https://iopscience.iop.org/article/10.1088/0029-5515/57/1/016011
function electricity(::Type{Val{:cryostat}}, time_array::Vector{<:Real})
return 30e6 .* ones(length(time_array)) # MWe
end

function electricity(::Type{Val{:tritium_handling}}, time_array::Vector{<:Real})
return 15e6 .* ones(length(time_array)) # MWe
end

function electricity(::Type{Val{:pumping}}, time_array::Vector{<:Real})
return 80e6 .* ones(length(time_array)) # MWe (Note this should not be a constant!)
end

function electricity(::Type{Val{:pf_active}}, time_array::Vector{<:Real})
return 0e6 .* ones(length(time_array)) # MWe (Note this should not be a constant!)
end
end
23 changes: 12 additions & 11 deletions src/actors/balance_plant/heat_transfer_actor.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,6 @@
#= ================= =#
# ACTOR FOR THE INTERMEDIATE HEAT TRANSFER SYSTEM

mutable struct ActorHeatTransfer <: FacilityAbstractActor
dd::IMAS.dd
par::ParametersActor
function ActorHeatTransfer(dd::IMAS.dd, par::ParametersActor; kw...)
logging_actor_init(ActorHeatTransfer)
par = par(kw...)
return new(dd, par)
end
end

const coolant_fluid = [:He, :PbLi]

Base.@kwdef mutable struct FUSEparameters__ActorHeatTransfer{T} <: ParametersActor where {T<:Real}
Expand Down Expand Up @@ -40,6 +30,11 @@ Base.@kwdef mutable struct FUSEparameters__ActorHeatTransfer{T} <: ParametersAct
divertor_coolant::Switch{Symbol} = Switch(Symbol, coolant_fluid, "-", "Breeder coolant fluid"; default=:He)
end

mutable struct ActorHeatTransfer <: FacilityAbstractActor
dd::IMAS.dd
par::FUSEparameters__ActorHeatTransfer
end

"""
ActorHeatTransfer(dd::IMAS.dd, act::ParametersAllActors; kw...)

Expand All @@ -54,6 +49,12 @@ function ActorHeatTransfer(dd::IMAS.dd, act::ParametersAllActors; kw...)
return actor
end

function ActorHeatTransfer(dd::IMAS.dd, par::FUSEparameters__ActorHeatTransfer, act::ParametersAllActors; kw...)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you're missing the act field in the ActorHeatTransfer structure, though that does not appear to be used, so why have it?

logging_actor_init(ActorHeatTransfer)
par = par(kw...)
return ActorHeatTransfer(dd, par)
end

function _step(actor::ActorHeatTransfer)
dd = actor.dd
par = actor.par
Expand Down Expand Up @@ -155,7 +156,7 @@ function pbLi_props(Temperature)
return [specific_heat, density]
end

function gas_circulator(rp, Tin, effC, mflow, nstages = 1)
function gas_circulator(rp, Tin, effC, mflow, nstages=1)
cp = 5.1926e3
cv = 3.1156e3
a1c = (effC * (rp^(1.0 / nstages))^(cv / cp) - (rp^(1.0 / nstages))^(cv / cp) + rp^(1.0 / nstages)) / (effC * (rp^(1.0 / nstages))^(cv / cp))
Expand Down
148 changes: 148 additions & 0 deletions src/actors/balance_plant/power_needs_actor.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#= =============== =#
# ActorPowerNeeds #
#= =============== =#
Base.@kwdef mutable struct FUSEparameters__ActorPowerNeeds{T} <: ParametersActor where {T<:Real}
_parent::WeakRef = WeakRef(Nothing)
_name::Symbol = :not_set
model::Switch{Symbol} = Switch(Symbol, [:gasc, :EU_DEMO, :FUSE], "-", "Power plant electrical needs model"; default=:FUSE)
do_plot::Entry{Bool} = Entry(Bool, "-", "plot"; default=false)
end

mutable struct ActorPowerNeeds <: FacilityAbstractActor
dd::IMAS.dd
par::FUSEparameters__ActorPowerNeeds
end

"""
ActorPowerNeeds(dd::IMAS.dd, act::ParametersAllActors; kw...)

Power needs actor that calculates the needed power to operate the plant

* `model = :gasc` simply assumes that the power to balance a plant is 7% of the electricity generated.
* `model = :EU_DEMO` subdivides the power plant electrical needs to [:cryostat, :tritium_handling, :pumping] using EU-DEMO numbers.
* `model = :FUSE` subdivides power plant needs and self-consistently calculates the power needs according to FUSE
!!! note
Stores data in `dd.balance_of_plant.power_electric_plant_operation`
"""
function ActorPowerNeeds(dd::IMAS.dd, act::ParametersAllActors; kw...)
par = act.ActorPowerNeeds(kw...)
actor = ActorPowerNeeds(dd, par, act)
step(actor)
finalize(actor)
return actor
end

function ActorPowerNeeds(dd::IMAS.dd, par::FUSEparameters__ActorPowerNeeds, act::ParametersAllActors; kw...)
logging_actor_init(ActorPowerNeeds)
par = par(kw...)
return ActorPowerNeeds(dd, par)
end

function _step(actor::ActorPowerNeeds)
dd = actor.dd
par = actor.par
bop = dd.balance_of_plant

bop_electric = bop.power_electric_plant_operation

## heating and current drive systems
sys = resize!(bop_electric.system, "name" => "H&CD", "index" => 1)
sys.power = zeros(length(bop.time))
for (idx, hcd_system) in enumerate(intersect([:nbi, :ec_launchers, :ic_antennas, :lh_antennas], keys(dd)))
sub_sys = resize!(sys.subsystem, "name" => string(hcd_system), "index" => idx)
@ddtime(sub_sys.power = electricity(getproperty(dd, hcd_system)))
sys.power .+= sub_sys.power
end

## Other subsytems based on model
if par.model == :gasc
sys = resize!(bop_electric.system, "name" => "BOP_gasc", "index" => 2)
sys.power = 0.07 .* bop_thermal.power_electric_generated

elseif par.model == :FUSE

# For now electrical needs same as DEMO but pumping self-consistent
bop_systems = [:cryostat, :tritium_handling, :pf_active]
for (idx, system) in enumerate(bop_systems)
if system == :pf_active
idx += 1
end
sys = resize!(bop_electric.system, "name" => string(system), "index" => (idx + 1))
@ddtime(sys.power = electricity(system))
end

sys = resize!(bop_electric.system, "name" => "pumping", "index" => 5)
@ddtime(sys.power = electricity(:pumping, dd.balance_of_plant))

elseif par.model == :EU_DEMO
# More realistic DEMO numbers
bop_systems = [:cryostat, :tritium_handling, :pumping, :pf_active] # index 2 : 5
for (idx, system) in enumerate(bop_systems)
sys = resize!(bop_electric.system, "name" => string(system), "index" => (idx + 1))
sys.power = electricity(system)
end
end
return actor
end

function heating_and_current_drive_calc(system_unit)
power_electric_total = 0.0
for item_unit in system_unit
efficiency = prod([getproperty(item_unit.efficiency, i) for i in keys(item_unit.efficiency)])
power_electric_total += @ddtime(item_unit.power_launched.data) / efficiency
end
return power_electric_total
end

function electricity(nbi::IMAS.nbi)
return heating_and_current_drive_calc(nbi.unit)
end

function electricity(ec_launchers::IMAS.ec_launchers)
return heating_and_current_drive_calc(ec_launchers.beam)
end

function electricity(ic_antennas::IMAS.ic_antennas)
return heating_and_current_drive_calc(ic_antennas.antenna)
end

function electricity(lh_antennas::IMAS.lh_antennas)
return heating_and_current_drive_calc(lh_antennas.antenna)
end

function electricity(symbol::Symbol)
return electricity(Val{symbol})
end

#= =================== =#
# EU DEMO electricity #
#= =================== =#

# Dummy functions values taken from DEMO 2017 https://iopscience.iop.org/article/10.1088/0029-5515/57/1/016011
function electricity(::Type{Val{:cryostat}})
return 30e6 # We
end

function electricity(::Type{Val{:tritium_handling}})
return 15e6# We
end

function electricity(::Type{Val{:pumping}})
return 80e6 # We (Note this should not be a constant!)
end

function electricity(::Type{Val{:pf_active}})
return 0.0 # We (Note this should not be a constant!)
end

#= =================== =#
# FUSE electricity #
#= =================== =#

function electricity(::Type{Val{:pumping}}, bop::IMAS.balance_of_plant)
return @ddtime(bop.heat_transfer.breeder.circulator_power) + @ddtime(bop.heat_transfer.divertor.circulator_power) + @ddtime(bop.heat_transfer.wall.circulator_power)
end

function electricity(symbol::Symbol, bop::IMAS.balance_of_plant)
return electricity(Val{symbol}, bop)
end
24 changes: 12 additions & 12 deletions src/actors/balance_plant/thermal_cycle_actor.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,6 @@
# SIMPLE rankine
# COMBINED BRAYTON RANKINE
#= =================== =#
mutable struct ActorThermalCycle <: FacilityAbstractActor
dd::IMAS.dd
par::ParametersActor
act::ParametersAllActors
function ActorThermalCycle(dd::IMAS.dd, par::ParametersActor, act; kw...)
logging_actor_init(ActorThermalCycle)
par = par(kw...)
return new(dd, par, act)
end
end

Base.@kwdef mutable struct FUSEparameters__ActorThermalCycle{T} <: ParametersActor where {T<:Real}
_parent::WeakRef = WeakRef(Nothing)
Expand All @@ -31,6 +21,17 @@ Base.@kwdef mutable struct FUSEparameters__ActorThermalCycle{T} <: ParametersAct
do_plot::Entry{Bool} = Entry(Bool, "-", "plot"; default=false)
end

mutable struct ActorThermalCycle <: FacilityAbstractActor
dd::IMAS.dd
par::FUSEparameters__ActorThermalCycle
act::ParametersAllActors
function ActorThermalCycle(dd::IMAS.dd, par::FUSEparameters__ActorThermalCycle, act::ParametersAllActors; kw...)
logging_actor_init(ActorThermalCycle)
par = par(kw...)
return new(dd, par, act)
end
end

"""
ActorThermalCycle(dd::IMAS.dd, act::ParametersAllActors; kw...)

Expand All @@ -51,7 +52,6 @@ end
function _step(actor::ActorThermalCycle)
dd = actor.dd
par = actor.par
act = actor.act
bop = dd.balance_of_plant
ihts = bop.heat_transfer
wall = ihts.wall
Expand All @@ -61,7 +61,7 @@ function _step(actor::ActorThermalCycle)
bop.power_cycle_type = string(par.power_cycle_type)

bop_thermal = bop.thermal_cycle
ihts_par = act.ActorHeatTransfer
ihts_par = actor.act.ActorHeatTransfer

blanket_power = @ddtime(bop.heat_transfer.wall.heat_load)
breeder_power = @ddtime(bop.heat_transfer.breeder.heat_load)
Expand Down
Loading