Working with solph.Results

With the update of solph v0.6.2, we marked solph.Results as stable. As it is planned to completely replace solph.processing.results(model), I am currently replacing all references to the old module from the documentation, so that new users will learn the new feature instead of the one that is about to be replaced.

With the old nested dict structure, we had the very convenient helper solph.views.node(results, "node_label"), which extracted all information related to a particular Node. For example, you could use it to extract all flows from and to a node.

## solph v0.5 way
model.solve()
results_dict = solph.processing.results(model)
node1_flows = solph.views.node(results, "node1")["sequences"]["flow"]

## proposed way (solph v0.6)
results = model.solve()  # planned v0.6.4 API
flows = results["flow"]
mask = (
    (flows.columns.get_level_values(0) == "node1")
    | (flows.columns.get_level_values(1) == "node1")
)
node1_flows = flows.loc[:, mask]

The mask code is a bit verbose, but it is very speaking and I consider it to be rather easy to understand. I suggest it as you typically know how many levels the index has. Alternatively, you can use the following:

mask = (
    flows.columns.to_frame(
        index=False
    ).eq("node1").any(axis=1).to_numpy()
)

I do see that the new way requires more lines of code by the user, but masking should be a common concept and you can find a lot of resources about it. Also, the new solution will still outperform the old one, when processing.results is also in scope: For my example, the solph 0.6 code above took about 12 ms to execute, the solph 0.5 code took about 33 ms. (I did not make a full benchmark, here, as it is not the scope.)

What do you think? Is masking actually more user friendly than the old custom API?

Leave a Reply

Your email address will not be published. Required fields are marked *