diff --git a/src/DynamicModel.cc b/src/DynamicModel.cc
index 90451ed2ca606cc9c7cf284a7c869c261b7ed9a6..1525b30ab301ae83b2f97e1a0168f06b0b2b32fd 100644
--- a/src/DynamicModel.cc
+++ b/src/DynamicModel.cc
@@ -3228,7 +3228,7 @@ DynamicModel::writeOutput(ostream &output, const string &basename, bool block_de
 
           tmp_s.str("");
           count_lead_lag_incidence = 0;
-          dynamic_jacob_map_t reordered_dynamic_jacobian;
+          map<tuple<int, int, int>, expr_t> reordered_dynamic_jacobian;
           for (const auto &[idx, d] : blocks_derivatives[block])
             reordered_dynamic_jacobian[{ get<2>(idx), get<1>(idx), get<0>(idx) }] = d;
           output << "block_structure.block(" << block+1 << ").lead_lag_incidence = [];" << endl;
@@ -4812,7 +4812,7 @@ DynamicModel::computingPass(bool jacobianExo, int derivsOrder, int paramsDerivsO
 
       computeNonSingularNormalization(contemporaneous_jacobian, cutoff, static_jacobian);
 
-      computePrologueAndEpilogue(static_jacobian);
+      computePrologueAndEpilogue();
 
       auto first_order_endo_derivatives = collectFirstOrderDerivativesEndogenous();
 
@@ -4820,7 +4820,7 @@ DynamicModel::computingPass(bool jacobianExo, int derivsOrder, int paramsDerivsO
 
       cout << "Finding the optimal block decomposition of the model ..." << endl;
 
-      auto variable_lag_lead = computeBlockDecompositionAndFeedbackVariablesForEachBlock(static_jacobian, false);
+      auto variable_lag_lead = computeBlockDecompositionAndFeedbackVariablesForEachBlock();
 
       reduceBlocksAndTypeDetermination(linear_decomposition);
 
@@ -4971,26 +4971,31 @@ DynamicModel::determineBlockDerivativesType(int blk)
   int nb_recursive = blocks[blk].getRecursiveSize();
   for (int lag = -blocks[blk].max_lag; lag <= blocks[blk].max_lead; lag++)
     for (int eq = 0; eq < size; eq++)
-      for (int var = 0; var < size; var++)
-        if (int eq_orig = getBlockEquationID(blk, eq), var_orig = getBlockVariableID(blk, var);
-            dynamic_jacobian.find({ lag, eq_orig, var_orig }) != dynamic_jacobian.end())
-          {
-            if (getBlockEquationType(blk, eq) == EquationType::evaluate_s
-                && eq < nb_recursive)
-              /* It’s a normalized recursive equation, we have to recompute
-                 the derivative using the chain rule */
-              derivType[{ lag, eq, var }] = BlockDerivativeType::normalizedChainRule;
-            else if (derivType.find({ lag, eq, var }) == derivType.end())
-              derivType[{ lag, eq, var }] = BlockDerivativeType::standard;
-
-            if (var < nb_recursive)
-              for (int feedback_var = nb_recursive; feedback_var < size; feedback_var++)
-                if (derivType.find({ lag, var, feedback_var }) != derivType.end())
-                  /* A new derivative needs to be computed using the chain rule
-                     (a feedback variable appears in the recursive equation
-                     defining the current variable) */
-                  derivType[{ lag, eq, feedback_var }] = BlockDerivativeType::chainRule;
-          }
+      {
+        set<pair<int, int>> endos_and_lags;
+        int eq_orig = getBlockEquationID(blk, eq);
+        equations[eq_orig]->collectEndogenous(endos_and_lags);
+        for (int var = 0; var < size; var++)
+          if (int var_orig = getBlockVariableID(blk, var);
+              endos_and_lags.find({ var_orig, lag }) != endos_and_lags.end())
+            {
+              if (getBlockEquationType(blk, eq) == EquationType::evaluate_s
+                  && eq < nb_recursive)
+                /* It’s a normalized recursive equation, we have to recompute
+                   the derivative using the chain rule */
+                derivType[{ lag, eq, var }] = BlockDerivativeType::normalizedChainRule;
+              else if (derivType.find({ lag, eq, var }) == derivType.end())
+                derivType[{ lag, eq, var }] = BlockDerivativeType::standard;
+
+              if (var < nb_recursive)
+                for (int feedback_var = nb_recursive; feedback_var < size; feedback_var++)
+                  if (derivType.find({ lag, var, feedback_var }) != derivType.end())
+                    /* A new derivative needs to be computed using the chain rule
+                       (a feedback variable appears in the recursive equation
+                       defining the current variable) */
+                    derivType[{ lag, eq, feedback_var }] = BlockDerivativeType::chainRule;
+            }
+      }
   return derivType;
 }
 
diff --git a/src/ModelTree.cc b/src/ModelTree.cc
index 7c69901e69cc111185259012f01bf11ad45fb97b..f92e4db9ca6153caad1b696deae32df94b37adad 100644
--- a/src/ModelTree.cc
+++ b/src/ModelTree.cc
@@ -101,8 +101,6 @@ ModelTree::copyHelper(const ModelTree &m)
     trend_symbols_map[it.first] = f(it.second);
   for (const auto &it : m.nonstationary_symbols_map)
     nonstationary_symbols_map[it.first] = {it.second.first, f(it.second.second)};
-  for (const auto &it : m.dynamic_jacobian)
-    dynamic_jacobian[it.first] = f(it.second);
 
   for (const auto &it : m.equation_type_and_normalized_equation)
     equation_type_and_normalized_equation.emplace_back(it.first, f(it.second));
@@ -200,7 +198,6 @@ ModelTree::operator=(const ModelTree &m)
   trend_symbols_map.clear();
   nonstationary_symbols_map.clear();
 
-  dynamic_jacobian.clear();
   eq_idx_block2orig = m.eq_idx_block2orig;
   endo_idx_block2orig = m.endo_idx_block2orig;
   eq_idx_orig2block = m.eq_idx_orig2block;
@@ -285,7 +282,7 @@ ModelTree::computeNormalization(const jacob_map_t &contemporaneous_jacobian, boo
 }
 
 void
-ModelTree::computeNonSingularNormalization(jacob_map_t &contemporaneous_jacobian, double cutoff, jacob_map_t &static_jacobian)
+ModelTree::computeNonSingularNormalization(const jacob_map_t &contemporaneous_jacobian, double cutoff, const jacob_map_t &static_jacobian)
 {
   cout << "Normalizing the model..." << endl;
 
@@ -338,33 +335,6 @@ ModelTree::computeNonSingularNormalization(jacob_map_t &contemporaneous_jacobian
          TODO: Explain why symbolic_jacobian is not contemporaneous. */
       auto symbolic_jacobian = computeSymbolicJacobian();
       found_normalization = computeNormalization(symbolic_jacobian, true);
-      if (found_normalization)
-        {
-          /* Update the Jacobian matrices by ensuring that they have a
-             numerical value associated to each symbolic occurrence.
-             TODO: Explain why this is needed. Maybe in order to have the
-             right incidence matrix in computePrologueAndEpilogue? */
-          for (const auto &[eq_and_endo, ignore] : symbolic_jacobian)
-            {
-              if (static_jacobian.find(eq_and_endo) == static_jacobian.end())
-                static_jacobian[eq_and_endo] = 0;
-              if (dynamic_jacobian.find({ 0, eq_and_endo.first, eq_and_endo.second }) == dynamic_jacobian.end())
-                dynamic_jacobian[{ 0, eq_and_endo.first, eq_and_endo.second }] = Zero;
-              if (contemporaneous_jacobian.find(eq_and_endo) == contemporaneous_jacobian.end())
-                contemporaneous_jacobian[eq_and_endo] = 0;
-              try
-                {
-                  if (derivatives[1].find({ eq_and_endo.first, getDerivID(symbol_table.getID(SymbolType::endogenous, eq_and_endo.second), 0) }) == derivatives[1].end())
-                    derivatives[1][{ eq_and_endo.first, getDerivID(symbol_table.getID(SymbolType::endogenous, eq_and_endo.second), 0) }] = Zero;
-                }
-              catch (DataTree::UnknownDerivIDException &e)
-                {
-                  cerr << "The variable " << symbol_table.getName(symbol_table.getID(SymbolType::endogenous, eq_and_endo.second))
-                       << " does not appear at the current period (i.e. with no lead and no lag); this case is not handled by the 'block' option of the 'model' block." << endl;
-                  exit(EXIT_FAILURE);
-                }
-            }
-        }
     }
 
   if (!found_normalization)
@@ -375,7 +345,7 @@ ModelTree::computeNonSingularNormalization(jacob_map_t &contemporaneous_jacobian
 }
 
 pair<ModelTree::jacob_map_t, ModelTree::jacob_map_t>
-ModelTree::evaluateAndReduceJacobian(const eval_context_t &eval_context, double cutoff, bool verbose)
+ModelTree::evaluateAndReduceJacobian(const eval_context_t &eval_context, double cutoff, bool verbose) const
 {
   jacob_map_t contemporaneous_jacobian, static_jacobian;
   int nb_elements_contemporaneous_jacobian = 0;
@@ -423,17 +393,10 @@ ModelTree::evaluateAndReduceJacobian(const eval_context_t &eval_context, double
                 static_jacobian[{ eq, var }] += val;
               else
                 static_jacobian[{ eq, var }] = val;
-
-              dynamic_jacobian[{ lag, eq, var }] = d1;
             }
         }
     }
 
-  // Get rid of the elements of the Jacobian matrix below the cutoff
-  // TODO: Why?
-  for (const auto &it : jacobian_elements_to_delete)
-    derivatives[1].erase(it);
-
   if (jacobian_elements_to_delete.size() > 0)
     {
       cout << jacobian_elements_to_delete.size() << " elements among " << derivatives[1].size() << " in the incidence matrices are below the cutoff (" << cutoff << ") and are discarded." << endl
@@ -514,7 +477,7 @@ ModelTree::computeNaturalNormalization()
 }
 
 void
-ModelTree::computePrologueAndEpilogue(const jacob_map_t &static_jacobian)
+ModelTree::computePrologueAndEpilogue()
 {
   const int n = equations.size();
 
@@ -533,17 +496,13 @@ ModelTree::computePrologueAndEpilogue(const jacob_map_t &static_jacobian)
      “eq_idx_block2orig” (resp. “endo_idx_block2orig”). Stored in row-major
      order. */
   vector<bool> IM(n*n, false);
-  if (cutoff == 0)
-    for (int i = 0; i < n; i++)
-      {
-        set<pair<int, int>> endos_and_lags;
-        equations[i]->collectEndogenous(endos_and_lags);
-        for (const auto &[endo, lag] : endos_and_lags)
-          IM[i * n + endo2eq[endo]] = true;
-      }
-  else
-    for (const auto &[eq_and_endo, val] : static_jacobian)
-      IM[eq_and_endo.first * n + endo2eq[eq_and_endo.second]] = true;
+  for (int i = 0; i < n; i++)
+    {
+      set<pair<int, int>> endos_and_lags;
+      equations[i]->collectEndogenous(endos_and_lags);
+      for (auto [endo, lag] : endos_and_lags)
+        IM[i * n + endo2eq[endo]] = true;
+    }
 
   bool something_has_been_done;
   // Find the prologue equations and place first the AR(1) shock equations first
@@ -677,22 +636,24 @@ ModelTree::getVariableLeadLagByBlock(const vector<int> &endo2simblock) const
                               };
 
   lag_lead_vector_t variable_lag_lead(nb_endo, { 0, 0 }), equation_lag_lead(nb_endo, { 0, 0 });
-  for (const auto &[key, value] : dynamic_jacobian)
+  for (int eq = 0; eq < nb_endo; eq++)
     {
-      auto [lag, eq, endo] = key;
-      if (belong_to_same_block(endo, eq))
-        {
-          variable_lag_lead[endo].first = max(variable_lag_lead[endo].first, -lag);
-          variable_lag_lead[endo].second = max(variable_lag_lead[endo].second, lag);
-          equation_lag_lead[eq].first = max(equation_lag_lead[eq].first, -lag);
-          equation_lag_lead[eq].second = max(equation_lag_lead[eq].second, lag);
-        }
+      set<pair<int, int>> endos_and_lags;
+      equations[eq]->collectEndogenous(endos_and_lags);
+      for (auto [endo, lag] : endos_and_lags)
+        if (belong_to_same_block(endo, eq))
+          {
+            variable_lag_lead[endo].first = max(variable_lag_lead[endo].first, -lag);
+            variable_lag_lead[endo].second = max(variable_lag_lead[endo].second, lag);
+            equation_lag_lead[eq].first = max(equation_lag_lead[eq].first, -lag);
+            equation_lag_lead[eq].second = max(equation_lag_lead[eq].second, lag);
+          }
     }
   return { equation_lag_lead, variable_lag_lead };
 }
 
 lag_lead_vector_t
-ModelTree::computeBlockDecompositionAndFeedbackVariablesForEachBlock(const jacob_map_t &static_jacobian, bool verbose_)
+ModelTree::computeBlockDecompositionAndFeedbackVariablesForEachBlock()
 {
   int nb_var = symbol_table.endo_nbr();
   int nb_simvars = nb_var - prologue - epilogue;
@@ -700,11 +661,10 @@ ModelTree::computeBlockDecompositionAndFeedbackVariablesForEachBlock(const jacob
   /* Construct the graph representing the dependencies between all
      variables that do not belong to the prologue or the epilogue.
 
-     For detecting dependencies between variables, use the static jacobian,
-     except when the cutoff is zero, in which case use the symbolic adjacency
+     For detecting dependencies between variables, use the symbolic adjacency
      matrix */
   VariableDependencyGraph G(nb_simvars);
-  for (const auto &[key, value] : cutoff == 0 ? computeSymbolicJacobian() : static_jacobian)
+  for (const auto &[key, value] : computeSymbolicJacobian())
     {
       auto [eq, endo] = key;
       if (eq_idx_orig2block[eq] >= prologue
@@ -869,29 +829,14 @@ ModelTree::reduceBlocksAndTypeDetermination(bool linear_decomposition)
           set<pair<int, int>> endos_and_lags;
           equations[eq_idx_block2orig[eq]]->collectEndogenous(endos_and_lags);
           for (const auto &[endo, lag] : endos_and_lags)
-            {
-              if (linear_decomposition)
-                {
-                  if (dynamic_jacobian.find({ lag, eq_idx_block2orig[eq], endo })
-                      != dynamic_jacobian.end())
-                    {
-                      max_lead = max(lag, max_lead);
-                      max_lag = max(-lag, max_lag);
-                    }
-                }
-              else
-                {
-                  if (find(endo_idx_block2orig.begin()+first_eq,
-                           endo_idx_block2orig.begin()+first_eq+blocks[i].size, endo)
-                      != endo_idx_block2orig.begin()+first_eq+blocks[i].size
-                      && dynamic_jacobian.find({ lag, eq_idx_block2orig[eq], endo })
-                      != dynamic_jacobian.end())
-                    {
-                      max_lead = max(lag, max_lead);
-                      max_lag = max(-lag, max_lag);
-                    }
-                }
-            }
+            if (linear_decomposition ||
+                find(endo_idx_block2orig.begin()+first_eq,
+                        endo_idx_block2orig.begin()+first_eq+blocks[i].size, endo)
+                != endo_idx_block2orig.begin()+first_eq+blocks[i].size)
+              {
+                max_lead = max(lag, max_lead);
+                max_lag = max(-lag, max_lag);
+              }
         }
 
       // Determine the block type
@@ -940,12 +885,10 @@ ModelTree::reduceBlocksAndTypeDetermination(bool linear_decomposition)
               for (int j = blocks[i-1].first_equation;
                    j < blocks[i-1].first_equation+blocks[i-1].size; j++)
                 {
-                  if (dynamic_jacobian.find({ -1, eq_idx_block2orig[first_eq], endo_idx_block2orig[j] })
-                      != dynamic_jacobian.end())
-                    is_lag = true;
-                  if (dynamic_jacobian.find({ +1, eq_idx_block2orig[first_eq], endo_idx_block2orig[j] })
-                      != dynamic_jacobian.end())
-                    is_lead = true;
+                  set<pair<int, int>> endos_and_lags;
+                  equations[eq_idx_block2orig[first_eq]]->collectEndogenous(endos_and_lags);
+                  is_lag = endos_and_lags.find({ endo_idx_block2orig[j], -1 }) != endos_and_lags.end();
+                  is_lead = endos_and_lags.find({ endo_idx_block2orig[j], 1 }) != endos_and_lags.end();
                 }
 
               BlockSimulationType prev_Type = blocks[i-1].simulation_type;
diff --git a/src/ModelTree.hh b/src/ModelTree.hh
index 7166e3cf35d65e5d159a4e9a6ad15fbbf272545d..2bffcf811748304040b9e27f1c97040c6aa80234 100644
--- a/src/ModelTree.hh
+++ b/src/ModelTree.hh
@@ -155,13 +155,6 @@ protected:
   //! Nonstationary variables and their deflators
   nonstationary_symbols_map_t nonstationary_symbols_map;
 
-  //! Sparse matrix of double to store the values of the Jacobian
-  /*! First index is lag, second index is equation number, third index is endogenous type specific ID */
-  using dynamic_jacob_map_t = map<tuple<int, int, int>, expr_t>;
-
-  //! The jacobian without the elements below the cutoff
-  dynamic_jacob_map_t dynamic_jacobian;
-
   /* Maps indices of equations in the block-decomposition order into original
      equation IDs */
   vector<int> eq_idx_block2orig;
@@ -255,7 +248,7 @@ protected:
   //! Writes LaTeX model file
   void writeLatexModelFile(const string &mod_basename, const string &latex_basename, ExprNodeOutputType output_type, bool write_equation_tags) const;
 
-  //! Sparse matrix of double to store the values of the Jacobian
+  //! Sparse matrix of double to store the values of the static Jacobian
   /*! First index is equation number, second index is endogenous type specific ID */
   using jacob_map_t = map<pair<int, int>, double>;
 
@@ -287,24 +280,24 @@ protected:
     If no matching is found with a zero cutoff, an error message is printed.
     The resulting normalization is stored in endo2eq.
   */
-  void computeNonSingularNormalization(jacob_map_t &contemporaneous_jacobian, double cutoff, jacob_map_t &static_jacobian);
+  void computeNonSingularNormalization(const jacob_map_t &contemporaneous_jacobian, double cutoff, const jacob_map_t &static_jacobian);
   //! Try to find a natural normalization if all equations are matched to an endogenous variable on the LHS
   bool computeNaturalNormalization();
   //! Evaluate the jacobian (w.r.t. endogenous) and suppress all the elements below the cutoff
-  /*! Returns a pair (contemporaneous_jacobian, static_jacobian). Also fills
-    dynamic_jacobian. Elements below the cutoff are discarded. External functions are evaluated to 1. */
-  pair<jacob_map_t, jacob_map_t> evaluateAndReduceJacobian(const eval_context_t &eval_context, double cutoff, bool verbose);
+  /*! Returns a pair (contemporaneous_jacobian, static_jacobian).
+      Elements below the cutoff are discarded. External functions are evaluated to 1. */
+  pair<jacob_map_t, jacob_map_t> evaluateAndReduceJacobian(const eval_context_t &eval_context, double cutoff, bool verbose) const;
   //! Select and reorder the non linear equations of the model
   void select_non_linear_equations_and_variables();
   //! Search the equations and variables belonging to the prologue and the epilogue of the model
-  void computePrologueAndEpilogue(const jacob_map_t &static_jacobian);
+  void computePrologueAndEpilogue();
   //! Determine the type of each equation of model and try to normalize the unnormalized equation
   void equationTypeDetermination(const map<tuple<int, int, int>, expr_t> &first_order_endo_derivatives, int mfs);
   /* Compute the block decomposition and for a non-recusive block find the minimum feedback set
 
      Initializes the “blocks” structure, and fills the following fields: size,
      mfs_size, n_static, n_forward, n_backward, n_mixed. */
-  lag_lead_vector_t computeBlockDecompositionAndFeedbackVariablesForEachBlock(const jacob_map_t &static_jacobian, bool verbose_);
+  lag_lead_vector_t computeBlockDecompositionAndFeedbackVariablesForEachBlock();
   /* Reduce the number of block by merging the same type of equations in the
      prologue and the epilogue, and determine the type of each block.
 
diff --git a/src/StaticModel.cc b/src/StaticModel.cc
index 3e76cddf14992d974356960121f4687854e6d559..e7c84cad8c3c8dde43f37de6aafcb7ae053d9b5b 100644
--- a/src/StaticModel.cc
+++ b/src/StaticModel.cc
@@ -1125,7 +1125,7 @@ StaticModel::computingPass(int derivsOrder, int paramsDerivsOrder, const eval_co
 
       computeNonSingularNormalization(contemporaneous_jacobian, cutoff, static_jacobian);
 
-      computePrologueAndEpilogue(static_jacobian);
+      computePrologueAndEpilogue();
 
       auto first_order_endo_derivatives = collectFirstOrderDerivativesEndogenous();
 
@@ -1133,7 +1133,7 @@ StaticModel::computingPass(int derivsOrder, int paramsDerivsOrder, const eval_co
 
       cout << "Finding the optimal block decomposition of the model ..." << endl;
 
-      auto variable_lag_lead = computeBlockDecompositionAndFeedbackVariablesForEachBlock(static_jacobian, false);
+      auto variable_lag_lead = computeBlockDecompositionAndFeedbackVariablesForEachBlock();
 
       reduceBlocksAndTypeDetermination(false);