Skip to content

Utils Test for Cost Calculations#455

Open
tolga-akan wants to merge 10 commits intomainfrom
utils_test_for_cost
Open

Utils Test for Cost Calculations#455
tolga-akan wants to merge 10 commits intomainfrom
utils_test_for_cost

Conversation

@tolga-akan
Copy link
Copy Markdown
Collaborator

@tolga-akan tolga-akan commented Apr 3, 2026

Crete a new general function in utilts test to check all the cost components in the problem
TODO:

  • Make the function as general as possible (initially for the all assets for heat networks, later we can extend it to other commodifities)

Modify var-opex part for:

  • pumppower cost calculation
  • influence of given electricity price profile

@tolga-akan tolga-akan marked this pull request as ready for review April 9, 2026 09:28
@tolga-akan
Copy link
Copy Markdown
Collaborator Author

@KobusVanRooyen Hi Kobus. I made a cost calculation test as general as possible. This function calculates the cost for assets that we use in heat networks. Assets like electrolyzer, pump, ... are not included yet. I did not incorporate influence of pumpower cost and electricity price profile yet. Shall I also include those? What do you think

# Conflicts:
#	tests/test_cold_demand.py
#	tests/test_elec_boiler.py
#	tests/test_end_scenario_sizing.py
#	tests/test_gas_boiler.py
#	tests/test_gas_electricity.py
Comment thread tests/test_cold_demand.py Outdated

hp_id = name_to_id_map["HeatPump_b97e"]

cost_calculation_test(heat_problem, results)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

  1. Will this test still pass if the _variable_operational_cost and parameters[f"{hp_id}.variable_operational_cost_coefficient"] might be zero for some reason?
  2. Based on the question above-> what about adding a hard check in the test case that one expects results[f"{hp_id}__variable_operational_cost"] to be > then .... (some sensible/large number and not 0).
  3. Some hard coded check like this will be needed in each test case where it is expected that a value should exist even if cost_calculation_test(heat_problem, results) is used. What do you think?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes. This is a good point. I will add additional check to all tests (if needed) to ensure regarding cost component is non zero (larger than a large enough positive value)

Comment thread tests/test_gas_electricity.py Outdated
Checks:
1. utils test: demand_matching_test, energy_conservation_test, heat_to_discharge_test,
electric_power_conservation_test, gas_pipes_head_loss_test
1. utils test: cost_calculation_test, demand_matching_test, energy_conservation_test,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Previously the TCO was checked, an not not anymore?

Comment thread tests/test_elec_boiler.py Outdated
2. demand is matched
3. energy conservation in the network
4. heat to discharge
5. energy conservation over the heat and electricity commodity
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why has item 5 been added? It is already mentioned in item 3?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

actually that line was refering to the followign check i the test: np.testing.assert_array_less(0.0, results[f"{e_boiler_id}.Heat_source"])
np.testing.assert_allclose(
parameters[f"{e_boiler_id}.efficiency"] * results[f"{e_boiler_id}.Power_consumed"],
results[f"{e_boiler_id}.Heat_source"],
)

However, you are right that item 3 is already hinting the same. Now I removed item 5

Comment thread tests/test_elec_boiler.py Outdated
2. demand is matched
3. energy conservation in the network
4. heat to discharge
5. energy conservation over the heat and electricity commodity
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why has item 5 been added? It is already mentioned in item 3?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

it should be "electric_power_conservation_test". now updated

Comment thread tests/test_elec_boiler.py Outdated
parameters = heat_problem.parameters(0)
name_to_id_map = heat_problem.esdl_asset_name_to_id_map

cost_calculation_test(heat_problem, results)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

checks needed before the function call to make sure the specific cost items exists (> than sensible value)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Check that ensures cost components of elec heat sourc elec are non-zero is added

)
total_opex += variable_operational_cost

np.testing.assert_allclose(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The obj is not tested anymore. Please add it to the new function "cost_calculation_test" in the test utils. But this check of obj value is not always required. So create a new input for the function "cost_calculation_test", something like check_objective_function and make the default value False.

So for this specific test case the input variable will be set to check_objective_function=True. Then for all the other test case check_objective_function does not have to be set, if the objective function value is not tested

Comment thread tests/utils_tests.py
)


def _get_esdl_scaled_cost(cost_esdl_info, uni_enum):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

please add docstring for this function

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

docstring is added

Comment thread tests/utils_tests.py
return cost_scaled


def cost_calculation_test(solution, results, atol=1e-8):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

please add a docstring for this function

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

docstring is added

Comment thread tests/utils_tests.py
# Investment Cost
investment_cost = 0.0
if asset not in solution.energy_system_components.get("gas_pipe", []):
investment_cost_info = _get_esdl_scaled_cost(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we add testing for the the cost coefficient itself, to check that the value in the esdl (which investment_cost_info) is the same as the value stored in the MESIDO parameters (whic his parameters[f"{asset}.variable_operational_cost_coefficient"] in this case)

The benefit is that we are also checking if we have parsed and save the actual cost coef correctly. Can you add this to all the other cost coefficients in this function please

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I added the suggested checks except "installation cost"and investemt cost of heat pipes. Because, there is no variable called installation_cost_coefficient. I could not figured out the reason but I see that parameters[f"{asset}.investment_cost_coefficient"] and investment_cost_info are different from each other.

Comment thread tests/utils_tests.py Outdated
np.testing.assert_allclose(
variable_operational_cost, results[f"{asset}__variable_operational_cost"]
)
total_opex += variable_operational_cost
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

please add a check for the obj function, but only checking it if check_objective_function=True

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

For a check on the objective function, you need to check the state of the asset (whether it is enabled or optional)

Comment thread tests/test_cold_demand.py Outdated

hp_id = name_to_id_map["HeatPump_b97e"]

cost_calculation_test(heat_problem, results)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The first tests should be related to the physics, heat_demand_matching energy_conservation and heat_to_discharge tests as these tests also tell us whether the majority of the code is working. If there already an issue occurs then the costs calucation might also fail, but that is then not the cause.
This is for all the tests

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I agree. order of the cost test is changed.

Comment thread tests/utils_tests.py Outdated
Comment on lines +776 to +782
for ii in range(1, len(solution.times())):
variable_operational_cost += (
var_op_costs_esdl
* nominator_vector[ii]
* timesteps_hr[ii - 1]
/ denominator
)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No for loop for the times is need, like discussed before. also in the elif below.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

for loops are removed and generalized by vector multiplication at the bottom

Comment thread tests/utils_tests.py Outdated
*transport_assets,
]

pipe_classes = [
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

rename to gas_pipe_classes

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

renamed to gas_pipe_classes

Comment thread tests/utils_tests.py Outdated
Comment on lines +682 to +685
elif asset in solution.energy_system_components.get("heat_pipe", []):
investment_cost = investment_cost_info * parameters[f"{asset}.length"]
elif asset in solution.energy_system_components.get("electricity_cable", []):
investment_cost = investment_cost_info * parameters[f"{asset}.length"]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

combine these elif statements

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

combined

Comment thread tests/utils_tests.py Outdated
)

# Installation Cost
if asset not in transport_assets:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why are transport_assets excluded? Wouldn't it be better to check that it is 0.0 or None and thus also 0.0

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, that would be better. I will add check for also transport assets

Comment thread tests/utils_tests.py
* timesteps_hr[ii - 1]
/ denominator
)
elif asset in solution.energy_system_components.get("airco", []):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Missing an else statement. Now there are several elif statemetns, but those do not cover all the non transport assets, so without realising you are missing out on several assets. I believe all those assets should be calculated and checked. Also the heat demand etc.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I would also prefer an "else .... error exit" to cater for cases when new assets are added, which are not included in the test. This way we are made aware of an issue, else we might think the test is doing something where is might not be doing/testing anything

Comment thread tests/utils_tests.py Outdated
Comment on lines +744 to +747
for ii in range(1, len(solution.times())):
variable_operational_cost += (
var_op_costs_esdl * nominator_vector[ii] * timesteps_hr[ii - 1]
)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can be moved down and generalized instead of being repeated several times, all variable opex is calculated with var_op_costs_esdl * nominator*dt/denominator.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I agree. now it is moved down

Comment thread tests/utils_tests.py Outdated
np.testing.assert_allclose(
variable_operational_cost, results[f"{asset}__variable_operational_cost"]
)
total_opex += variable_operational_cost
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

For a check on the objective function, you need to check the state of the asset (whether it is enabled or optional)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think the costs check on demands that is covered in this test can also be removed once it is included in cost_calculation_test

Comment thread tests/test_cold_demand.py
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

test_max_size_and_optional_assets.py also contains many costs checks, some hard-coded values, those you should keep, but the once where it checks the calculation you should remove and add the cost_calculation_test

@KobusVanRooyen
Copy link
Copy Markdown
Collaborator

@tolga-akan review completed

Copy link
Copy Markdown
Collaborator Author

@tolga-akan tolga-akan left a comment

Choose a reason for hiding this comment

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

@KobusVanRooyen and @FJanssen-TNO I tried to address the comments as much as possible but it is not complete yet. There are still missing points that I could not respond due to lack of time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants