Skip to content

Commit

Permalink
Merge pull request OSeMOSYS#187 from trevorb1/issue-19
Browse files Browse the repository at this point in the history
Add GLPK solver reading
  • Loading branch information
trevorb1 authored Aug 18, 2023
2 parents b24a24b + 9a42fb6 commit 5a194de
Show file tree
Hide file tree
Showing 7 changed files with 453 additions and 20 deletions.
30 changes: 30 additions & 0 deletions docs/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,36 @@ Use ``otoole``'s ``result`` package to generate the result CSVs::

$ otoole results cbc csv simplicity.sol results datafile simplicity.txt config.yaml

Data Processing with GLPK
-------------------------

Objective
~~~~~~~~~

Build and solve a model using GLPK and otoole

1. Build the solve the model using GLPK
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Use GLPK_ to build the model, save the problem as ``simplicity.glp``, solve the model,
and save the solution as ``simplicity.sol```::

$ glpsol -m OSeMOSYS.txt -d simplicity.txt --wglp simplicity.glp --write simplicity.sol

2. Use otoole to process the solution in CSVs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Use ``otoole``'s ``results`` command to transform the soltuion file into a folder of CSVs
under the directory ``results-glpk``. When processing solutions from GLPK, the model file (``*.glp``)
must also be passed::

$ otoole results glpk csv simplicity.sol results-glpk datafile simplicity.txt config.yaml --glpk_model simplicity.glp

.. NOTE::
By default, MathProg OSeMOSYS models will write out folder of CSV results to a ``results/``
directory if solving via GLPK. However, for programatically accessing results, using ``otoole``
to control the read/write location, and for supporting future implementations of OSeMOSYS,
using ``otoole`` can be benifical.


Model Visualization
-------------------

Expand Down
4 changes: 2 additions & 2 deletions docs/functionality.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ apparent. CBC_ is an alternative open-source solver which offers better performa
GLPK_ and can handle much larger models. However, CBC_ has no way of knowing how to write
out the CSV files you were used to dealing with when using GLPK_. ``otoole`` to the rescue!

``otoole`` currently supports using CBC_, CPLEX_ or Gurobi_ with all three versions of
``otoole`` currently supports using GLPK_, CBC_, CPLEX_ or Gurobi_ with all versions of
GNU MathProg OSeMOSYS - the long, short and fast versions.

The long version includes all results as variables within the formulation, so the
Expand All @@ -97,7 +97,7 @@ Gurobi_ or CPLEX_ solution file together with the input data::
{cbc,cplex,gurobi} {csv} from_path to_path {csv,datafile,excel} input_path config

positional arguments:
{cbc,cplex,gurobi} Result data format to convert from
{cbc,cplex,glpk,gurobi} Result data format to convert from
{csv} Result data format to convert to
from_path Path to file or folder to convert from
to_path Path to file or folder to convert to
Expand Down
8 changes: 7 additions & 1 deletion src/otoole/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def _result_matrix(args):
args.input_format,
args.input_path,
write_defaults=args.write_defaults,
glpk_model=args.glpk_model,
)


Expand Down Expand Up @@ -166,7 +167,7 @@ def get_parser():
result_parser.add_argument(
"from_format",
help="Result data format to convert from",
choices=sorted(["cbc", "cplex", "gurobi"]),
choices=sorted(["cbc", "cplex", "gurobi", "glpk"]),
)
result_parser.add_argument(
"to_format",
Expand All @@ -185,6 +186,11 @@ def get_parser():
)
result_parser.add_argument("input_path", help="Path to input_data")
result_parser.add_argument("config", help="Path to config YAML file")
result_parser.add_argument(
"--glpk_model",
help="GLPK model file required for processing GLPK results",
default=None,
)
result_parser.add_argument(
"--write_defaults",
help="Writes default values",
Expand Down
44 changes: 30 additions & 14 deletions src/otoole/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,23 @@

import pandas as pd

from otoole.exceptions import OtooleError
from otoole.input import Context, ReadStrategy, WriteStrategy
from otoole.read_strategies import ReadCsv, ReadDatafile, ReadExcel
from otoole.results.results import ReadCbc, ReadCplex, ReadGurobi, ReadResults
from otoole.results.results import ReadCbc, ReadCplex, ReadGlpk, ReadGurobi, ReadResults
from otoole.utils import _read_file, read_deprecated_datapackage, validate_config
from otoole.write_strategies import WriteCsv, WriteDatafile, WriteExcel

logger = logging.getLogger(__name__)


def read_results(
config: str, from_format: str, from_path: str, input_format: str, input_path: str
config: str,
from_format: str,
from_path: str,
input_format: str,
input_path: str,
glpk_model: str = None,
) -> Tuple[Dict[str, pd.DataFrame], Dict[str, float]]:
"""Read OSeMOSYS results from CBC, GLPK or Gurobi results files
Expand All @@ -32,16 +38,15 @@ def read_results(
config : str
Path to config file
from_format : str
Available options are 'datafile', 'csv', 'excel' and 'datapackage' [deprecated]
Available options are 'cbc', 'gurobi', 'cplex', and 'glpk'
from_path : str
Path to source file (if datafile or excel) or folder (csv)
input_format: str
Format of input data. Available options are 'datafile', 'csv' and 'excel'
input_path: str
Path to input data input_format: str
Format of input data. Available options are 'datafile', 'csv' and 'excel'
input_path: str
Path to input data
glpk_model : str
Path to ``*.glp`` model file
Returns
-------
Expand All @@ -50,7 +55,7 @@ def read_results(
"""
user_config = _get_user_config(config)
input_strategy = _get_read_strategy(user_config, input_format)
result_strategy = _get_read_result_strategy(user_config, from_format)
result_strategy = _get_read_result_strategy(user_config, from_format, glpk_model)

if input_strategy:
input_data, _ = input_strategy.read(input_path)
Expand All @@ -73,9 +78,10 @@ def convert_results(
to_path: str,
input_format: str,
input_path: str,
write_defaults=False,
write_defaults: bool = False,
glpk_model: str = None,
) -> bool:
"""Post-process results from a CBC, CPLEX or Gurobi solution file into CSV format
"""Post-process results from a CBC, CPLEX, Gurobi, or GLPK solution file into CSV format
Arguments
---------
Expand All @@ -93,8 +99,10 @@ def convert_results(
Format of input data. Available options are 'datafile', 'csv' and 'excel'
input_path: str
Path to input data
write_defaults : str
write_defaults : bool
Write default values to CSVs
glpk_model : str
Path to ``*.glp`` model file
Returns
-------
Expand All @@ -110,7 +118,7 @@ def convert_results(

# set read strategy

read_strategy = _get_read_result_strategy(user_config, from_format)
read_strategy = _get_read_result_strategy(user_config, from_format, glpk_model)

# set write strategy

Expand All @@ -135,15 +143,19 @@ def convert_results(
return True


def _get_read_result_strategy(user_config, from_format) -> Union[ReadResults, None]:
"""Get ``ReadResults`` for gurobi, cbc and cplex formats
def _get_read_result_strategy(
user_config, from_format, glpk_model=None
) -> Union[ReadResults, None]:
"""Get ``ReadResults`` for gurobi, cbc, cplex, and glpk formats
Arguments
---------
config : dict
User configuration describing parameters and sets
from_format : str
Available options are 'cbc', 'gurobi', and 'cplex'
Available options are 'cbc', 'gurobi', 'cplex', and 'glpk'
glpk_model : str
Path to ``*.glp`` model file
Returns
-------
Expand All @@ -158,6 +170,10 @@ def _get_read_result_strategy(user_config, from_format) -> Union[ReadResults, No
read_strategy = ReadGurobi(user_config=user_config)
elif from_format == "cplex":
read_strategy = ReadCplex(user_config=user_config)
elif from_format == "glpk":
if not glpk_model:
raise OtooleError(resource="Read GLPK", message="Provide glpk model file")
read_strategy = ReadGlpk(user_config=user_config, glpk_model=glpk_model)
else:
return None

Expand Down
Loading

0 comments on commit 5a194de

Please sign in to comment.