Repository: Pyomo/pyomo

Issue:
GAMS solver initial expression evaluation checking
When GAMS is run on a model, it requires that all expressions evaluate at their initial values. So, for example, if you don't warmstart a variable `x` and then you have a constraint that contains `log(x)`, then GAMS will have an `Execution error` because `x` is initially 0 by default and GAMS can't evaluate `log(0)`. The same is true for division by zero. To aid in this type of error, if GAMS fails a solve, then the GAMS solver will proceed to try to evaluate every expression in the model at their initial or default values, and throw a helpful warning if it finds an expression it thinks GAMS failed on. The current code to do this is as follows:

```python
try:
    value(expr)
except ValueError:
    logger.warning("While evaluating model.%s's expression, GAMS solver "
                   "encountered an error.\nGAMS requires that all "
                   "equations and expressions evaluate at initial values.\n"
                   "Ensure variable values do not violate any domains, "
                   "and use the warmstart=True keyword to solve()." % name)
    if solver_io == 'shell':
        # For shell, there is no previous exception to worry about
        # overwriting, so raise the ValueError.
        # But for direct, the GamsExceptionExecution will be raised.
        raise
```

This works for `log` and `log10`, since errors in evaluating those raise `ValueError`, but I just found out for expressions that try to divide by 0, it raises `ZeroDivisionError`, so it was not caught and the helpful warning was not sent. This will be a simple fix, but I wanted to ask, should we change the `except` to catch any error that arises from `value(expr)` (i.e. put nothing after `except`)? Or change it to include exactly `ValueError` and `ZeroDivisionError`? I wasn't sure if we wanted to anticipate more situations like this where a different kind of error we weren't expecting showed up, but I also didn't want to stomp out some other error that actually means something else went wrong not having to do with domain issues.


Discussion:
I would suggest having specific `except`s where possible. You don't want to inadvertently `except` something that you didn't mean to. We can add more cases as they come up.

