diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 4ce400b7..e77cdacc 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -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 -------------- diff --git a/linopy/io.py b/linopy/io.py index 779d3636..3d8cda02 100644 --- a/linopy/io.py +++ b/linopy/io.py @@ -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") @@ -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)) diff --git a/linopy/solvers.py b/linopy/solvers.py index c2323409..9edb404d 100644 --- a/linopy/solvers.py +++ b/linopy/solvers.py @@ -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") @@ -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." ) @@ -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) @@ -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) diff --git a/pyproject.toml b/pyproject.toml index 116a62d8..59a216bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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'", diff --git a/test/test_io.py b/test/test_io.py index 8bca1a0b..6def28c3 100644 --- a/test/test_io.py +++ b/test/test_io.py @@ -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 diff --git a/test/test_optimization.py b/test/test_optimization.py index ed9f9b80..171c5810 100644 --- a/test/test_optimization.py +++ b/test/test_optimization.py @@ -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__) @@ -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: