diff --git a/src/DynamicModel.cc b/src/DynamicModel.cc
index 2cd7fdd5976b39da14c2adca56598adeb53d18d0..a34e356cdec38a35bd49533e2deb1f6bf9816ad3 100644
--- a/src/DynamicModel.cc
+++ b/src/DynamicModel.cc
@@ -3293,34 +3293,16 @@ DynamicModel::computingPass(bool jacobianExo, int derivsOrder, int paramsDerivsO
   if (paramsDerivsOrder > 0 && !no_tmp_terms)
     computeParamsDerivativesTemporaryTerms();
 
-  if (!computingPassBlock(eval_context, no_tmp_terms) && block)
+  computingPassBlock(eval_context, no_tmp_terms);
+  if (block_decomposed)
+    computeBlockDynJacobianCols();
+  if (!block_decomposed && block)
     {
       cerr << "ERROR: Block decomposition requested but failed." << endl;
       exit(EXIT_FAILURE);
     }
 }
 
-bool
-DynamicModel::computingPassBlock(const eval_context_t &eval_context, bool no_tmp_terms)
-{
-  auto contemporaneous_jacobian = evaluateAndReduceJacobian(eval_context);
-  if (!computeNonSingularNormalization(contemporaneous_jacobian))
-    return false;
-  auto [prologue, epilogue] = computePrologueAndEpilogue();
-  auto first_order_endo_derivatives = collectFirstOrderDerivativesEndogenous();
-  equationTypeDetermination(first_order_endo_derivatives, mfs);
-  cout << "Finding the optimal block decomposition of the " << modelClassName() << "..." << endl;
-  computeBlockDecomposition(prologue, epilogue);
-  reduceBlockDecomposition();
-  printBlockDecomposition();
-  computeChainRuleJacobian();
-  determineLinearBlocks();
-  computeBlockDynJacobianCols();
-  if (!no_tmp_terms)
-    computeBlockTemporaryTerms();
-  return true;
-}
-
 void
 DynamicModel::computeXrefs()
 {
@@ -3462,9 +3444,11 @@ DynamicModel::determineBlockDerivativesType(int blk)
 void
 DynamicModel::computeChainRuleJacobian()
 {
-  int nb_blocks = blocks.size();
+  size_t nb_blocks { blocks.size() };
+
   blocks_derivatives.resize(nb_blocks);
-  for (int blk = 0; blk < nb_blocks; blk++)
+
+  for (int blk {0}; blk < static_cast<int>(nb_blocks); blk++)
     {
       int nb_recursives = blocks[blk].getRecursiveSize();
 
@@ -3507,52 +3491,71 @@ DynamicModel::computeChainRuleJacobian()
             blocks_derivatives[blk][{ eq, var, lag }] = d;
         }
     }
-}
 
-void
-DynamicModel::computeBlockDynJacobianCols()
-{
-  int nb_blocks = blocks.size();
+  /* Also store information and derivatives w.r.t. other types of variables
+     (for the stochastic mode) */
   blocks_derivatives_other_endo.resize(nb_blocks);
   blocks_derivatives_exo.resize(nb_blocks);
   blocks_derivatives_exo_det.resize(nb_blocks);
   blocks_other_endo.resize(nb_blocks);
   blocks_exo.resize(nb_blocks);
   blocks_exo_det.resize(nb_blocks);
+  for (auto &[indices, d1] : derivatives[1])
+    {
+      auto [eq_orig, deriv_id] { vectorToTuple<2>(indices) };
+      int block_eq { eq2block[eq_orig] };
+      int eq { getBlockInitialEquationID(block_eq, eq_orig) };
+      int var { getTypeSpecificIDByDerivID(deriv_id) };
+      int lag { getLagByDerivID(deriv_id) };
+      switch (getTypeByDerivID(indices[1]))
+        {
+        case SymbolType::endogenous:
+          if (block_eq != endo2block[var])
+            {
+              blocks_derivatives_other_endo[block_eq][{ eq, var, lag }] = d1;
+              blocks_other_endo[block_eq].insert(var);
+            }
+          break;
+        case SymbolType::exogenous:
+          blocks_derivatives_exo[block_eq][{ eq, var, lag }] = d1;
+          blocks_exo[block_eq].insert(var);
+          break;
+        case SymbolType::exogenousDet:
+          blocks_derivatives_exo_det[block_eq][{ eq, var, lag }] = d1;
+          blocks_exo_det[block_eq].insert(var);
+          break;
+        default:
+          break;
+        }
+    }
+}
+
+void
+DynamicModel::computeBlockDynJacobianCols()
+{
+  size_t nb_blocks { blocks.size() };
   // Structures used for lexicographic ordering over (lag, var ID)
   vector<set<pair<int, int>>> dynamic_endo(nb_blocks), dynamic_other_endo(nb_blocks),
     dynamic_exo(nb_blocks), dynamic_exo_det(nb_blocks);
 
   for (auto & [indices, d1] : derivatives[1])
     {
-      int eq_orig = indices[0];
-      int block_eq = eq2block[eq_orig];
-      int eq = getBlockInitialEquationID(block_eq, eq_orig);
-      int var { getTypeSpecificIDByDerivID(indices[1]) };
-      int lag = getLagByDerivID(indices[1]);
-      switch (getTypeByDerivID(indices[1]))
+      auto [eq_orig, deriv_id] { vectorToTuple<2>(indices) };
+      int block_eq { eq2block[eq_orig] };
+      int var { getTypeSpecificIDByDerivID(deriv_id) };
+      int lag { getLagByDerivID(deriv_id) };
+      switch (getTypeByDerivID(deriv_id))
         {
         case SymbolType::endogenous:
           if (block_eq == endo2block[var])
-            {
-              int var_in_block = getBlockInitialVariableID(block_eq, var);
-              dynamic_endo[block_eq].emplace(lag, var_in_block);
-            }
+            dynamic_endo[block_eq].emplace(lag, getBlockInitialVariableID(block_eq, var));
           else
-            {
-              blocks_derivatives_other_endo[block_eq][{ eq, var, lag }] = derivatives[1][{ eq_orig, getDerivID(symbol_table.getID(SymbolType::endogenous, var), lag) }];
-              blocks_other_endo[block_eq].insert(var);
-              dynamic_other_endo[block_eq].emplace(lag, var);
-            }
+            dynamic_other_endo[block_eq].emplace(lag, var);
           break;
         case SymbolType::exogenous:
-          blocks_derivatives_exo[block_eq][{ eq, var, lag }] = derivatives[1][{ eq_orig, getDerivID(symbol_table.getID(SymbolType::exogenous, var), lag) }];
-          blocks_exo[block_eq].insert(var);
           dynamic_exo[block_eq].emplace(lag, var);
           break;
         case SymbolType::exogenousDet:
-          blocks_derivatives_exo_det[block_eq][{ eq, var, lag }] = derivatives[1][{ eq_orig, getDerivID(symbol_table.getID(SymbolType::exogenousDet, var), lag) }];
-          blocks_exo_det[block_eq].insert(var);
           dynamic_exo_det[block_eq].emplace(lag, var);
           break;
         default:
@@ -3565,7 +3568,7 @@ DynamicModel::computeBlockDynJacobianCols()
   blocks_jacob_cols_other_endo.resize(nb_blocks);
   blocks_jacob_cols_exo.resize(nb_blocks);
   blocks_jacob_cols_exo_det.resize(nb_blocks);
-  for (int blk = 0; blk < nb_blocks; blk++)
+  for (size_t blk {0}; blk < nb_blocks; blk++)
     {
       for (int index{0};
            auto [lag, var] : dynamic_endo[blk])
diff --git a/src/DynamicModel.hh b/src/DynamicModel.hh
index b6a3fe780966cc14f6fe26ac02cd67c70b5c5e46..9e0c1780e0ce4315fa975c79a69361de41136895 100644
--- a/src/DynamicModel.hh
+++ b/src/DynamicModel.hh
@@ -169,8 +169,7 @@ private:
      between 0 and blocks[blk].size-1). */
   map<tuple<int, int, int>, BlockDerivativeType> determineBlockDerivativesType(int blk);
 
-  //! Computes chain rule derivatives of the Jacobian w.r. to endogenous variables
-  void computeChainRuleJacobian();
+  void computeChainRuleJacobian() override;
 
   string reform(const string &name) const;
 
@@ -299,12 +298,6 @@ private:
     return blocks_jacob_cols_endo[blk].at({ var, lag });
   }
 
-  /* Compute block decomposition, its derivatives and temporary terms.
-     Meant to be overriden in derived classes which don’t support block
-     decomposition (currently Epilogue). Returns a boolean indicating success
-     (failure can happen in normalization). */
-  virtual bool computingPassBlock(const eval_context_t &eval_context, bool no_tmp_terms);
-
 protected:
   string
   modelClassName() const override
diff --git a/src/ModelEquationBlock.cc b/src/ModelEquationBlock.cc
index 58db1c8a1aacdc205eae895f47c016a8b44529ab..dfe4ef54d9cf1dd6a4aa4ada3a2469b35c7d93b5 100644
--- a/src/ModelEquationBlock.cc
+++ b/src/ModelEquationBlock.cc
@@ -30,12 +30,11 @@ PlannerObjective::PlannerObjective(SymbolTable &symbol_table_arg,
 {
 }
 
-bool
+void
 PlannerObjective::computingPassBlock([[maybe_unused]] const eval_context_t &eval_context,
                                      [[maybe_unused]] bool no_tmp_terms)
 {
   // Disable block decomposition on planner objective
-  return false;
 }
 
 OrigRamseyDynamicModel::OrigRamseyDynamicModel(SymbolTable &symbol_table_arg,
@@ -521,10 +520,9 @@ Epilogue::writeOutput(ostream &output) const
   SymbolList{move(symbol_list)}.writeOutput("M_.epilogue_var_list_", output);
 }
 
-bool
+void
 Epilogue::computingPassBlock([[maybe_unused]] const eval_context_t &eval_context,
                              [[maybe_unused]] bool no_tmp_terms)
 {
   // Disable block decomposition on epilogue blocks
-  return false;
 }
diff --git a/src/ModelEquationBlock.hh b/src/ModelEquationBlock.hh
index a34fb6dcfcf887dab547747823c225de00f210ec..8efe6aaf7aa24f98132b7e7afe7416bd04ece960 100644
--- a/src/ModelEquationBlock.hh
+++ b/src/ModelEquationBlock.hh
@@ -40,7 +40,7 @@ protected:
   }
 
 private:
-  bool computingPassBlock(const eval_context_t &eval_context, bool no_tmp_terms) override;
+  void computingPassBlock(const eval_context_t &eval_context, bool no_tmp_terms) override;
 };
 
 class OrigRamseyDynamicModel : public DynamicModel
@@ -141,7 +141,7 @@ private:
   //! Helper for public writeEpilogueFile
   void writeStaticEpilogueFile(const string &basename) const;
   void writeDynamicEpilogueFile(const string &basename) const;
-  bool computingPassBlock(const eval_context_t &eval_context, bool no_tmp_terms) override;
+  void computingPassBlock(const eval_context_t &eval_context, bool no_tmp_terms) override;
 };
 
 #endif
diff --git a/src/ModelTree.cc b/src/ModelTree.cc
index 364a1777cabab273b98c1d2e7ce6212449a9c5f3..e375c68468d0a5548536769a1de0cfbbfd3fe1fd 100644
--- a/src/ModelTree.cc
+++ b/src/ModelTree.cc
@@ -1874,3 +1874,23 @@ ModelTree::joinMEXCompilationThreads()
   for (auto &it : mex_compilation_threads)
     it.join();
 }
+
+void
+ModelTree::computingPassBlock(const eval_context_t &eval_context, bool no_tmp_terms)
+{
+  auto contemporaneous_jacobian = evaluateAndReduceJacobian(eval_context);
+  if (!computeNonSingularNormalization(contemporaneous_jacobian))
+    return;
+  auto [prologue, epilogue] = computePrologueAndEpilogue();
+  auto first_order_endo_derivatives = collectFirstOrderDerivativesEndogenous();
+  equationTypeDetermination(first_order_endo_derivatives, mfs);
+  cout << "Finding the optimal block decomposition of the " << modelClassName() << "..." << endl;
+  computeBlockDecomposition(prologue, epilogue);
+  reduceBlockDecomposition();
+  printBlockDecomposition();
+  computeChainRuleJacobian();
+  determineLinearBlocks();
+  if (!no_tmp_terms)
+    computeBlockTemporaryTerms();
+  block_decomposed = true;
+}
diff --git a/src/ModelTree.hh b/src/ModelTree.hh
index 87ac681cdd727b7efba8d52a8997db5beb2a018b..68887054021f26a37a00eff6bc707190d1fa1baa 100644
--- a/src/ModelTree.hh
+++ b/src/ModelTree.hh
@@ -189,6 +189,9 @@ protected:
     };
   };
 
+  // Whether block decomposition has been successfully computed
+  bool block_decomposed {false};
+
   // Stores various informations on the blocks
   vector<BlockInfo> blocks;
 
@@ -321,6 +324,7 @@ protected:
   //! Writes LaTeX model file
   void writeLatexModelFile(const string &mod_basename, const string &latex_basename, ExprNodeOutputType output_type, bool write_equation_tags) const;
 
+private:
   //! 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>;