Resolving patch:
diff --git a/pyomo/repn/plugins/gams_writer.py b/pyomo/repn/plugins/gams_writer.py
--- a/pyomo/repn/plugins/gams_writer.py
+++ b/pyomo/repn/plugins/gams_writer.py
@@ -133,34 +133,38 @@ def __call__(self,
                  solver_capability,
                  io_options):
         """
-        output_filename:
+        Write a model in the GAMS modeling language format.
+
+        Keyword Arguments
+        -----------------
+        output_filename: str
             Name of file to write GAMS model to. Optionally pass a file-like
             stream and the model will be written to that instead.
-        io_options:
-            warmstart=True:
+        io_options: dict
+            - warmstart=True
                 Warmstart by initializing model's variables to their values.
-            symbolic_solver_labels=False:
+            - symbolic_solver_labels=False
                 Use full Pyomo component names rather than
                 shortened symbols (slower, but useful for debugging).
-            labeler=None:
+            - labeler=None
                 Custom labeler. Incompatible with symbolic_solver_labels.
-            solver=None:
+            - solver=None
                 If None, GAMS will use default solver for model type.
-            mtype=None:
+            - mtype=None
                 Model type. If None, will chose from lp, nlp, mip, and minlp.
-            add_options=None:
+            - add_options=None
                 List of additional lines to write directly
                 into model file before the solve statement.
                 For model attributes, <model name> is GAMS_MODEL.
-            skip_trivial_constraints=False:
-                Skip writing constraints whose body section is fixed
-            file_determinism=1:
-                How much effort do we want to put into ensuring the
-                GAMS file is written deterministically for a Pyomo model:
-                   0 : None
-                   1 : sort keys of indexed components (default)
-                   2 : sort keys AND sort names (over declaration order)
-            put_results=None:
+            - skip_trivial_constraints=False
+                Skip writing constraints whose body section is fixed.
+            - file_determinism=1
+                | How much effort do we want to put into ensuring the
+                | GAMS file is written deterministically for a Pyomo model:
+                |     0 : None
+                |     1 : sort keys of indexed components (default)
+                |     2 : sort keys AND sort names (over declaration order)
+            - put_results=None
                 Filename for optionally writing solution values and
                 marginals to (put_results).dat, and solver statuses
                 to (put_results + 'stat').dat.
diff --git a/pyomo/solvers/plugins/solvers/GAMS.py b/pyomo/solvers/plugins/solvers/GAMS.py
--- a/pyomo/solvers/plugins/solvers/GAMS.py
+++ b/pyomo/solvers/plugins/solvers/GAMS.py
@@ -154,35 +154,8 @@ def solve(self, *args, **kwds):
             Print timing reports for presolve, solver, postsolve, etc.
         io_options: dict
             Options that get passed to the writer.
+            See writer in pyomo.repn.plugins.gams_writer for details.
             Updated with any other keywords passed to solve method.
-
-            - warmstart=False
-                Warmstart by initializing model's variables to their values.
-            - symbolic_solver_labels=False
-                Use full Pyomo component names rather than
-                shortened symbols (slower, but useful for debugging).
-            - labeler=None
-                Custom labeler. Incompatible with symbolic_solver_labels.
-            - solver=None
-                If None, GAMS will use default solver for model type.
-            - mtype=None
-                Model type. If None, will chose from lp, nlp, mip, and minlp.
-            - add_options=None
-                List of additional lines to write directly
-                into model file before the solve statement.
-                For model attributes, <model name> is GAMS_MODEL.
-            - skip_trivial_constraints=False
-                Skip writing constraints whose body section is fixed.
-            - file_determinism=1
-                | How much effort do we want to put into ensuring the
-                | GAMS file is written deterministically for a Pyomo model:
-                |     0 : None
-                |     1 : sort keys of indexed components (default)
-                |     2 : sort keys AND sort names (over declaration order)
-            - put_results='results'
-                Filename for optionally writing solution values and
-                marginals to (put_results).dat, and solver statuses
-                to (put_results + 'stat').dat.
         """
 
         # Make sure available() doesn't crash
@@ -641,33 +614,10 @@ def solve(self, *args, **kwds):
             Print timing reports for presolve, solver, postsolve, etc.
         io_options: dict
             Options that get passed to the writer.
+            See writer in pyomo.repn.plugins.gams_writer for details.
             Updated with any other keywords passed to solve method.
-
-            - warmstart=False
-                Warmstart by initializing model's variables to their values.
-            - symbolic_solver_labels=False
-                Use full Pyomo component names rather than
-                shortened symbols (slower, but useful for debugging).
-            - labeler=None
-                Custom labeler. Incompatible with symbolic_solver_labels.
-            - solver=None
-                If None, GAMS will use default solver for model type.
-            - mtype=None
-                Model type. If None, will chose from lp, nlp, mip, and minlp.
-            - add_options=None
-                List of additional lines to write directly
-                into model file before the solve statement.
-                For model attributes, <model name> is GAMS_MODEL.
-            - skip_trivial_constraints=False
-                Skip writing constraints whose body section is fixed.
-            - file_determinism=1
-                | How much effort do we want to put into ensuring the
-                | GAMS file is written deterministically for a Pyomo model:
-                |     0 : None
-                |     1 : sort keys of indexed components (default)
-                |     2 : sort keys AND sort names (over declaration order)
-            - put_results='results'
-                Not available for modification on GAMSShell solver.
+            Note: put_results is not available for modification on
+            GAMSShell solver.
         """
 
         # Make sure available() doesn't crash
@@ -1144,7 +1094,7 @@ def check_expr(expr, name, solver_io):
     # Used to handle log and log10 violations, for example
     try:
         value(expr)
-    except ValueError:
+    except (ValueError, ZeroDivisionError):
         logger.warning("While evaluating model.%s's expression, GAMS solver "
                        "encountered an error.\nGAMS requires that all "
                        "equations and expressions evaluate at initial values.\n"


Test patch: