Skip to content

Commit

Permalink
correct aggregation; correct LCOE/LCOH eq
Browse files Browse the repository at this point in the history
  • Loading branch information
kurbansitterley committed Nov 21, 2024
1 parent 841bb60 commit 205decb
Show file tree
Hide file tree
Showing 2 changed files with 206 additions and 102 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,70 @@
solver = get_solver()


def check_proper_aggregation(treat_cost, energy_cost, system_cost):
"""
Test if capital and operating costs from treatment and energy
costing blocks are properly aggregated on the system costing block.
"""

assert pytest.approx(
value(treat_cost.total_capital_cost + energy_cost.total_capital_cost), rel=1e-3
) == value(system_cost.total_capital_cost)

assert pytest.approx(
value(
treat_cost.total_fixed_operating_cost
+ energy_cost.total_fixed_operating_cost
),
rel=1e-3,
) == value(system_cost.total_fixed_operating_cost)

if hasattr(energy_cost, "aggregate_flow_heat"):
assert pytest.approx(
value(treat_cost.aggregate_flow_heat + energy_cost.aggregate_flow_heat),
rel=1e-3,
) == value(system_cost.aggregate_flow_heat)

assert pytest.approx(value(system_cost.aggregate_flow_heat), rel=1e-3) == value(
system_cost.aggregate_flow_heat_purchased
- system_cost.aggregate_flow_heat_sold
)
assert pytest.approx(
value(system_cost.aggregate_flow_electricity), rel=1e-3
) == value(
system_cost.aggregate_flow_electricity_purchased
- system_cost.aggregate_flow_electricity_sold
)

assert pytest.approx(
value(
treat_cost.aggregate_flow_electricity
+ energy_cost.aggregate_flow_electricity
),
rel=1e-3,
) == value(system_cost.aggregate_flow_electricity)

assert pytest.approx(
value(
treat_cost.total_operating_cost
- sum(
treat_cost.aggregate_flow_costs[f]
for f in ["electricity", "heat"]
if f in treat_cost.used_flows
)
+ energy_cost.total_operating_cost
- sum(
energy_cost.aggregate_flow_costs[f]
for f in ["electricity", "heat"]
if f in energy_cost.used_flows
)
+ system_cost.total_electric_operating_cost
+ system_cost.total_heat_operating_cost
),
rel=1e-3,
) == value(system_cost.total_operating_cost)


