def add(self, datapoint):
        """ Adds a datapoint to the file. """
        # check access level
        if self._access_mode == READ_ONLY_ACCESS:
            raise ValueError('Cannot add datapoints with read-only access')

        # read tensor datapoint ind
        tensor_ind = self._num_datapoints // self._datapoints_per_file

        # check datapoint fields
        for field_name in datapoint.keys():
            if field_name not in self.field_names:
                raise ValueError('Field %s not specified in dataset' %(field_name))
        
        # store data in tensor
        cur_num_tensors = self._num_tensors
        new_num_tensors = cur_num_tensors
        for field_name in self.field_names:
            if tensor_ind < cur_num_tensors:
                # load tensor if it was previously allocated
                self._tensors[field_name] = self.tensor(field_name, tensor_ind)
            else:
                # clear tensor if this is a new tensor
                self._tensors[field_name].reset()
                self._tensor_cache_file_num[field_name] = tensor_ind
                new_num_tensors = cur_num_tensors + 1
                self._has_unsaved_data = True
            self._tensors[field_name].add(datapoint[field_name])
            cur_size = self._tensors[field_name].size

        # update num tensors
        if new_num_tensors > cur_num_tensors:
            self._num_tensors = new_num_tensors

        # update file indices
        self._index_to_file_num[self._num_datapoints] = tensor_ind
        self._file_num_to_indices[tensor_ind] = tensor_ind * self._datapoints_per_file + np.arange(cur_size)

        # save if tensors are full
        field_name = self.field_names[0]
        if self._tensors[field_name].is_full:
            # save next tensors to file
            logging.info('Dataset %s: Writing tensor %d to disk' %(self.filename, tensor_ind))
            self.write()

        # increment num datapoints
        self._num_datapoints += 1
Adds a datapoint to the file.