diff --git a/new_python/genericio.cpp b/new_python/genericio.cpp
index 8719477a9dbf27d70df43db99cf430fb2eba6951..a2b1e0c6ef4e46ab37354c1c27e076d6d57bf3fd 100644
--- a/new_python/genericio.cpp
+++ b/new_python/genericio.cpp
@@ -17,14 +17,18 @@ namespace py = pybind11;
 
 class PyGenericIO : public gio::GenericIO {
 public:
-  PyGenericIO(const std::string& filename, gio::GenericIO::FileIO method=gio::GenericIO::FileIOPOSIX, gio::GenericIO::MismatchBehavior redistribute=gio::GenericIO::MismatchRedistribute)
+  PyGenericIO(
+      const std::string& filename,
+      gio::GenericIO::FileIO method=gio::GenericIO::FileIOPOSIX,
+      gio::GenericIO::MismatchBehavior redistribute=gio::GenericIO::MismatchRedistribute,
+      int eff_rank = -1)
 #ifdef GENERICIO_NO_MPI
       : gio::GenericIO(filename, method), num_ranks(0) {
 #else
       : gio::GenericIO(MPI_COMM_WORLD, filename, method), num_ranks(0) {
 #endif
     // open headers and rank info
-    openAndReadHeader(redistribute);
+    openAndReadHeader(redistribute, eff_rank);
     num_ranks = readNRanks();
     // read variable info
     getVariableInfo(variables);
@@ -62,13 +66,21 @@ public:
   }
 
   std::map<std::string, py::array> read(
-      std::optional<std::vector<std::string>> var_names, 
-      bool print_stats=true, 
-      bool collective_stats=true
+      std::optional<std::vector<std::string>> var_names,
+      bool print_stats=true,
+      bool collective_stats=true,
+      int eff_rank=-1
     ) {
+    int rank;
+  #ifdef GENERICIO_NO_MPI
+    rank = 0;
+  #else
+    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+  #endif
+
     // read number of elements
-    int64_t num_elem = readNumElems();
-    
+    int64_t num_elem = readNumElems(eff_rank);
+
     // if no argument, read all
     if(!var_names.has_value()) {
       var_names.emplace(std::vector<std::string>());
@@ -82,8 +94,8 @@ public:
 
     for(const std::string& var_name: *var_names) {
       auto varp = std::find_if(
-        variables.begin(), 
-        variables.end(), 
+        variables.begin(),
+        variables.end(),
         [&var_name](const auto& v){ return v.Name == var_name; }
         );
       if (varp != variables.end()) {
@@ -107,8 +119,10 @@ public:
         }
       }
     }
-    
-    readData(-1, print_stats, collective_stats);
+
+    readData(eff_rank, print_stats, collective_stats);
+    clearVariables();
+
   #ifndef GENERICIO_NO_MPI
     MPI_Barrier(MPI_COMM_WORLD);
   #endif
@@ -135,31 +149,38 @@ public:
     return scale;
   }
 
+  std::vector<int> get_source_ranks() {
+    std::vector<int> sr;
+    getSourceRanks(sr);
+    return sr;
+  }
+
 private:
   int num_ranks;
   std::vector<gio::GenericIO::VariableInfo> variables;
 };
 
 std::map<std::string, py::array> read_genericio(
-    std::string filename, 
-    std::optional<std::vector<std::string>> var_names, 
-    PyGenericIO::FileIO method=PyGenericIO::FileIO::FileIOPOSIX, 
-    PyGenericIO::MismatchBehavior redistribute=PyGenericIO::MismatchBehavior::MismatchRedistribute, 
+    std::string filename,
+    std::optional<std::vector<std::string>> var_names,
+    PyGenericIO::FileIO method=PyGenericIO::FileIO::FileIOPOSIX,
+    PyGenericIO::MismatchBehavior redistribute=PyGenericIO::MismatchBehavior::MismatchRedistribute,
     bool print_stats=true,
     bool collective_stats=true,
-    bool rebalance_source_ranks=false
+    bool rebalance_source_ranks=false,
+    int eff_rank=-1
   ) {
-  PyGenericIO reader(filename, method, redistribute);
+  PyGenericIO reader(filename, method, redistribute, eff_rank);
 #ifndef GENERICIO_NO_MPI
   if(rebalance_source_ranks)
     reader.rebalanceSourceRanks();
 #endif
-  return reader.read(var_names, print_stats, collective_stats);
+  return reader.read(var_names, print_stats, collective_stats, eff_rank);
 }
 
 void inspect_genericio(
-    std::string filename, 
-    PyGenericIO::FileIO method=PyGenericIO::FileIO::FileIOPOSIX, 
+    std::string filename,
+    PyGenericIO::FileIO method=PyGenericIO::FileIO::FileIOPOSIX,
     PyGenericIO::MismatchBehavior redistribute=PyGenericIO::MismatchBehavior::MismatchRedistribute
   ) {
   PyGenericIO reader(filename, method, redistribute);
@@ -168,9 +189,9 @@ void inspect_genericio(
 
 #ifndef GENERICIO_NO_MPI
 void write_genericio(
-    std::string filename, 
-    std::map<std::string, py::array> variables, 
-    std::array<double, 3> phys_scale, std::array<double, 3> phys_origin, 
+    std::string filename,
+    std::map<std::string, py::array> variables,
+    std::array<double, 3> phys_scale, std::array<double, 3> phys_origin,
     PyGenericIO::FileIO method=PyGenericIO::FileIO::FileIOPOSIX
   ) {
   // check data integrity, find particle count
@@ -197,15 +218,15 @@ void write_genericio(
   }
 
   for(auto& [name, data]: variables) {
-    if(py::isinstance<py::array_t<float>>(data)) 
+    if(py::isinstance<py::array_t<float>>(data))
       writer.addVariable(name.c_str(), reinterpret_cast<float*>(data.mutable_data()));
-    else if(py::isinstance<py::array_t<double>>(data)) 
+    else if(py::isinstance<py::array_t<double>>(data))
       writer.addVariable(name.c_str(), reinterpret_cast<double*>(data.mutable_data()));
-    else if(py::isinstance<py::array_t<int32_t>>(data)) 
+    else if(py::isinstance<py::array_t<int32_t>>(data))
       writer.addVariable(name.c_str(), reinterpret_cast<int32_t*>(data.mutable_data()));
-    else if(py::isinstance<py::array_t<int64_t>>(data)) 
+    else if(py::isinstance<py::array_t<int64_t>>(data))
       writer.addVariable(name.c_str(), reinterpret_cast<int64_t*>(data.mutable_data()));
-    else if(py::isinstance<py::array_t<uint16_t>>(data)) 
+    else if(py::isinstance<py::array_t<uint16_t>>(data))
       writer.addVariable(name.c_str(), reinterpret_cast<uint16_t*>(data.mutable_data()));
     else
       throw std::runtime_error("array dtype not supported for " + name);
@@ -228,7 +249,7 @@ PYBIND11_MODULE(pygio, m) {
     MPI_Initialized(&initialized);
     if(!initialized) {
       int level_provided;
-      MPI_Init_thread(nullptr, nullptr, MPI_THREAD_SINGLE, &level_provided); 
+      MPI_Init_thread(nullptr, nullptr, MPI_THREAD_SINGLE, &level_provided);
     }
   });
 #endif
@@ -246,9 +267,9 @@ PYBIND11_MODULE(pygio, m) {
     .value("MismatchRedistribute", PyGenericIO::MismatchBehavior::MismatchRedistribute);
 
   pyGenericIO.def(
-      py::init<std::string, PyGenericIO::FileIO, PyGenericIO::MismatchBehavior>(), 
-        py::arg("filename"), 
-        py::arg("method")=PyGenericIO::FileIO::FileIOPOSIX, 
+      py::init<std::string, PyGenericIO::FileIO, PyGenericIO::MismatchBehavior>(),
+        py::arg("filename"),
+        py::arg("method")=PyGenericIO::FileIO::FileIOPOSIX,
         py::arg("redistribute")=PyGenericIO::MismatchBehavior::MismatchRedistribute)
       .def("inspect", &PyGenericIO::inspect, "Print variable infos and size of GenericIO file")
       .def("get_variables", &PyGenericIO::get_variables, "Get a list of VariableInformations defined in the GenericIO file")
@@ -256,8 +277,14 @@ PYBIND11_MODULE(pygio, m) {
       .def("read_total_num_elems", (uint64_t (PyGenericIO::*)(void))(&PyGenericIO::readTotalNumElems))
       .def("read_phys_origin", &PyGenericIO::read_phys_origin)
       .def("read_phys_scale", &PyGenericIO::read_phys_scale)
-      .def("read", &PyGenericIO::read, py::arg("variables")=nullptr, py::arg("print_stats")=true, py::arg("collective_stats")=true)
-      .def("get_source_ranks", &PyGenericIO::getSourceRanks)
+      .def("read", &PyGenericIO::read,
+        py::arg("variables")=nullptr,
+        py::kw_only(),
+        py::arg("print_stats")=true,
+        py::arg("collective_stats")=true,
+        py::arg("eff_rank")=-1)
+      .def("get_source_ranks", &PyGenericIO::get_source_ranks)
+      .def("read_nranks", (int (PyGenericIO::*)(void))(&PyGenericIO::readNRanks))
 #ifndef GENERICIO_NO_MPI
       .def("rebalance_source_ranks", &PyGenericIO::rebalanceSourceRanks)
 #endif
@@ -273,29 +300,30 @@ PYBIND11_MODULE(pygio, m) {
                (vi.IsFloat ? "float" : "int") + " name='" + vi.Name + "'>";
       });
 
-  m.def("read_genericio", &read_genericio, 
-        py::arg("filename"), 
-        py::arg("variables")=nullptr, 
+  m.def("read_genericio", &read_genericio,
+        py::arg("filename"),
+        py::arg("variables")=nullptr,
         py::kw_only(),
-        py::arg("method")=PyGenericIO::FileIO::FileIOPOSIX, 
-        py::arg("redistribute")=PyGenericIO::MismatchBehavior::MismatchRedistribute, 
+        py::arg("method")=PyGenericIO::FileIO::FileIOPOSIX,
+        py::arg("redistribute")=PyGenericIO::MismatchBehavior::MismatchRedistribute,
         py::arg("print_stats")=true,
         py::arg("collective_stats")=true,
-        py::arg("rebalance_sourceranks")=false, 
+        py::arg("rebalance_sourceranks")=false,
+        py::arg("eff_rank")=-1,
         py::return_value_policy::move);
 
-  m.def("inspect_genericio", &inspect_genericio, 
-        py::arg("filename"), 
+  m.def("inspect_genericio", &inspect_genericio,
+        py::arg("filename"),
         py::kw_only(),
-        py::arg("method")=PyGenericIO::FileIO::FileIOPOSIX, 
+        py::arg("method")=PyGenericIO::FileIO::FileIOPOSIX,
         py::arg("redistribute")=PyGenericIO::MismatchBehavior::MismatchRedistribute);
 
 #ifndef GENERICIO_NO_MPI
-  m.def("write_genericio", &write_genericio, 
-        py::arg("filename"), 
-        py::arg("variables"), 
-        py::arg("phys_scale"), 
-        py::arg("phys_origin") = std::array<double, 3>({0., 0., 0.}), 
+  m.def("write_genericio", &write_genericio,
+        py::arg("filename"),
+        py::arg("variables"),
+        py::arg("phys_scale"),
+        py::arg("phys_origin") = std::array<double, 3>({0., 0., 0.}),
         py::kw_only(),
         py::arg("method")=PyGenericIO::FileIO::FileIOPOSIX);
 #endif