def build_electricity_gen_only_with_heat():
"""
Test flowsheet with only electricity generation units on energy block.
Expand Down Expand Up @@ -198,6 +262,7 @@ def build_heat_gen_only():
#### TREATMENT BLOCK
m.fs.treatment = Block()
m.fs.treatment.costing = TreatmentCosting()
m.fs.treatment.costing.electricity_cost.fix(0.09)

m.fs.treatment.unit = DummyTreatmentUnit(property_package=m.fs.properties)
m.fs.treatment.unit.costing = UnitModelCostingBlock(
Expand Down Expand Up @@ -442,7 +507,7 @@ def energy_gen_only_with_heat(self):
return m

@pytest.mark.unit
def test_build(slef, energy_gen_only_with_heat):
def test_build(self, energy_gen_only_with_heat):

m = energy_gen_only_with_heat

Expand Down Expand Up @@ -516,6 +581,13 @@ def test_init_and_solve(self, energy_gen_only_with_heat):
value(m.fs.costing.aggregate_flow_heat), rel=1e-3
) == value(m.fs.treatment.unit.heat_consumption)

@pytest.mark.component
def test_check_proper_aggregation(self, energy_gen_only_with_heat):
m = energy_gen_only_with_heat
check_proper_aggregation(
m.fs.treatment.costing, m.fs.energy.costing, m.fs.costing
)

@pytest.mark.component
def test_optimize_frac_from_grid(self):

Expand Down Expand Up @@ -575,20 +647,24 @@ def test_optimize_frac_from_grid(self):
+ m.fs.energy.costing.aggregate_variable_operating_cost
)

check_proper_aggregation(
m.fs.treatment.costing, m.fs.energy.costing, m.fs.costing
)


class TestElectricityGenOnlyNoHeat:

@pytest.fixture(scope="class")
def energy_gen_only_no_heat(self):
def elec_gen_only_no_heat(self):

m = build_electricity_gen_only_no_heat()

return m

@pytest.mark.unit
def test_build(slef, energy_gen_only_no_heat):
def test_build(self, elec_gen_only_no_heat):

m = energy_gen_only_no_heat
m = elec_gen_only_no_heat

assert_units_consistent(m)

Expand All @@ -603,8 +679,8 @@ def test_build(slef, energy_gen_only_no_heat):
assert not hasattr(m.fs.costing, "frac_heat_from_grid")

@pytest.mark.component
def test_init_and_solve(self, energy_gen_only_no_heat):
m = energy_gen_only_no_heat
def test_init_and_solve(self, elec_gen_only_no_heat):
m = elec_gen_only_no_heat

# constraints are active before initialization
assert m.fs.costing.total_heat_operating_cost_constraint.active
Expand Down Expand Up @@ -684,6 +760,13 @@ def test_init_and_solve(self, energy_gen_only_no_heat):
+ m.fs.energy.costing.aggregate_variable_operating_cost
)

@pytest.mark.component
def test_check_proper_aggregation(self, elec_gen_only_no_heat):
m = elec_gen_only_no_heat
check_proper_aggregation(
m.fs.treatment.costing, m.fs.energy.costing, m.fs.costing
)

@pytest.mark.component
def test_optimize_frac_from_grid(self):

Expand Down Expand Up @@ -721,6 +804,10 @@ def test_optimize_frac_from_grid(self):
+ m.fs.energy.costing.aggregate_flow_electricity
)

check_proper_aggregation(
m.fs.treatment.costing, m.fs.energy.costing, m.fs.costing
)


class TestHeatGenOnly:
@pytest.fixture(scope="class")
Expand Down Expand Up @@ -803,6 +890,13 @@ def test_init_and_solve(self, heat_gen_only):
* m.fs.costing.frac_heat_from_grid
)

@pytest.mark.component
def test_check_proper_aggregation(self, heat_gen_only):
m = heat_gen_only
check_proper_aggregation(
m.fs.treatment.costing, m.fs.energy.costing, m.fs.costing
)

@pytest.mark.component
def test_optimize_frac_from_grid(self):

Expand Down Expand Up @@ -852,6 +946,10 @@ def test_optimize_frac_from_grid(self):
+ m.fs.energy.costing.aggregate_variable_operating_cost
)

check_proper_aggregation(
m.fs.treatment.costing, m.fs.energy.costing, m.fs.costing
)


class TestElectricityAndHeatGen:

Expand All @@ -875,7 +973,7 @@ def heat_and_elec_gen(self):
return m

@pytest.mark.unit
def test_build(slef, heat_and_elec_gen):
def test_build(self, heat_and_elec_gen):

m = heat_and_elec_gen

Expand Down Expand Up @@ -969,6 +1067,13 @@ def test_init_and_solve(self, heat_and_elec_gen):
/ m.fs.treatment.costing.aggregate_flow_heat
)

@pytest.mark.component
def test_check_proper_aggregation(self, heat_and_elec_gen):
m = heat_and_elec_gen
check_proper_aggregation(
m.fs.treatment.costing, m.fs.energy.costing, m.fs.costing
)

@pytest.mark.component
def test_optimize_frac_from_grid(self):

Expand Down Expand Up @@ -1036,6 +1141,10 @@ def test_optimize_frac_from_grid(self):
+ m.fs.energy.costing.aggregate_variable_operating_cost
)

check_proper_aggregation(
m.fs.treatment.costing, m.fs.energy.costing, m.fs.costing
)


@pytest.mark.component
def test_no_energy_treatment_block():
Expand All @@ -1055,89 +1164,6 @@ def test_no_energy_treatment_block():
m.fs.costing = REFLOSystemCosting()


@pytest.mark.component
def test_common_params_equivalent():

m = build_default()

m.fs.energy.costing.cost_process()
m.fs.treatment.costing.cost_process()

m.fs.energy.costing.electricity_cost.fix(0.02)

# raise error when electricity costs aren't equivalent

with pytest.raises(
ValueError,
match="The common costing parameter electricity_cost was found to "
"have a different value on the energy and treatment costing blocks\\. "
"Common costing parameters must be equivalent across all"
" costing blocks to use REFLOSystemCosting\\.",
):
m.fs.costing = REFLOSystemCosting()

m = build_default()

m.fs.energy.costing.electricity_cost.fix(0.02)
m.fs.treatment.costing.electricity_cost.fix(0.02)

m.fs.energy.costing.cost_process()
m.fs.treatment.costing.cost_process()

m.fs.costing = REFLOSystemCosting()
m.fs.costing.cost_process()

assert_units_consistent(m)

# when they are equivalent, assert equivalency across all three costing packages

assert value(m.fs.costing.electricity_cost) == value(
m.fs.treatment.costing.electricity_cost
)
assert value(m.fs.costing.electricity_cost) == value(
m.fs.energy.costing.electricity_cost
)

m = build_default()

m.fs.treatment.costing.base_currency = pyunits.USD_2011

m.fs.energy.costing.cost_process()
m.fs.treatment.costing.cost_process()

assert_units_consistent(m)

# raise error when base currency isn't equivalent

with pytest.raises(
ValueError,
match="The common costing parameter base_currency was found to "
"have a different value on the energy and treatment costing blocks\\. "
"Common costing parameters must be equivalent across all"
" costing blocks to use REFLOSystemCosting\\.",
):
m.fs.costing = REFLOSystemCosting()

m = build_default()

m.fs.treatment.costing.base_currency = pyunits.USD_2011
m.fs.energy.costing.base_currency = pyunits.USD_2011

m.fs.energy.costing.cost_process()
m.fs.treatment.costing.cost_process()

m.fs.costing = REFLOSystemCosting()
m.fs.costing.cost_process()

assert_units_consistent(m)

# when they are equivalent, assert equivalency across all three costing packages

assert m.fs.costing.base_currency is pyunits.USD_2011
assert m.fs.treatment.costing.base_currency is pyunits.USD_2011
assert m.fs.energy.costing.base_currency is pyunits.USD_2011


@pytest.mark.component
def test_add_LCOW_to_energy_costing():

Expand Down
Loading

0 comments on commit 205decb

Please sign in to comment.