diff --git a/src/DynamicModel.cc b/src/DynamicModel.cc
index 421b46c9a7e2c2bfa5274f56e891cee7f0969bc6..8191719126c0b85b1f0f6091cf0448f15289c52e 100644
--- a/src/DynamicModel.cc
+++ b/src/DynamicModel.cc
@@ -2924,6 +2924,47 @@ DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput,
     }
 }
 
+void
+DynamicModel::writeDynamicJacobianNonZeroElts(const string &basename) const
+{
+  vector<pair<int, int>> nzij_pred, nzij_current, nzij_fwrd; // pairs (tsid, equation)
+  for (const auto &it : derivatives[1])
+    {
+      if (symbol_table.getType(getSymbIDByDerivID(it.first[1])) != SymbolType::endogenous)
+        continue;
+      int tsid = symbol_table.getTypeSpecificID(getSymbIDByDerivID(it.first[1]));
+      int lag = getLagByDerivID(it.first[1]);
+      if (lag == -1)
+        nzij_pred.emplace_back(tsid, it.first[0]);
+      else if (lag == 0)
+        nzij_current.emplace_back(tsid, it.first[0]);
+      else
+        nzij_fwrd.emplace_back(tsid, it.first[0]);
+    }
+  sort(nzij_pred.begin(), nzij_pred.end());
+  sort(nzij_current.begin(), nzij_current.end());
+  sort(nzij_fwrd.begin(), nzij_fwrd.end());
+
+  ofstream output{"+" + basename + "/dynamic_g1_nz.m", ios::out | ios::binary};
+  output << "function [nzij_pred, nzij_current, nzij_fwrd] = dynamic_g1_nz()" << endl
+         << "% Returns the coordinates of non-zero elements in the Jacobian, in column-major order, for each lead/lag (only for endogenous)" << endl;
+  auto print_nzij = [&output](const vector<pair<int, int>> &nzij, const string &name)    {
+      output << "  " << name << " = zeros(" << nzij.size() << ", 2, 'int32');" << endl;
+      int idx = 1;
+      for (const auto &it : nzij)
+        {
+          output << "  " << name << "(" << idx << ",1)=" << it.second+1 << ';'
+                 << " " << name << "(" << idx << ",2)=" << it.first+1 << ';' << endl;
+          idx++;
+        }
+    };
+  print_nzij(nzij_pred, "nzij_pred");
+  print_nzij(nzij_current, "nzij_current");
+  print_nzij(nzij_fwrd, "nzij_fwrd");
+  output << "end" << endl;
+  output.close();
+}
+
 void
 DynamicModel::writeOutput(ostream &output, const string &basename, bool block_decomposition, bool linear_decomposition, bool byte_code, bool use_dll, int order, bool estimation_present, bool compute_xrefs, bool julia) const
 {
diff --git a/src/DynamicModel.hh b/src/DynamicModel.hh
index 83e34e6261e4515da4a8d010f09c635723a9bf32..324fa8b78e19ecfd32bb4665dca450a54e537f73 100644
--- a/src/DynamicModel.hh
+++ b/src/DynamicModel.hh
@@ -365,6 +365,10 @@ public:
   //! Writes file containing parameters derivatives
   void writeParamsDerivativesFile(const string &basename, bool julia) const;
 
+  //! Writes file containing coordinates of non-zero elements in the Jacobian
+  /*! Used by the perfect_foresight_stacked_jacobian MEX */
+  void writeDynamicJacobianNonZeroElts(const string &basename) const;
+
   //! Converts to nonlinear model (only the equations)
   /*! It assumes that the nonlinear model given in argument has just been allocated */
   void toNonlinearPart(DynamicModel &non_linear_equations_dynamic_model) const;
diff --git a/src/ModFile.cc b/src/ModFile.cc
index b4f85ea0e65b5292d977c82cfe843af43bf890d5..159bf6b6bfa933d6c0f1eea14fa654c0aa29e482 100644
--- a/src/ModFile.cc
+++ b/src/ModFile.cc
@@ -1101,6 +1101,8 @@ ModFile::writeOutputFiles(const string &basename, bool clear_all, bool clear_glo
           dynamic_model.writeDynamicFile(basename, block, false, byte_code, use_dll, mexext, matlabroot, dynareroot, mod_file_struct.order_option, false);
 
           dynamic_model.writeParamsDerivativesFile(basename, false);
+
+          dynamic_model.writeDynamicJacobianNonZeroElts(basename);
         }
 
       // Create steady state file