diff --git a/sPuReMD/src/reax_types.h b/sPuReMD/src/reax_types.h
index 097c99c9e3ae8fd0bae863c3f34fa471ffa00f76..8ba64f9aff7c957a4d815fc7c538201e0f9ca98d 100644
--- a/sPuReMD/src/reax_types.h
+++ b/sPuReMD/src/reax_types.h
@@ -859,6 +859,10 @@ struct reax_system
     int ffield_params_allocated;
     /* number of local (non-periodic image) atoms for the current simulation */
     int N;
+    /* number of local (non-periodic image) QM atoms for the current simulation in QMMM mode */
+    int N_qm;
+    /* number of local (non-periodic image) MM atoms for the current simulation in QMMM mode */
+    int N_mm;
     /* max. number of local (non-periodic image) atoms across all simulations */
     int N_max;
     /* dimension of the N x N sparse charge method matrix H */
diff --git a/sPuReMD/src/spuremd.c b/sPuReMD/src/spuremd.c
index 694dfc78c693f73b375a5288886a78bf642ef5ff..364b105b996ee01b0d973a1a1e63a2e361f9e213 100644
--- a/sPuReMD/src/spuremd.c
+++ b/sPuReMD/src/spuremd.c
@@ -227,6 +227,8 @@ void * setup_qmmm_( int qm_num_atoms, const int * const qm_types,
     spmd_handle->callback = NULL;
     spmd_handle->data->sim_id = 0;
 
+    spmd_handle->system->N_qm = qm_num_atoms;
+    spmd_handle->system->N_mm = mm_num_atoms;
     spmd_handle->system->N = qm_num_atoms + mm_num_atoms;
 
     PreAllocate_Space( spmd_handle->system, spmd_handle->control,
@@ -632,6 +634,8 @@ int reset_qmmm_( const void * const handle,
         spmd_handle->realloc = FALSE;
         spmd_handle->data->sim_id++;
 
+        spmd_handle->system->N_qm = qm_num_atoms;
+        spmd_handle->system->N_mm = mm_num_atoms;
         spmd_handle->system->N = qm_num_atoms + mm_num_atoms;
 
         PreAllocate_Space( spmd_handle->system, spmd_handle->control,
@@ -769,6 +773,181 @@ int reset( const void * const handle, const char * const geo_file,
 }
 
 
+/* Getter for atom positions in QMMM mode
+ *
+ * handle: pointer to wrapper struct with top-level data structures
+ * qm_pos_x: x-coordinate of QM atom positions, in Angstroms (allocated by caller)
+ * qm_pos_y: y-coordinate of QM atom positions, in Angstroms (allocated by caller)
+ * qm_pos_z: z-coordinate of QM atom positions, in Angstroms (allocated by caller)
+ * mm_pos_x: x-coordinate of MM atom positions, in Angstroms (allocated by caller)
+ * mm_pos_y: y-coordinate of MM atom positions, in Angstroms (allocated by caller)
+ * mm_pos_z: z-coordinate of MM atom positions, in Angstroms (allocated by caller)
+ *
+ * returns: SPUREMD_SUCCESS upon success, SPUREMD_FAILURE otherwise
+ */
+int get_atom_positions_qmmm_( const void * const handle, double * const qm_pos_x,
+        double * const qm_pos_y, double * const qm_pos_z, double * const mm_pos_x,
+        double * const mm_pos_y, double * const mm_pos_z )
+{
+    int i, ret;
+    spuremd_handle *spmd_handle;
+
+    ret = SPUREMD_FAILURE;
+
+    if ( handle != NULL )
+    {
+        spmd_handle = (spuremd_handle*) handle;
+
+        for ( i = 0; i < spmd_handle->system->N_qm; ++i )
+        {
+            qm_pos_x[i] = spmd_handle->system->atoms[i].x[0];
+            qm_pos_y[i] = spmd_handle->system->atoms[i].x[1];
+            qm_pos_z[i] = spmd_handle->system->atoms[i].x[2];
+        }
+
+        for ( i = spmd_handle->system->N_qm; i < spmd_handle->system->N; ++i )
+        {
+            mm_pos_x[i - spmd_handle->system->N_qm] = spmd_handle->system->atoms[i].x[0];
+            mm_pos_y[i - spmd_handle->system->N_qm] = spmd_handle->system->atoms[i].x[1];
+            mm_pos_z[i - spmd_handle->system->N_qm] = spmd_handle->system->atoms[i].x[2];
+        }
+
+        ret = SPUREMD_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+/* Getter for atom velocities in QMMM mode
+ *
+ * handle: pointer to wrapper struct with top-level data structures
+ * qm_vel_x: x-coordinate of QM atom velocities, in Angstroms / ps (allocated by caller)
+ * qm_vel_y: y-coordinate of QM atom velocities, in Angstroms / ps (allocated by caller)
+ * qm_vel_z: z-coordinate of QM atom velocities, in Angstroms / ps (allocated by caller)
+ * mm_vel_x: x-coordinate of MM atom velocities, in Angstroms / ps (allocated by caller)
+ * mm_vel_y: y-coordinate of MM atom velocities, in Angstroms / ps (allocated by caller)
+ * mm_vel_z: z-coordinate of MM atom velocities, in Angstroms / ps (allocated by caller)
+ *
+ * returns: SPUREMD_SUCCESS upon success, SPUREMD_FAILURE otherwise
+ */
+int get_atom_velocities_qmmm_( const void * const handle, double * const qm_vel_x,
+        double * const qm_vel_y, double * const qm_vel_z, double * const mm_vel_x,
+        double * const mm_vel_y, double * const mm_vel_z )
+{
+    int i, ret;
+    spuremd_handle *spmd_handle;
+
+    ret = SPUREMD_FAILURE;
+
+    if ( handle != NULL )
+    {
+        spmd_handle = (spuremd_handle*) handle;
+
+        for ( i = 0; i < spmd_handle->system->N_qm; ++i )
+        {
+            qm_vel_x[i] = spmd_handle->system->atoms[i].v[0];
+            qm_vel_y[i] = spmd_handle->system->atoms[i].v[1];
+            qm_vel_z[i] = spmd_handle->system->atoms[i].v[2];
+        }
+
+        for ( i = spmd_handle->system->N_qm; i < spmd_handle->system->N; ++i )
+        {
+            mm_vel_x[i - spmd_handle->system->N_qm] = spmd_handle->system->atoms[i].v[0];
+            mm_vel_y[i - spmd_handle->system->N_qm] = spmd_handle->system->atoms[i].v[1];
+            mm_vel_z[i - spmd_handle->system->N_qm] = spmd_handle->system->atoms[i].v[2];
+        }
+
+        ret = SPUREMD_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+/* Getter for atom forces in QMMM mode
+ *
+ * handle: pointer to wrapper struct with top-level data structures
+ * qm_f_x: x-coordinate of QM atom forces, in Angstroms * Daltons / ps^2 (allocated by caller)
+ * qm_f_y: y-coordinate of QM atom forces, in Angstroms * Daltons / ps^2 (allocated by caller)
+ * qm_f_z: z-coordinate of QM atom forces, in Angstroms * Daltons / ps^2 (allocated by caller)
+ * mm_f_x: x-coordinate of MM atom forces, in Angstroms * Daltons / ps^2 (allocated by caller)
+ * mm_f_y: y-coordinate of MM atom forces, in Angstroms * Daltons / ps^2 (allocated by caller)
+ * mm_f_z: z-coordinate of MM atom forces, in Angstroms * Daltons / ps^2 (allocated by caller)
+ *
+ * returns: SPUREMD_SUCCESS upon success, SPUREMD_FAILURE otherwise
+ */
+int get_atom_forces_qmmm_( const void * const handle, double * const qm_f_x,
+        double * const qm_f_y, double * const qm_f_z, double * const mm_f_x,
+        double * const mm_f_y, double * const mm_f_z )
+{
+    int i, ret;
+    spuremd_handle *spmd_handle;
+
+    ret = SPUREMD_FAILURE;
+
+    if ( handle != NULL )
+    {
+        spmd_handle = (spuremd_handle*) handle;
+
+        for ( i = 0; i < spmd_handle->system->N_qm; ++i )
+        {
+            qm_f_x[i] = spmd_handle->system->atoms[i].f[0];
+            qm_f_y[i] = spmd_handle->system->atoms[i].f[1];
+            qm_f_z[i] = spmd_handle->system->atoms[i].f[2];
+        }
+
+        for ( i = spmd_handle->system->N_qm; i < spmd_handle->system->N; ++i )
+        {
+            mm_f_x[i - spmd_handle->system->N_qm] = spmd_handle->system->atoms[i].f[0];
+            mm_f_y[i - spmd_handle->system->N_qm] = spmd_handle->system->atoms[i].f[1];
+            mm_f_z[i - spmd_handle->system->N_qm] = spmd_handle->system->atoms[i].f[2];
+        }
+
+        ret = SPUREMD_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+/* Getter for atom charges in QMMM mode
+ *
+ * handle: pointer to wrapper struct with top-level data structures
+ * qm_q: QM atom charges, in Coulombs (allocated by caller)
+ * mm_q: MM atom charges, in Coulombs (allocated by caller)
+ *
+ * returns: SPUREMD_SUCCESS upon success, SPUREMD_FAILURE otherwise
+ */
+int get_atom_charges_qmmm_( const void * const handle, double * const qm_q,
+        double * const mm_q )
+{
+    int i, ret;
+    spuremd_handle *spmd_handle;
+
+    ret = SPUREMD_FAILURE;
+
+    if ( handle != NULL )
+    {
+        spmd_handle = (spuremd_handle*) handle;
+
+        for ( i = 0; i < spmd_handle->system->N_qm; ++i )
+        {
+            qm_q[i] = spmd_handle->system->atoms[i].q;
+        }
+
+        for ( i = spmd_handle->system->N_qm; i < spmd_handle->system->N; ++i )
+        {
+            mm_q[i - spmd_handle->system->N_qm] = spmd_handle->system->atoms[i].q;
+        }
+
+        ret = SPUREMD_SUCCESS;
+    }
+
+    return ret;
+}
+
+
 /* Getter for atom positions
  *
  * handle: pointer to wrapper struct with top-level data structures
diff --git a/sPuReMD/src/spuremd.h b/sPuReMD/src/spuremd.h
index 3ee91704cbc2d00db5325f2e33d75b806ab46a57..74803b5afa7192f6fdcaddec1b182a4fc6386769 100644
--- a/sPuReMD/src/spuremd.h
+++ b/sPuReMD/src/spuremd.h
@@ -62,6 +62,20 @@ int reset_qmmm_( const void * const, int,
 int reset( const void * const, const char * const,
         const char * const, const char * const );
 
+int get_atom_positions_qmmm_( const void * const, double * const,
+        double * const, double * const, double * const,
+        double * const, double * const );
+
+int get_atom_velocities_qmmm_( const void * const, double * const,
+        double * const, double * const, double * const,
+        double * const, double * const );
+
+int get_atom_forces_qmmm_( const void * const, double * const,
+        double * const, double * const, double * const,
+        double * const, double * const );
+
+int get_atom_charges_qmmm_( const void * const, double * const, double * const );
+
 int get_atom_positions( const void * const, double * const,
         double * const, double * const );
 
diff --git a/tools/driver_qmmm.py b/tools/driver_qmmm.py
index ec315724f1c881ee357e3e7aa9ea74e5b545fe42..dfa80de68b7ec499cd119dc18894bc799da37e90 100644
--- a/tools/driver_qmmm.py
+++ b/tools/driver_qmmm.py
@@ -393,19 +393,21 @@ if __name__ == '__main__':
     set_control_parameter.argtypes = [c_void_p, c_char_p, POINTER(c_char_p)]
     set_control_parameter.restype = c_int
 
-    get_atom_positions = lib.get_atom_positions
-    get_atom_positions.argtypes = [c_void_p, POINTER(c_double),
-            POINTER(c_double), POINTER(c_double)]
-    get_atom_positions.restype = c_int
+    get_atom_positions_qmmm = lib.get_atom_positions_qmmm_
+    get_atom_positions_qmmm.argtypes = [c_void_p,
+            POINTER(c_double), POINTER(c_double), POINTER(c_double),
+            POINTER(c_double), POINTER(c_double), POINTER(c_double)]
+    get_atom_positions_qmmm.restype = c_int
 
-    get_atom_forces = lib.get_atom_forces
-    get_atom_forces.argtypes = [c_void_p, POINTER(c_double),
-            POINTER(c_double), POINTER(c_double)]
-    get_atom_forces.restype = c_int
+    get_atom_forces_qmmm = lib.get_atom_forces_qmmm_
+    get_atom_forces_qmmm.argtypes = [c_void_p,
+            POINTER(c_double), POINTER(c_double), POINTER(c_double),
+            POINTER(c_double), POINTER(c_double), POINTER(c_double)]
+    get_atom_forces_qmmm.restype = c_int
 
-    get_atom_charges = lib.get_atom_charges
-    get_atom_charges.argtypes = [c_void_p, POINTER(c_double)]
-    get_atom_charges.restype = c_int
+    get_atom_charges_qmmm = lib.get_atom_charges_qmmm_
+    get_atom_charges_qmmm.argtypes = [c_void_p, POINTER(c_double), POINTER(c_double)]
+    get_atom_charges_qmmm.restype = c_int
 
     def get_simulation_step_results(num_atoms, atoms, data):
         print("{0:24.15f} {1:24.15f} {2:24.15f}".format(
@@ -450,27 +452,35 @@ if __name__ == '__main__':
     if ret != 0:
         print("[ERROR] simulate returned {0}".format(ret))
 
-    p_x = (c_double * num_atoms)()
-    p_y = (c_double * num_atoms)()
-    p_z = (c_double * num_atoms)()
-    ret = get_atom_positions(handle, p_x, p_y, p_z)
+    qm_p_x = (c_double * num_qm_atoms)()
+    qm_p_y = (c_double * num_qm_atoms)()
+    qm_p_z = (c_double * num_qm_atoms)()
+    mm_p_x = (c_double * num_mm_atoms)()
+    mm_p_y = (c_double * num_mm_atoms)()
+    mm_p_z = (c_double * num_mm_atoms)()
+    ret = get_atom_positions_qmmm(handle, qm_p_x, qm_p_y, qm_p_z,
+            mm_p_x, mm_p_y, mm_p_z)
 
     if ret != 0:
-        print("[ERROR] get_atom_positions returned {0}".format(ret))
+        print("[ERROR] get_atom_positions_qmmm returned {0}".format(ret))
 
-    f_x = (c_double * num_atoms)()
-    f_y = (c_double * num_atoms)()
-    f_z = (c_double * num_atoms)()
-    ret = get_atom_forces(handle, f_x, f_y, f_z)
+    qm_f_x = (c_double * num_qm_atoms)()
+    qm_f_y = (c_double * num_qm_atoms)()
+    qm_f_z = (c_double * num_qm_atoms)()
+    mm_f_x = (c_double * num_mm_atoms)()
+    mm_f_y = (c_double * num_mm_atoms)()
+    mm_f_z = (c_double * num_mm_atoms)()
+    ret = get_atom_forces_qmmm(handle, qm_f_x, qm_f_y, qm_f_z, mm_f_x, mm_f_y, mm_f_z)
 
     if ret != 0:
-        print("[ERROR] get_atom_forces returned {0}".format(ret))
+        print("[ERROR] get_atom_forces_qmmm returned {0}".format(ret))
 
-    q = (c_double * num_atoms)()
-    ret = get_atom_charges(handle, q)
+    qm_q = (c_double * num_qm_atoms)()
+    mm_q = (c_double * num_mm_atoms)()
+    ret = get_atom_charges_qmmm(handle, qm_q, mm_q)
 
     if ret != 0:
-        print("[ERROR] get_atom_charges returned {0}".format(ret))
+        print("[ERROR] get_atom_charges_qmmm returned {0}".format(ret))
 
     # silica
     sim_box = (c_double * 6)(36.477, 50.174, 52.110, 90.0, 90.0, 90.0)
@@ -499,26 +509,34 @@ if __name__ == '__main__':
     if ret != 0:
         print("[ERROR] simulate returned {0}".format(ret))
 
-    p_x = (c_double * num_atoms)()
-    p_y = (c_double * num_atoms)()
-    p_z = (c_double * num_atoms)()
-    ret = get_atom_positions(handle, p_x, p_y, p_z)
+    qm_p_x = (c_double * num_qm_atoms)()
+    qm_p_y = (c_double * num_qm_atoms)()
+    qm_p_z = (c_double * num_qm_atoms)()
+    mm_p_x = (c_double * num_mm_atoms)()
+    mm_p_y = (c_double * num_mm_atoms)()
+    mm_p_z = (c_double * num_mm_atoms)()
+    ret = get_atom_positions_qmmm(handle, qm_p_x, qm_p_y, qm_p_z,
+            mm_p_x, mm_p_y, mm_p_z)
 
     if ret != 0:
-        print("[ERROR] get_atom_positions returned {0}".format(ret))
+        print("[ERROR] get_atom_positions_qmmm returned {0}".format(ret))
 
-    f_x = (c_double * num_atoms)()
-    f_y = (c_double * num_atoms)()
-    f_z = (c_double * num_atoms)()
-    ret = get_atom_forces(handle, f_x, f_y, f_z)
+    qm_f_x = (c_double * num_qm_atoms)()
+    qm_f_y = (c_double * num_qm_atoms)()
+    qm_f_z = (c_double * num_qm_atoms)()
+    mm_f_x = (c_double * num_mm_atoms)()
+    mm_f_y = (c_double * num_mm_atoms)()
+    mm_f_z = (c_double * num_mm_atoms)()
+    ret = get_atom_forces_qmmm(handle, qm_f_x, qm_f_y, qm_f_z, mm_f_x, mm_f_y, mm_f_z)
 
     if ret != 0:
-        print("[ERROR] get_atom_forces returned {0}".format(ret))
+        print("[ERROR] get_atom_forces_qmmm returned {0}".format(ret))
 
-    q = (c_double * num_atoms)()
-    ret = get_atom_charges(handle, q)
+    qm_q = (c_double * num_qm_atoms)()
+    mm_q = (c_double * num_mm_atoms)()
+    ret = get_atom_charges_qmmm(handle, qm_q, mm_q)
 
     if ret != 0:
-        print("[ERROR] get_atom_charges returned {0}".format(ret))
+        print("[ERROR] get_atom_charges_qmmm returned {0}".format(ret))
 
     cleanup(handle)