@@ -394,6 +398,7 @@ protected:
   //! Determine for each block if it is linear or not
   void determineLinearBlocks();
 
+protected:
   //! Return the type of equation belonging to the block
   EquationType
   getBlockEquationType(int blk, int eq) const
@@ -444,11 +449,23 @@ protected:
   };
   //! Initialize equation_reordered & variable_reordered
   void initializeVariablesAndEquations();
+
+private:
   //! Returns the 1st derivatives w.r.t. endogenous in a different format
   /*! Returns a map (equation, type-specific ID, lag) → derivative.
       Assumes that derivatives have already been computed. */
   map<tuple<int, int, int>, expr_t> collectFirstOrderDerivativesEndogenous();
 
+protected:
+  //! Computes chain rule derivatives of the Jacobian w.r. to endogenous variables
+  virtual void computeChainRuleJacobian() = 0;
+
+  /* Compute block decomposition, its derivatives and temporary terms. Meant to
+     be overriden in derived classes which don’t support block decomposition
+     (currently Epilogue and PlannerObjective). Sets “block_decomposed” to true
+     in case of success. */
+  virtual void computingPassBlock(const eval_context_t &eval_context, bool no_tmp_terms);
+
   /* Get column number within Jacobian of a given block.
      “var” is the block-specific endogenous variable index. */
   virtual int getBlockJacobianEndoCol(int blk, int var, int lag) const = 0;
diff --git a/src/StaticModel.cc b/src/StaticModel.cc
index 33ed5c7ea850be04de1920a5d96ed830f46cb004..cc02959535ce7455b730e8491032b5c1ae1fa8f1 100644
--- a/src/StaticModel.cc
+++ b/src/StaticModel.cc
@@ -375,33 +375,14 @@ StaticModel::computingPass(int derivsOrder, int paramsDerivsOrder, const eval_co
   if (paramsDerivsOrder > 0 && !no_tmp_terms)
     computeParamsDerivativesTemporaryTerms();
 
-  if (!computingPassBlock(eval_context, no_tmp_terms) && block)
+  computingPassBlock(eval_context, no_tmp_terms);
+  if (!block_decomposed && block)
     {
       cerr << "ERROR: Block decomposition requested but failed. If your model does not have a steady state, you may want to try the 'no_static' option of the 'model' block." << endl;
       exit(EXIT_FAILURE);
     }
 }
 
-bool
-StaticModel::computingPassBlock(const eval_context_t &eval_context, bool no_tmp_terms)
-{
-  auto contemporaneous_jacobian = evaluateAndReduceJacobian(eval_context);
-  if (!computeNonSingularNormalization(contemporaneous_jacobian))
-    return false;
-  auto [prologue, epilogue] = computePrologueAndEpilogue();
-  auto first_order_endo_derivatives = collectFirstOrderDerivativesEndogenous();
-  equationTypeDetermination(first_order_endo_derivatives, mfs);
-  cout << "Finding the optimal block decomposition of the " << modelClassName() << "..." << endl;
-  computeBlockDecomposition(prologue, epilogue);
-  reduceBlockDecomposition();
-  printBlockDecomposition();
-  computeChainRuleJacobian();
-  determineLinearBlocks();
-  if (!no_tmp_terms)
-    computeBlockTemporaryTerms();
-  return true;
-}
-
 void
 StaticModel::writeStaticMFile(const string &basename) const
 {
diff --git a/src/StaticModel.hh b/src/StaticModel.hh
index 471a309e834000a3511cc7697f0453baee690b1c..d76202d10e700d4e964528971b8fccf9d8b9be37 100644
--- a/src/StaticModel.hh
+++ b/src/StaticModel.hh
@@ -91,8 +91,7 @@ private:
     return symbol_table.endo_nbr();
   }
 
-  //! Computes chain rule derivatives of the Jacobian w.r. to endogenous variables
-  void computeChainRuleJacobian();
+  void computeChainRuleJacobian() override;
 
   // Helper for writing MATLAB/Octave functions for residuals/derivatives and their temporary terms
   void writeStaticMFileHelper(const string &basename,
@@ -113,12 +112,6 @@ private:
     return var;
   }
 
-  /* Compute block decomposition, its derivatives and temporary terms.
-     Meant to be overriden in derived classes which don’t support block
-     decomposition (currently PlannerObjective). Returns a boolean indicating success
-     (failure can happen in normalization). */
-  virtual bool computingPassBlock(const eval_context_t &eval_context, bool no_tmp_terms);
-
   // Write the block structure of the model in the driver file
   void writeBlockDriverOutput(ostream &output) const;