From 54ca5d9cc08f6f2a3bec9ff57a3808f988e9b397 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= <sebastien@dynare.org>
Date: Tue, 19 Apr 2022 17:06:37 +0200
Subject: [PATCH] Improve messages during model normalization
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

– Indicate whether we are trying to normalize the static or dynamic model
– If failed to normalize the static model, suggest to use the “no_static”
  option
– Remove a superfluous error message
---
 src/DynamicModel.cc |  2 +-
 src/ModelTree.cc    | 23 +++++++++++++++--------
 src/ModelTree.hh    |  4 ++--
 src/StaticModel.cc  |  4 ++--
 4 files changed, 20 insertions(+), 13 deletions(-)

diff --git a/src/DynamicModel.cc b/src/DynamicModel.cc
index 478e9df8..247bbad0 100644
--- a/src/DynamicModel.cc
+++ b/src/DynamicModel.cc
@@ -4245,7 +4245,7 @@ DynamicModel::computingPass(bool jacobianExo, int derivsOrder, int paramsDerivsO
     {
       auto contemporaneous_jacobian = evaluateAndReduceJacobian(eval_context);
 
-      computeNonSingularNormalization(contemporaneous_jacobian);
+      computeNonSingularNormalization(contemporaneous_jacobian, true);
 
       auto [prologue, epilogue] = computePrologueAndEpilogue();
 
diff --git a/src/ModelTree.cc b/src/ModelTree.cc
index 37319bac..a78a57b7 100644
--- a/src/ModelTree.cc
+++ b/src/ModelTree.cc
@@ -201,7 +201,7 @@ ModelTree::operator=(const ModelTree &m)
 }
 
 bool
-ModelTree::computeNormalization(const jacob_map_t &contemporaneous_jacobian, bool verbose)
+ModelTree::computeNormalization(const jacob_map_t &contemporaneous_jacobian, bool dynamic, bool verbose)
 {
   const int n = equations.size();
 
@@ -241,7 +241,7 @@ ModelTree::computeNormalization(const jacob_map_t &contemporaneous_jacobian, boo
       it != mate_map.begin() + n)
     {
       if (verbose)
-        cerr << "ERROR: Could not normalize the model. Variable "
+        cerr << "ERROR: Could not normalize the " << (dynamic ? "dynamic" : "static") << " model. Variable "
              << symbol_table.getName(symbol_table.getID(SymbolType::endogenous, it - mate_map.begin()))
              << " is not in the maximum cardinality matching." << endl;
       check = false;
@@ -250,9 +250,9 @@ ModelTree::computeNormalization(const jacob_map_t &contemporaneous_jacobian, boo
 }
 
 void
-ModelTree::computeNonSingularNormalization(const jacob_map_t &contemporaneous_jacobian)
+ModelTree::computeNonSingularNormalization(const jacob_map_t &contemporaneous_jacobian, bool dynamic)
 {
-  cout << "Normalizing the model..." << endl;
+  cout << "Normalizing the " << (dynamic ? "dynamic" : "static") << " model..." << endl;
 
   int n = equations.size();
 
@@ -284,14 +284,14 @@ ModelTree::computeNonSingularNormalization(const jacob_map_t &contemporaneous_ja
           suppressed++;
 
       if (suppressed != last_suppressed)
-        found_normalization = computeNormalization(normalized_contemporaneous_jacobian_above_cutoff, false);
+        found_normalization = computeNormalization(normalized_contemporaneous_jacobian_above_cutoff, dynamic, false);
       last_suppressed = suppressed;
       if (!found_normalization)
         {
           current_cutoff /= 2;
           // In this last case try to normalize with the complete jacobian
           if (current_cutoff <= cutoff_lower_limit)
-            found_normalization = computeNormalization(normalized_contemporaneous_jacobian, false);
+            found_normalization = computeNormalization(normalized_contemporaneous_jacobian, dynamic, false);
         }
     }
 
@@ -302,12 +302,19 @@ ModelTree::computeNonSingularNormalization(const jacob_map_t &contemporaneous_ja
          normalization even with a potential singularity.
          TODO: Explain why symbolic_jacobian is not contemporaneous. */
       auto symbolic_jacobian = computeSymbolicJacobian();
-      found_normalization = computeNormalization(symbolic_jacobian, true);
+      found_normalization = computeNormalization(symbolic_jacobian, dynamic, true);
     }
 
   if (!found_normalization)
     {
-      cerr << "No normalization could be computed. Aborting." << endl;
+      /* Some models don’t have a steady state, and this can cause the
+         normalization to fail (e.g. if some variable only appears in a diff(),
+         it will disappear from the static model). Suggest the “no_static”
+         option as a possible solution. */
+      if (!dynamic)
+        cerr << "If your model does not have a steady state, you may want to try the 'no_static' option of the 'model' block." << endl;
+      /* The last call to computeNormalization(), which was verbose, already
+         printed an error message, so we can immediately exit. */
       exit(EXIT_FAILURE);
     }
 }
diff --git a/src/ModelTree.hh b/src/ModelTree.hh
index 4c5b1810..c7f17aea 100644
--- a/src/ModelTree.hh
+++ b/src/ModelTree.hh
@@ -283,7 +283,7 @@ protected:
     \param contemporaneous_jacobian Jacobian used as an incidence matrix: all elements declared in the map (even if they are zero), are used as vertices of the incidence matrix
     \return True if a complete normalization has been achieved
   */
-  bool computeNormalization(const jacob_map_t &contemporaneous_jacobian, bool verbose);
+  bool computeNormalization(const jacob_map_t &contemporaneous_jacobian, bool dynamic, bool verbose);
 
   //! Try to compute the matching between endogenous and variable using a decreasing cutoff
   /*!
@@ -292,7 +292,7 @@ protected:
     If no matching is found with a zero cutoff, an error message is printed.
     The resulting normalization is stored in endo2eq.
   */
-  void computeNonSingularNormalization(const jacob_map_t &contemporaneous_jacobian);
+  void computeNonSingularNormalization(const jacob_map_t &contemporaneous_jacobian, bool dynamic);
   //! Evaluate the jacobian (w.r.t. endogenous) and suppress all the elements below the cutoff
   /*! Returns the contemporaneous_jacobian.
       Elements below the cutoff are discarded. External functions are evaluated to 1. */
diff --git a/src/StaticModel.cc b/src/StaticModel.cc
index 906bdca0..3123b088 100644
--- a/src/StaticModel.cc
+++ b/src/StaticModel.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2003-2021 Dynare Team
+ * Copyright © 2003-2022 Dynare Team
  *
  * This file is part of Dynare.
  *
@@ -1040,7 +1040,7 @@ StaticModel::computingPass(int derivsOrder, int paramsDerivsOrder, const eval_co
     {
       auto contemporaneous_jacobian = evaluateAndReduceJacobian(eval_context);
 
-      computeNonSingularNormalization(contemporaneous_jacobian);
+      computeNonSingularNormalization(contemporaneous_jacobian, false);
 
       auto [prologue, epilogue] = computePrologueAndEpilogue();
 
-- 
GitLab