How to get the marginal costs of all powerplants?

Hey there,

I’m curious how to get financial information about generation costs from the solved model. Lets say I want to know the marginal generation cost of every node (or flow) in my solved energy system for every time step. How do I get this information from the model? At some point in the solving process there must be a linear equation existent containing the generation costs for each timestep. I have the feeling that it wouldn’t need much work to get the information from the model. From my simple understanding of the solving process I would say the solver already calculated all the marginal costs to compare and pick the cheapest option.

I’m looking for something similar to the dual variable of the bus equality constraint giving marginal cost of production in the specific bus. Maybe I’m just missing something out and somebody can point out to me how and where to find this information.

My basic idea would be to just multiply the ´variable_costs´ of each flow with the respective ´value´ of the flow. Like described in here: How to get the total costs of the optimized model?
Further enhacements would be to also take startup_costs and fuelcosts into account depending on the status variable of the flow. But again I have the feeling that the solver already must have done this calculation. But maybe I’m wrong.

Any suggestions, comments and help are highly appreciated.

Regards
Till

1 Like

Hello Till,

I think, unfortunately what you are looking for doesn’t come out of the box. The pyomo model and the solver have the information, right. But the objective term is constructed from terms attributed to each component of your network. So e.g. for a generator - i.e. transformer - you also have costs attached to your commodity bus.

You are right about the dual values. These can be retrieved for every bus if you call

import oemof.solph as solph
...
om = solph.Model(energy_system)
om.receive_duals()

before calling

om.solve()

whereby om is your optimization model.

Now it depends on how you interpret your model:

  • In the day-ahead power market, we have uniform pricing. So, as you described, just multiplying the dual value of a solved model with the bus outflow value (to a demand sink, given as input) could be used to calculate the financial volume that is payed including the contribution margins, generators will receive.
  • If you are interested in discriminating prices and using the marginal costs of each generator (so kind of a pay as bid logic), it is a bit more complicated. You basically would need to check the value of the respective inflow into the bus and multiply that with its individual costs which are given by your models parameterization and consisting of fuel costs (commodity bus + considering transformer efficiency) and variable costs (transformer). I think it would make sense to write a small routine to calculate that for each generator.

In general, you can use the following to retrieve your bus results:

results = solph.processing.results(om)
bus_flows = solph.views.node(results, bus_el)["sequences"]

You optionally can convert keys to strings. What you then need to do is to filter nodes. You can use the function filter_nodes of the views module. What I tend to do is to use some sort of naming scheme for your components which you can then filter for (e.g. by checking whether a certain substring is in the column name of your sequences DataFrame).

I hope this helps at least a bit.

Best regards,
Johannes

1 Like

Thank you, Johannes.

In the past few days I wrote a little script that tries to calculate the short term marginal generation costs of each solph.Transformer object. Maybe it helps somebody in the future.

But beware of the arbitrary assignment of generation costs if there are multiple outputs/inputs. For example if you want to calculate costs of heat and electricity you should use something like the Equivalence number method - Wikipedia.

def postprocessing(energysystem):
    # save all flow variables and dual variables to an excelfile
    results = solph.views.convert_keys_to_strings(energysystem.results["main"])
    results_df = pd.concat(
        [(lambda x: x.set_axis([str(k) + ' ' + s for s in x.columns], axis=1))(v['sequences']) for k, v in results.items()],
        axis=1)
    #calculate marginal prices of transformer nodes
    for entity in energysystem.entities:
        if isinstance(entity, solph.Transformer):
            input_flow_group = [x for x in energysystem.groups[solph.blocks.flow.Flow] if x[1] == entity]
            output_flow_group = [x for x in energysystem.groups[solph.blocks.flow.Flow] if x[0] == entity]
            conversion_factors = dict(entity.conversion_factors.items()) #create dict bc items() just returns a view
            cost_df = pd.DataFrame()
            for flow_group in input_flow_group:
                shadowprice_input_bus = energysystem.results["Main"][(flow_group[0], None)]["sequences"]["duals"]
                input_conversion_factor = conversion_factors.get(flow_group[0])
                cost_df[flow_group[0]] = shadowprice_input_bus.mul(input_conversion_factor) #(fuel costs * efficiency) + variable costs = input costs
                cost_df[flow_group[0]] = cost_df[flow_group[0]].add(flow_group[2].variable_costs) #add variable flow costs
            cost_df = cost_df.sum(axis=1) #input costs is the sum of all input flow costs
            sum_output_efficiencies = [conversion_factors.get(n) for n in conversion_factors.keys() if n in [x[1] for x in output_flow_group]] #collect output efficiencies
            sum_output_efficiencies = [sum(y) for y in zip(*sum_output_efficiencies)]#sum up all output efficiencies of transformer to calculate eqivalence factor
            for flow_group in output_flow_group:
                equivalence_factor = [i/j for i,j in zip(conversion_factors.get(flow_group[1]), sum_output_efficiencies)] #other than chp there is no defined main flow. therefore there is just an efficiency weighting.
                stmgc_output_flow = stmgc_input_flow.div(conversion_factors.get(flow_group[1])).mul(equivalence_factor).add(
                    flow_group[2].variable_costs) # outflow costs = input costs * equivalence factor / conversion factor + variable costs
                results_df["('{}', '{}') marginal".format(flow_group[0], flow_group[1])] = stmgc_output_flow
    results_df.to_excel("output/results.xlsx")

Cheers
Till

1 Like

Dear @tllsngnzlz ,
as you have correctly predicted that someone might work in the similar problem. That someone is me right now. I get the idea how you are calculating the marginal price of each transformer.
I had a question regarding the stmgc_input_flow variable you have used in the code. You have not defined it clearly inside the code, I assume it is equivalent to cost_df ?

Best Regards,
Dev