def aspirate(self, volume=None, location=None, rate=1.0):
        """
        Aspirate a volume of liquid (in microliters/uL) using this pipette
        from the specified location

        Notes
        -----
        If only a volume is passed, the pipette will aspirate
        from it's current position. If only a location is passed,
        `aspirate` will default to it's `max_volume`.

        The location may be a Well, or a specific position in relation to a
        Well, such as `Well.top()`. If a Well is specified without calling a
        a position method (such as .top or .bottom), this method will default
        to the bottom of the well.

        Parameters
        ----------
        volume : int or float
            The number of microliters to aspirate (Default: self.max_volume)

        location : :any:`Placeable` or tuple(:any:`Placeable`, :any:`Vector`)
            The :any:`Placeable` (:any:`Well`) to perform the aspirate.
            Can also be a tuple with first item :any:`Placeable`,
            second item relative :any:`Vector`

        rate : float
            Set plunger speed for this aspirate, where
            speed = rate * aspirate_speed (see :meth:`set_speed`)

        Returns
        -------

        This instance of :class:`Pipette`.

        Examples
        --------
        ..
        >>> from opentrons import instruments, labware, robot # doctest: +SKIP
        >>> robot.reset() # doctest: +SKIP
        >>> plate = labware.load('96-flat', '2') # doctest: +SKIP
        >>> p300 = instruments.P300_Single(mount='right') # doctest: +SKIP
        >>> p300.pick_up_tip() # doctest: +SKIP
        # aspirate 50uL from a Well
        >>> p300.aspirate(50, plate[0]) # doctest: +SKIP
        # aspirate 50uL from the center of a well
        >>> p300.aspirate(50, plate[1].bottom()) # doctest: +SKIP
        >>> # aspirate 20uL in place, twice as fast
        >>> p300.aspirate(20, rate=2.0) # doctest: +SKIP
        >>> # aspirate the pipette's remaining volume (80uL) from a Well
        >>> p300.aspirate(plate[2]) # doctest: +SKIP
        """
        if not self.tip_attached:
            log.warning("Cannot aspirate without a tip attached.")

        # Note: volume positional argument may not be passed. if it isn't then
        # assume the first positional argument is the location
        if not helpers.is_number(volume):
            if volume and not location:
                location = volume
            volume = self.max_volume - self.current_volume

        display_location = location if location else self.previous_placeable

        do_publish(self.broker, commands.aspirate, self.aspirate, 'before',
                   None, None, self, volume, display_location, rate)

        # if volume is specified as 0uL, then do nothing
        if volume != 0:
            if self.current_volume + volume > self.max_volume:
                raise RuntimeWarning(
                    'Pipette with max volume of {0} cannot hold volume {1}'
                    .format(
                        self.max_volume,
                        self.current_volume + volume)
                )

            self._position_for_aspirate(location)

            mm_position = self._aspirate_plunger_position(
                self.current_volume + volume)
            speed = self.speeds['aspirate'] * rate
            self.instrument_actuator.push_speed()
            self.instrument_actuator.set_speed(speed)
            self.instrument_actuator.set_active_current(self._plunger_current)
            self.robot.poses = self.instrument_actuator.move(
                self.robot.poses,
                x=mm_position
            )
            self.instrument_actuator.pop_speed()
            self.current_volume += volume  # update after actual aspirate

        do_publish(self.broker, commands.aspirate, self.aspirate, 'after',
                   self, None, self, volume, display_location, rate)

        return self