Your merit function is nonlinear, continuous and differentiable.
So long as $C$ is unknown, variable and unbounded, the fact that $V_o$ may be expressed as a function of $P_o$ is immaterial. If $C$ is bounded or fixed then constraints need to be added to the demonstration below, but I assume that that is not the case.
When optimizing, $V_a$ and $V_b$ being bounded implies that $V_o$ is also bounded, but this bound can stay implicit; and the simpler form for the merit function is one where $V_o$ is substituted with $V_a + V_b$. As a cost (negative merit),
$$
c = P_o (V_a + V_b) - P_a V_a - P_b V_b
$$
The gradient of the cost function is easy and can help many optimizers, so include it in your problem construction:
$$
\nabla c = \begin{bmatrix}
P_o - P_a \\
P_o - P_b \\
V_a + V_b
\end{bmatrix}
$$
All together, an implementation could look like
import numpy as np
import pandas as pd
from scipy.optimize import check_grad, minimize
Pa = 0.3
Pb = 0.6
Pmin = 0.2
Pmax = 0.7
Vmax_a = 12
Vmax_b = 9
def cost(params: np.ndarray) -> float:
Va, Vb, Po = params
return Po*(Va + Vb) - Pa*Va - Pb*Vb
def cost_jacobian(params: np.ndarray) -> tuple[float, float, float]:
Va, Vb, Po = params
return (
Po - Pa,
Po - Pb,
Va + Vb,
)
error = check_grad(func=cost, grad=cost_jacobian, x0=(0.9, 1.1, 1.4))
assert error < 1e-6
result = minimize(
fun=cost, jac=cost_jacobian,
x0=(1, 1, 1),
bounds=(
(0, Vmax_a),
(0, Vmax_b),
(Pmin, Pmax),
),
)
assert result.success, result.message
Va, Vb, Po = result.x
Vo = Va + Vb
C = Vo - np.e*Po
df = pd.DataFrame(
{
'V': (Va, Vb, Vo),
'P': (Pa, Pb, Po),
},
index=pd.Index(['a', 'b', 'o']),
)
print(df)
print(f'\nC = {C:.3f}')
V P
a 12.0 0.3
b 9.0 0.6
o 21.0 0.2
C = 20.456