Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

io: fix netcdf IO for parameters #304

Merged
merged 3 commits into from
Jun 14, 2024
Merged
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
1 change: 1 addition & 0 deletions doc/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Release Notes
Upcoming Version
----------------

* The writing and reading from netcdf files was fixed to correctly handle the model `parameters` field.

Version 0.3.10
--------------
Expand Down
4 changes: 2 additions & 2 deletions linopy/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -918,7 +918,7 @@ def with_prefix(ds, prefix):
if m.objective.value is not None:
objective = objective.assign_attrs(value=m.objective.value)
obj = [with_prefix(objective, "objective")]
params = [with_prefix(m.parameters, "params")]
params = [with_prefix(m.parameters, "parameters")]

scalars = {k: getattr(m, k) for k in m.scalar_attrs}
ds = xr.merge(vars + cons + obj + params, combine_attrs="drop_conflicts")
Expand Down Expand Up @@ -1008,7 +1008,7 @@ def get_prefix(ds, prefix):
m.objective.sense = objective.attrs.pop("sense")
m.objective._value = objective.attrs.pop("value", None)

m.parameters = get_prefix(ds, "parameter")
m.parameters = get_prefix(ds, "parameters")

for k in m.scalar_attrs:
setattr(m, k, ds.attrs.get(k))
Expand Down
18 changes: 13 additions & 5 deletions linopy/solvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,17 @@

available_solvers.append("gurobi")
with contextlib.suppress(ImportError):
_new_highspy_mps_layout = None
import highspy

available_solvers.append("highs")
from importlib.metadata import version

if version("highspy") < "1.7.1":
_new_highspy_mps_layout = False
else:
_new_highspy_mps_layout = True

if sub.run([which, "glpsol"], stdout=sub.DEVNULL, stderr=sub.STDOUT).returncode == 0:
available_solvers.append("glpk")

Expand Down Expand Up @@ -132,7 +140,7 @@ def maybe_adjust_objective_sign(solution, sense, io_api):
if np.isnan(solution.objective):
return

if io_api == "mps" and sys.version_info < (3, 12):
if io_api == "mps" and not _new_highspy_mps_layout:
logger.info(
"Adjusting objective sign due to switched coefficients in MPS file."
)
Expand Down Expand Up @@ -180,9 +188,9 @@ def run_cbc(
)

# CBC does not like the OBJSENSE line in MPS files, which new highspy versions write
if io_api == "mps" and model.sense == "max" and sys.version_info >= (3, 12):
if io_api == "mps" and model.sense == "max" and _new_highspy_mps_layout:
raise ValueError(
"GLPK does not support maximization in MPS format for Python 3.12+"
"CBC does not support maximization in MPS format highspy versions >=1.7.1"
)

problem_fn = model.to_file(problem_fn, io_api)
Expand Down Expand Up @@ -293,9 +301,9 @@ def run_glpk(
)

# GLPK does not like the OBJSENSE line in MPS files, which new highspy versions write
if io_api == "mps" and model.sense == "max" and sys.version_info >= (3, 12):
if io_api == "mps" and model.sense == "max" and _new_highspy_mps_layout:
raise ValueError(
"GLPK does not support maximization in MPS format for Python 3.12+"
"GLPK does not support maximization in MPS format highspy versions >=1.7.1"
)

problem_fn = model.to_file(problem_fn, io_api)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ dev = [
solvers = [
"gurobipy",
"highspy>=1.5.0; python_version < '3.12'",
"highspy>=1.7.1.dev1; python_version >= '3.12'",
"highspy>=1.7.1; python_version >= '3.12'",
"cplex; platform_system != 'Darwin' and python_version < '3.12'",
"mosek",
"mindoptpy; python_version < '3.12'",
Expand Down
2 changes: 2 additions & 0 deletions test/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ def model():

m.add_objective(2 * x + 3 * y)

m.parameters["param"] = xr.DataArray([1, 2, 3, 4], dims=["x"])

return m


Expand Down
4 changes: 2 additions & 2 deletions test/test_optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from xarray.testing import assert_equal

from linopy import GREATER_EQUAL, LESS_EQUAL, Model
from linopy.solvers import available_solvers, quadratic_solvers
from linopy.solvers import _new_highspy_mps_layout, available_solvers, quadratic_solvers

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -389,7 +389,7 @@ def test_model_maximization(model_maximization, solver, io_api):
assert m.objective.sense == "max"
assert m.objective.value is None

if solver in ["cbc", "glpk"] and io_api == "mps" and sys.version_info >= (3, 12):
if solver in ["cbc", "glpk"] and io_api == "mps" and _new_highspy_mps_layout:
with pytest.raises(ValueError):
m.solve(solver, io_api=io_api)
else:
Expand Down
Loading