Custom Constraint for maximum flow of two Converters

Hello everyone,

I have an energy system in which two offsetConverters should map a converter that participates at two different markets. I would like to limit the sum of the flows of the two offset converters, but I am having problems with this.
I have tried two approaches:

  1. in the first approach I tried to define my own limit_active_flow_count constraint, but I don’t know which keyword I have to use instead of “status” to consider the flow not only as a binary variable but the actual flow
myblock = po.Block()
om.add_component("MyBlock", myblock)

def limit_active_flow_count_rule(m, t):
    expr = (om.NonConvexFlowBlock.status[b_el, electrolyzer1, t] + om.NonConvexFlowBlock.status[b_el_affr, electrolyzer2, t] <= P_in_max)
    return expr

myblock.limit_active_flow_count = po.Constraint(om.TIMESTEPS, rule=limit_active_flow_count_rule)
  1. in the second approach I have oriented myself to the shared_limit constraint. Here I get the error with the “quantity” variable that the OffsetConverterBlock has no “inputs” attribute
solph.constraints.shared_limit(
    model = om,
    quantity = om.OffsetConverterBlock.inputs,
    limit_name = "Input Flow Limit",
    components = [electrolyzer1, electrolyzer2],
    weights =[1,1],
    upper_limit = 20
)

Could someone help me if the approaches can work and what I might have to change?

Thanks in advance
Best regards
Luca

Hi @dhorschi,

unfortunately, both of the constraints were not really designed for your use case. The former really is for binary decisions (only). The second is focusses on components, not on Flows. (I should have documented that in a better way.)

I’d suggest to use a constraint that looks similar to [equate_flows](https://oemof-solph.readthedocs.io/en/stable/_modules/oemof/solph/constraints/equate_flows.html#equate_flows):

    def _equate_flow_groups_rule(m):
        for ts in m.TIMESTEPS:
            m.shared_flow_limit.add(
                ts,
                m.flow[oc1, bus1, ts] * weight1 + m.flow[oc2, bus2, ts] * weight2 <= limit
            )

    setattr(
        model,
        "shared_flow_limit",
        po.Constraint(model.TIMESTEPS, noruleinit=True),
    )
    setattr(
        model,
        "shared_flow_limit_build",
        po.BuildAction(),
    )

It’s probably not the most elegant way to code this but I tried to define something that works without having to test it. (I hope it works.)

Cheers,
Patrik

Hey @pschoen

Thanks for your reply.
I have adapted your suggestion slightly. But now it works.

def _equate_flow_groups_rule(m):
        for ts in m.TIMESTEPS:
            m.shared_flow_limit.add(
                ts,
                m.flow[b_el, electrolyzer1, ts] * weight1 + m.flow[b_el_neg_affr, electrolyzer2, ts] * weight2 + m.flow[b_el_pos_affr, electrolyzer3, ts] * weight3 <= limit
            )


def build_shared_flow_limit_rule(m):
    return _equate_flow_groups_rule(m)


setattr(
    om,
    "shared_flow_limit",
    po.Constraint(om.TIMESTEPS, noruleinit=True),
)
setattr(
    om,
    "shared_flow_limit_build",
    po.BuildAction(rule=build_shared_flow_limit_rule),
)

I have also modified my first attempt so that it works too.

myblock = po.Block()
om.add_component("MyBlock", myblock)

def limit_active_flow_count_rule(m, t):
    expr = (om.flow[b_el, electrolyzer1, t] + om.flow[b_el_neg_affr, electrolyzer2, t] +om.flow[b_el_pos_affr, electrolyzer3, t] <= P_in_max)
    return expr

myblock.limit_active_flow_count = po.Constraint(om.TIMESTEPS, rule=limit_active_flow_count_rule)

Do you know if one of the two approaches is better than the other for defining custom constraints?

Best regards
Luca

In fact, both of the constraints are more or less the same. In your first snipped, you define a useless wrapper function, just giving _equate_flow_groups_rule as the rule should work. I forgot to do so. However, anyway your second solution (“modified first attempt)” is more elegant as it directly defines the multi-dimensional constraint.