Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test_and_lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Install Dependencies
shell: bash -el {0}
run: |
conda install ipopt
conda install ipopt pip
python -m pip install --upgrade pip
pip install .[test]
- name: Test with pytest
Expand Down
4 changes: 2 additions & 2 deletions eeco/tests/test_emissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ def test_calculate_grid_emissions_cvx(
emissions_units=emissions_units,
)
prob = cp.Problem(cp.Minimize(result), constraints)
prob.solve()
assert result.value == expected
prob.solve(solver=cp.CLARABEL)
assert result.value == pytest.approx(expected)
assert model is None


Expand Down
39 changes: 22 additions & 17 deletions eeco/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,41 +53,46 @@ def gas_constraint(m, t):

@pytest.mark.skipif(skip_all_tests, reason="Exclude all tests")
@pytest.mark.parametrize(
"consumption_data, varstr1, varstr2, expected",
"consumption_data, varstr1, varstr2, time_set, expected",
[
(
{"electric": np.ones(96) * 100, "gas": np.ones(96)},
"electric",
"gas",
None,
np.ones(96) * 100,
),
# non-range (float seconds) index set: the numpy charge_array must be
# indexed by position and the Pyomo var by member. Regression for
# IndexError under numpy >= 1.24 (float index into a numpy array).
(
{"electric": np.array([100.0, 200.0, 300.0, 400.0])},
"electric",
np.array([0.1, 0.2, 0.3, 0.4]),
[0.0, 900.0, 1800.0, 2700.0],
np.array([10.0, 40.0, 90.0, 160.0]),
),
],
)
def test_multiply_pyo(consumption_data, varstr1, varstr2, expected):
def test_multiply_pyo(consumption_data, varstr1, varstr2, time_set, expected):
model = pyo.ConcreteModel()
model.T = len(consumption_data["electric"])
model.t = range(model.T)
pyo_vars = {}
model.T = len(consumption_data[varstr1])
model.t = range(model.T) if time_set is None else time_set
pos = {t: i for i, t in enumerate(model.t)}
for key, val in consumption_data.items():
var = pyo.Var(model.t, initialize=np.zeros(len(val)), bounds=(0, None))
var = pyo.Var(model.t, bounds=(None, None))
model.add_component(key, var)
pyo_vars[key] = var

@model.Constraint(model.t)
def electric_constraint(m, t):
return consumption_data["electric"][t] == m.electric[t]

@model.Constraint(model.t)
def gas_constraint(m, t):
return consumption_data["gas"][t] == m.gas[t]
for t in model.t:
var[t].fix(float(val[pos[t]]))

var1 = getattr(model, varstr1)
var2 = getattr(model, varstr2)
# second operand: a Pyomo var (by name) or a numpy charge_array passed directly
var2 = getattr(model, varstr2) if isinstance(varstr2, str) else varstr2
result, model = ut.multiply(var1, var2, model=model, varstr="test")
model.objective = pyo.Objective(expr=0)
solver = pyo.SolverFactory("ipopt")
solver.solve(model)
assert np.allclose([pyo.value(result[i]) for i in range(len(result))], expected)
assert np.allclose([pyo.value(result[t]) for t in model.t], expected)
assert model is not None


Expand Down
13 changes: 10 additions & 3 deletions eeco/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,14 +395,21 @@ def multiply(
) or isinstance(
expression2, (SumExpression, IndexedExpression, pyo.Param, pyo.Var)
):
pos = {t: i for i, t in enumerate(model.t)}

def at(expr, t):
# numpy operands are positional (length T); Pyomo operands are
# indexed by time-set member. Map the member to its position.
return expr[pos[t]] if isinstance(expr, np.ndarray) else expr[t]

if (not isinstance(expression1, (int, float))) and (len(expression1) > 1):
if (not isinstance(expression2, (int, float))) and (len(expression2) > 1):
# TODO: replace model.t with better way to get dimensions
model.add_component(varstr, pyo.Var(model.t))
var = model.find_component(varstr)

def const_rule(model, t):
return var[t] == expression1[t] * expression2[t]
return var[t] == at(expression1, t) * at(expression2, t)

constraint = pyo.Constraint(model.t, rule=const_rule)
model.add_component(varstr + "_constraint", constraint)
Expand All @@ -412,7 +419,7 @@ def const_rule(model, t):
var = model.find_component(varstr)

def const_rule(model, t):
return var[t] == expression1[t] * expression2
return var[t] == at(expression1, t) * expression2

constraint = pyo.Constraint(model.t, rule=const_rule)
model.add_component(varstr + "_constraint", constraint)
Expand All @@ -422,7 +429,7 @@ def const_rule(model, t):
var = model.find_component(varstr)

def const_rule(model, t):
return var[t] == expression1 * expression2[t]
return var[t] == expression1 * at(expression2, t)

constraint = pyo.Constraint(model.t, rule=const_rule)
model.add_component(varstr + "_constraint", constraint)
Expand Down
Loading