Repository: Pyomo/pyomo

Issue:
Loading a SolverResults object warning message
This warning message (generated from [`PyomoModel.py`](https://github.com/Pyomo/pyomo/blob/0031882b6e0f4a51e7a501b5672c42b0e9af65d4/pyomo/core/base/PyomoModel.py#L226)) can read very cryptically:
```
WARNING: Loading a SolverResults object with a warning status into
    model=unknown;
        message from solver=<undefined>
```

So much so that there is a [contrib class](https://github.com/Pyomo/pyomo/blob/f607ebf301165caa8c78a3d5806fd5fcd6f946ec/pyomo/contrib/gdpopt/util.py#L45) specifically written to suppress this. There are also lots of questions on this various places online.

Part of the problem is the [initialization](https://github.com/Pyomo/pyomo/blob/1e3bb15da5107ac769bcfc029861b7b22e7c5949/pyomo/core/base/PyomoModel.py#L579) of `name` for models to "unknown".

See #1633. Will propose an improved warning soon.


Discussion:
### Proposal:

Using `instance.name` here and referencing `results.solver.Message` should probably both be removed given that they can both be completely uninformative.

I would also suggest capturing the infeasibility special case (which I suspect is actually most cases) and providing a special error message (started this in #1633)

I disagree regarding the utility of "instance.name" in this message. Unfortunately, most developers don't actually provide (or even know about, for that matter) the "name" attribute of a model. That said, various decomposition solvers (including those in pysp) do give reasonable names to models, such that it is useful to know which model solve failed. 

I agree that the "<undefined>" is useless for diagnostic purposes, but this says more about the state of the solver plugins in Pyomo than it does the message attribute. The message would be quite useful if the solver plugins extracted and populated the corresponding attribute.


@jeanpaulwatson good points. 

`instance.name` is definitely useful if there is a name, but like you said, many developers don't know about that param as it isn't obvious why it is helpful for a model object. I personally think defaulting to string "unknown" isn't really the right approach (should probably just be `None`) but that's a separate issue.

I haven't had a few minutes to sit down and bang something out but my current thought was to check both attributes and avoid including either if they will be uninformative (or extra confusing). The "unknown" and "undefined" give the impression something is wrong with the model instance, when the issue is likely just infeasibility (in the infeasibility case, I think it would be helpful to add that to the warning as a likely culprit to increase clarity).

Does that sound reasonable to you? Would only include the infeasibility case if the model's termination condition is both defined and equal to TerminationCondition.infeasible.

Resolving patch:
diff --git a/pyomo/core/base/PyomoModel.py b/pyomo/core/base/PyomoModel.py
--- a/pyomo/core/base/PyomoModel.py
+++ b/pyomo/core/base/PyomoModel.py
@@ -218,13 +218,17 @@ def load_from(self,
         # If there is a warning, then print a warning message.
         #
         if (results.solver.status == SolverStatus.warning):
+            tc = getattr(results.solver, 'termination_condition', None)
+            msg = getattr(results.solver, 'message', None)
             logger.warning(
                 'Loading a SolverResults object with a '
-                'warning status into model=%s;\n'
-                '    message from solver=%s'
-                % (instance.name, results.solver.Message))
+                'warning status into model.name="%s";\n'
+                '  - termination condition: %s\n'
+                '  - message from solver: %s'
+                % (instance.name, tc, msg))
         #
-        # If the solver status not one of either OK or Warning, then generate an error.
+        # If the solver status not one of either OK or Warning, then
+        # generate an error.
         #
         elif results.solver.status != SolverStatus.ok:
             if (results.solver.status == SolverStatus.aborted) and \


Test patch: