docs: def add_loom(self, other_file: str, key: str = None, fill_values: Dict[str, np.ndarray] = None, batch_size: int = 1000, convert_attrs: bool = False, include_graphs: bool = False) -> None:
  		"""
  		Add the content of another loom file
  
  		Args:
  			other_file:       filename of the loom file to append
  			key:                    Primary key to use to align rows in the other file with this file
  			fill_values:     default values to use for missing attributes (or None to drop missing attrs, or 'auto' to fill with sensible defaults)
  			batch_size:       the batch size used by batchscan (limits the number of rows/columns read in memory)
  			convert_attrs:   convert file attributes that differ between files into column attributes
  			include_graphs:  if true, include all the column graphs from other_file that are also present in this file
  
  		Returns:
  			Nothing, but adds the loom file. Note that the other loom file must have exactly the same
  			number of rows, and must have exactly the same column attributes.
  			Adds all the contents including layers but ignores layers in `other_file` that are not already present in self
  			Note that graphs are normally not added, unless include_graphs == True, in which case column graphs are added
  		"""
  		if self._file.mode != "r+":
  			raise IOError("Cannot add data when connected in read-only mode")
  		# Connect to the loom files
  		with loompy.connect(other_file) as other:
  			# Verify that the row keys can be aligned
  			ordering = None
  			if key is not None:
  				# This was original Sten's version but it creates a 400M entries array in memory
  				# ordering = np.where(other.row_attrs[key][None, :] == self.row_attrs[key][:, None])[1]
  
  				def ixs_thatsort_a2b(a: np.ndarray, b: np.ndarray, check_content: bool = True) -> np.ndarray:
  					"This is super duper magic sauce to make the order of one list to be like another"
  					if check_content:
  						assert len(np.intersect1d(a, b)) == len(a), "The two arrays are not matching"
  					return np.argsort(a)[np.argsort(np.argsort(b))]
  
  				ordering = ixs_thatsort_a2b(a=other.row_attrs[key], b=self.row_attrs[key])
  				pk1 = sorted(other.row_attrs[key])
  				pk2 = sorted(self.row_attrs[key])
  				for ix, val in enumerate(pk1):
  					if pk2[ix] != val:
  						raise ValueError("Primary keys are not 1-to-1 alignable!")
  			diff_layers = set(self.layers.keys()) - set(other.layers.keys())
  			if len(diff_layers) > 0:
  				raise ValueError("%s is missing a layer, cannot merge with current file. layers missing:%s" % (other_file, diff_layers))
  
  			if convert_attrs:
  				# Prepare to replace any global attribute that differ between looms or is missing in either loom with a column attribute.
  				globalkeys = set(self.attrs)
  				globalkeys.update(other.attrs)
  				for globalkey in globalkeys:
  					if globalkey in self.attrs and globalkey in other.attrs and self.attrs[globalkey] == other.attrs[globalkey]:
  						continue
  					if globalkey not in self.col_attrs:
  						self_value = self.attrs[globalkey] if globalkey in self.attrs else np.zeros(1, dtype=other.attrs[globalkey].dtype)[0]
  						self.col_attrs[globalkey] = np.array([self_value] * self.shape[1])
  					if globalkey not in other.col_attrs:
  						other_value = other.attrs[globalkey] if globalkey in other.attrs else np.zeros(1, dtype=self.attrs[globalkey].dtype)[0]
  						other.col_attrs[globalkey] = np.array([other_value] * other.shape[1])
  					if globalkey in self.attrs:
  						delattr(self.attrs, globalkey)
  
  			for (ix, selection, vals) in other.batch_scan_layers(axis=1, layers=self.layers.keys(), batch_size=batch_size):
  				ca = {key: v[selection] for key, v in other.col_attrs.items()}
  				if ordering is not None:
  					vals = {key: val[ordering, :] for key, val in vals.items()}
  				self.add_columns(vals, ca, fill_values=fill_values)
  			
  			if include_graphs:
  				for gname in self.col_graphs.keys():
  					if gname in other.col_graphs:
  						g1 = self.col_graphs[gname]
  						g2 = other.col_graphs[gname]
  						n = self.shape[1]
  						m = other.shape[1]
  						g3 = scipy.sparse.coo_matrix((np.concatenate([g1.data, g2.data]), (np.concatenate([g1.row, g2.row + n]), np.concatenate([g1.col, g2.col + n]))), shape=(n + m, n + m))
  						self.col_graphs[gname] = g3