diff --git a/src/Configuration.cpp b/src/Configuration.cpp
index b1a89c49049548ef72024c4d4bfd69357acf60ab..04f2a94653b3942ff55be51c85fd6b4c9f9de3ec 100644
--- a/src/Configuration.cpp
+++ b/src/Configuration.cpp
@@ -97,8 +97,24 @@ Configuration::Configuration(const char* config_file, int simNum, bool debug) :
     }
   } // end result: variable "num" is set
 
+	// Count particles associated with rigid bodies
+	num_rb_attached_particles = 0;
+	if (numRigidTypes > 0) {
+	    // grow list of rbs
+	    for (int i = 0; i < numRigidTypes; i++) {
+		RigidBodyType &rbt = rigidBody[i];
+		rbt.attach_particles();
+		num_rb_attached_particles += rbt.num * rbt.num_attached_particles();
+	    }
+	}
+	assert( num_rb_attached_particles == 0 || simNum == 1 ); // replicas not yet implemented
+	// num = num+num_rb_attached_particles;
+
+
 	// Set the number capacity
 	printf("\n%d particles\n", num);
+	printf("%d particles attached to RBs\n", num_rb_attached_particles);
+
 	if (numCap <= 0) numCap = numCapFactor*num; // max number of particles
 	if (numCap <= 0) numCap = 20;
 
@@ -106,18 +122,23 @@ Configuration::Configuration(const char* config_file, int simNum, bool debug) :
 	printf("%d groups\n", numGroupSites);
 
 	// Allocate particle variables.
-	pos = new Vector3[num * simNum];
+	// First num*simNum entries are for point particles, next num_rb_attached_particles*simNum are for RB particles
+
+
+	pos = new Vector3[ (num+num_rb_attached_particles) * simNum];
+
         //Han-Yi Chou
         if (ParticleDynamicType == String("Langevin") || ParticleDynamicType == String("NoseHooverLangevin"))
-            momentum = new Vector3[num * simNum];
+            momentum = new Vector3[(num+num_rb_attached_particles) * simNum];
+
+	type   = new int[(num+num_rb_attached_particles) * simNum];
+	serial = new int[(num+num_rb_attached_particles) * simNum];
+	posLast = new Vector3[(num+num_rb_attached_particles) * simNum];
 
-	type   = new int[num * simNum];
-	serial = new int[num * simNum];
-	posLast = new Vector3[num * simNum];
         //Han-Yi Chou
         if(ParticleDynamicType == String("Langevin") || ParticleDynamicType == String("NoseHooverLangevin"))
-           momLast = new Vector3[num * simNum];
-	name = new String[num * simNum];
+           momLast = new Vector3[(num+num_rb_attached_particles) * simNum];
+	name = new String[(num+num_rb_attached_particles) * simNum];
 	currSerial = 0;
 
 
@@ -164,7 +185,12 @@ Configuration::Configuration(const char* config_file, int simNum, bool debug) :
 				String* tokenList = new String[numTokens];
 				partsFromFile[i].tokenize(tokenList);
 
-				int currType = 0;
+				int currType = find_particle_type(tokenList[2]);
+				if (currType == -1) {
+				    printf("Error: Unable to find particle type %s\n", tokenList[2].val());
+				    exit(1);
+
+				}
 				for (int j = 0; j < numParts; j++)
 					if (tokenList[2] == part[j].name)
 						currType = j;
@@ -216,7 +242,7 @@ Configuration::Configuration(const char* config_file, int simNum, bool debug) :
                                 printf("done!\n");
                         }
                         else
-                            loadedMomentum = Boltzmann(COM_Velocity, num * simNum);
+                            loadedMomentum = Boltzmann(COM_Velocity, (num * simNum));
                     }
                 }
             }
@@ -410,9 +436,10 @@ Configuration::Configuration(const char* config_file, int simNum, bool debug) :
 
 
 	// Create exclusions from the exclude rule, if it was specified in the config file
+    assert(false); // TODO implement RB exclusions
 	if (excludeRule != String("")) {
 		int oldNumExcludes = numExcludes;
-		Exclude* newExcludes = makeExcludes(bonds, bondMap, num, numBonds, excludeRule, numExcludes);
+		Exclude* newExcludes = makeExcludes(bonds, bondMap, num+num_rb_attached_particles, numBonds, excludeRule, numExcludes);
 		if (excludes == NULL) {
 			excludes = new Exclude[numExcludes];
 		} else if (numExcludes >= excludeCapacity) {
@@ -434,7 +461,8 @@ Configuration::Configuration(const char* config_file, int simNum, bool debug) :
 	for (int i = 0; i < numParts; ++i) {
 		numPartsOfType[i] = 0;
 	}
-	for (int i = 0; i < num; ++i) {
+    assert(false); // First assign type[i] for rb particles
+	for (int i = 0; i < num+num_rb_attached_particles; ++i) {
 		++numPartsOfType[type[i]];
 	}
 
@@ -679,7 +707,7 @@ void Configuration::copyToCUDA() {
 	gpuErrchk(cudaMalloc(&sys_d, sizeof(BaseGrid)));
 	gpuErrchk(cudaMemcpyAsync(sys_d, sys, sizeof(BaseGrid), cudaMemcpyHostToDevice));
 	/*gpuErrchk(cudaMalloc(&type_d, sizeof(int) * num * simNum));
-	gpuErrchk(cudaMemcpyAsync(type_d, type, sizeof(int) * num * simNum, cudaMemcpyHostToDevice));
+	gpuErrchk(cudaMemcpyAsync(type_d, type, sizeof(int+num_rb_attached_particles) * num * simNum, cudaMemcpyHostToDevice));
 	
 	if (numBonds > 0) {
 		// bonds_d
@@ -698,7 +726,7 @@ void Configuration::copyToCUDA() {
 				cudaMemcpyHostToDevice));
 		
 		// excludeMap_d
-		gpuErrchk(cudaMalloc(&excludeMap_d, sizeof(int2) * num));
+		gpuErrchk(cudaMalloc(&excludeMap_d, sizeof(int2) * (num));
 		gpuErrchk(cudaMemcpyAsync(excludeMap_d, excludeMap, sizeof(int2) * num,
 				cudaMemcpyHostToDevice));
 	}
@@ -1117,6 +1145,8 @@ int Configuration::readParameters(const char * config_file) {
 		else if (param == String("rotDamping"))
 			rigidBody[currRB].rotDamping = stringToVector3( value );
 
+		else if (param == String("attachedParticles"))
+			rigidBody[currRB].append_attached_particle_file(value);
 		else if (param == String("densityGrid"))
 			rigidBody[currRB].addDensityGrid(value);
 		else if (param == String("potentialGrid"))
@@ -1275,6 +1305,7 @@ void Configuration::readAtoms() {
 		for (int i = 0; i < numParts; i++)
 			if (part[i].num == 0)
 				found = false;
+		assert(false); // TODO probably relax constraint that particle must be found; could just be in RB
 		if (!found) {
 			printf("ERROR: Number of particles not specified in config file.\n");
 			exit(1);
@@ -1454,9 +1485,11 @@ void Configuration::readGroups() {
 		std::vector<int> tmp;
 		for (int i=1; i < numTokens; ++i) {
 		    const int ai = atoi(tokenList[i].val());
-		    if (ai >= num) {
+		    if (ai >= num+num_rb_attached_particles) {
 			printf("Error: Attempted to include invalid particle in group: %s\n", line);
 			exit(-1);
+		    } else if (ai >= num) {
+			printf("WARNING: including RB particles in group with line: %s\n", line);
 		    }
 		    tmp.push_back( ai );
 		}
@@ -1514,7 +1547,8 @@ void Configuration::readBonds() {
 			continue;
 		}
 
-		if (ind1 < 0 || ind1 >= num+numGroupSites || ind2 < 0 || ind2 >= num+numGroupSites) {
+		if (ind1 < 0 || ind1 >= num+num_rb_attached_particles+numGroupSites ||
+		    ind2 < 0 || ind2 >= num+num_rb_attached_particles+numGroupSites) {
 			printf("ERROR: Bond file line '%s' includes invalid index\n", line);
 			exit(1);
 		}
@@ -1562,8 +1596,8 @@ void Configuration::readBonds() {
 	 * bondMap[i].x is the index in the bonds array where the ith particle's bonds begin
 	 * bondMap[i].y is the index in the bonds array where the ith particle's bonds end
 	 */
-	bondMap = new int2[num+numGroupSites];
-	for (int i = 0; i < num+numGroupSites; i++) {	
+	bondMap = new int2[num+num_rb_attached_particles+numGroupSites];
+	for (int i = 0; i < num+num_rb_attached_particles+numGroupSites; i++) {
 		bondMap[i].x = -1;
 		bondMap[i].y = -1;
 	}
@@ -1621,8 +1655,8 @@ void Configuration::readExcludes()
 	}
 }
 void Configuration::addExclusion(int ind1, int ind2) {
-    if (ind1 >= num || ind2 >= num) {
-	printf("WARNING: Attempted to add an exclusion for an out-of-range particle index (%d or %d >= %d).\n", ind1, ind2, num);
+    if (ind1 >= num+num_rb_attached_particles || ind2 >= num+num_rb_attached_particles) {
+	printf("WARNING: Attempted to add an exclusion for an out-of-range particle index (%d or %d >= %d).\n", ind1, ind2, num+num_rb_attached_particles);
 	return;
     }
 		
@@ -1665,8 +1699,8 @@ void Configuration::buildExcludeMap() {
      * excludeMap[i].x is the index in the excludes array where the ith particle's excludes begin
      * excludeMap[i].y is the index in the excludes array where the ith particle's excludes end
      */
-    excludeMap = new int2[num];
-    for (int i = 0; i < num; i++) {	
+    excludeMap = new int2[num+num_rb_attached_particles];
+    for (int i = 0; i < num+num_rb_attached_particles; i++) {
 	excludeMap[i].x = -1;
 	excludeMap[i].y = -1;
     }
@@ -1675,7 +1709,7 @@ void Configuration::buildExcludeMap() {
     for (int i = 0; i < numExcludes; i++) {
 	if (excludes[i].ind1 != currPart) {
 	    currPart = excludes[i].ind1;
-	    assert(currPart < num);
+	    assert(currPart < num+num_rb_attached_particles);
 	    excludeMap[currPart].x = i;
 	    if (lastPart >= 0)
 		excludeMap[lastPart].y = i;
@@ -1724,7 +1758,7 @@ void Configuration::readAngles() {
 		int ind3 = atoi(tokenList[3].val());
 		String file_name = tokenList[4];
 		//printf("file_name %s\n", file_name.val());
-		if (ind1 >= num+numGroupSites or ind2 >= num+numGroupSites or ind3 >= num+numGroupSites)
+		if (ind1 >= num+num_rb_attached_particles+numGroupSites or ind2 >= num+num_rb_attached_particles+numGroupSites or ind3 >= num+num_rb_attached_particles+numGroupSites)
 			continue;
 
 		if (numAngles >= capacity) {
@@ -1785,8 +1819,10 @@ void Configuration::readDihedrals() {
 		int ind4 = atoi(tokenList[4].val());
 		String file_name = tokenList[5];
 		//printf("file_name %s\n", file_name.val());
-		if (ind1 >= num+numGroupSites or ind2 >= num+numGroupSites
-				or ind3 >= num+numGroupSites or ind4 >= num+numGroupSites)
+		if (ind1 >= num+num_rb_attached_particles+numGroupSites or
+		    ind2 >= num+num_rb_attached_particles+numGroupSites or
+		    ind3 >= num+num_rb_attached_particles+numGroupSites or
+		    ind4 >= num+num_rb_attached_particles+numGroupSites)
 			continue;
 
 		if (numDihedrals >= capacity) {
@@ -1845,7 +1881,7 @@ void Configuration::readRestraints() {
 		float y0 = (float) strtod(tokenList[4].val(), NULL);
 		float z0 = (float) strtod(tokenList[5].val(), NULL);
 
-		if (id >= num+numGroupSites) continue;
+		if (id >= num + num_rb_attached_particles + numGroupSites) continue;
 
 		if (numRestraints >= capacity) {
 			Restraint* temp = restraints;
diff --git a/src/Configuration.h b/src/Configuration.h
index fc70e2a1e61eed800c52235e868d8f3afd5bf6cc..8bc023de4be3c92c07bc45d2e36fa63477475f8a 100644
--- a/src/Configuration.h
+++ b/src/Configuration.h
@@ -65,6 +65,7 @@ class Configuration {
 	void readDihedrals();
 	void readRestraints();
 
+
 	bool readTableFile(const String& value, int currTab);
 	bool readBondFile(const String& value, int currBond);
 	bool readAngleFile(const String& value, int currAngle);
@@ -89,6 +90,13 @@ public:
 	Configuration(const char * config_file, int simNum = 0, bool debug=false);
 	~Configuration();
 
+    int find_particle_type(const char* s) const {
+	for (int j = 0; j < numParts; j++)
+	    if (s == part[j].name)
+		return j;
+	return -1;
+    }
+
 	void copyToCUDA();
 
 	// Output variables
@@ -122,7 +130,8 @@ public:
 	Bond* bonds;
 	int numCap; // max number of particles
 	int num; // current number of particles
-	Vector3* pos; //  position of each particle
+    int num_rb_attached_particles;
+        Vector3* pos; //  position of each particle
         Vector3* momentum; //momentum of each brownian particles Han-Yi Chou
         Vector3  COM_Velocity; //center of mass velocity Han-Yi Chou
 	int* type; // type of each particle
diff --git a/src/RigidBodyType.cu b/src/RigidBodyType.cu
index 969a16ad7d6c3183d9a0f69ee0c4bffd142faf0f..8f7739ceecc4a15b50dd0258e0f58d82ea6480d8 100644
--- a/src/RigidBodyType.cu
+++ b/src/RigidBodyType.cu
@@ -81,6 +81,55 @@ void RigidBodyType::setDampingCoeffs(float timestep) { /* MUST ONLY BE CALLED ON
 
 }
 	
+void RigidBodyType::attach_particles() {
+    for (const auto& filename: attached_particle_files) {
+	const size_t line_char = 256;
+	FILE* inp = fopen(filename.val(), "r");
+	char line[line_char];
+
+	// If the particle file cannot be found, exit the program
+	if (inp == NULL) {
+	    printf("ERROR: Could not open `%s'.\n", filename.val());
+	    fclose(inp);
+	    exit(1);
+	}
+
+	// Get and process all lines of input
+	while (fgets(line, line_char, inp) != NULL) {
+		// Lines in the particle file that begin with # are comments
+		if (line[0] == '#') continue;
+
+		String s(line);
+		int numTokens = s.tokenCount();
+
+		// Break the line down into pieces (tokens) so we can process them individually
+		String* tokenList = new String[numTokens];
+		s.tokenize(tokenList);
+
+		// Legitimate GROUP input lines have at least 3 tokens:
+		// Particle_type | x | y | z
+		// A line without exactly six tokens should be discarded.
+		if (numTokens != 4) {
+		    printf("Error: Invalid attached particle file line: %s\n", line);
+		    fclose(inp);
+		    exit(-1);
+		}
+
+		// Make sure the index of this particle is unique.
+		// NOTE: The particle list is sorted by index.
+		int type_idx = conf->find_particle_type( tokenList[0].val() );
+		if (type_idx < 0) {
+		    printf("Error: Unrecognized particle type: %s\n", line);
+		    fclose(inp);
+		    exit(-1);
+		}
+		attached_particle_types.push_back( type_idx );
+		attached_particle_positions.push_back( Vector3(atof(tokenList[1].val()), atof(tokenList[2].val()), atof(tokenList[3].val())) );
+	}
+	fclose(inp);
+    }
+}
+
 void RigidBodyType::addGrid(String s, std::vector<String> &keys, std::vector<String> &files) {
 	// tokenize and return
 	int numTokens = s.tokenCount();
diff --git a/src/RigidBodyType.h b/src/RigidBodyType.h
index 575a31348eb24dd305e913bbf46bf0b095bb8649..7a6edcc1dc6d1294e2a8cb37ce61b4eacd221870 100644
--- a/src/RigidBodyType.h
+++ b/src/RigidBodyType.h
@@ -39,6 +39,10 @@ public:
 	/* RigidBodyType& operator=(const RigidBodyType& src); */
 	void copyGridsToDevice();
 	
+    void append_attached_particle_file(String s) { attached_particle_files.push_back(s); }
+    void attach_particles();
+    size_t num_attached_particles() { return attached_particle_types.size() ;}
+
 	void addPotentialGrid(String s);
 	void addDensityGrid(String s);
 	void addPMF(String s);
@@ -54,6 +58,10 @@ public:
 	String name;
 private:
 	const Configuration* conf;
+	std::vector<String> attached_particle_files;
+    std::vector<int>attached_particle_types;
+    std::vector<Vector3>attached_particle_positions;
+
 public:
 	int num; // number of particles of this type