Checks whether the source and sink plate are compatible given the tool

        :param tool: The tool
        :param source_plate: The source plate
        :param sink_plate: The sink plate
        :return: Either an error, or None
        :type tool: Tool
        :type source_plate: Plate
        :type sink_plate: Plate
        :rtype: None | str

def check_plate_compatibility(tool, source_plate, sink_plate):
        """
        Checks whether the source and sink plate are compatible given the tool

        :param tool: The tool
        :param source_plate: The source plate
        :param sink_plate: The sink plate
        :return: Either an error, or None
        :type tool: Tool
        :type source_plate: Plate
        :type sink_plate: Plate
        :rtype: None | str
        """
        if sink_plate == source_plate.parent:
            return None

        # could be that they have the same meta data, but the sink plate is a simplification of the source
        # plate (e.g. when using IndexOf tool)
        if sink_plate.meta_data_id == source_plate.meta_data_id:
            if sink_plate.is_sub_plate(source_plate):
                return None
            return "Sink plate {} is not a simplification of source plate {}".format(
                sink_plate.plate_id, source_plate.plate_id)

        # Also check to see if the meta data differs by only one value
        meta_data_diff = set(source_plate.ancestor_meta_data_ids) - set(sink_plate.ancestor_meta_data_ids)
        if len(meta_data_diff) == 1:
            # Is the diff value the same as the aggregation meta id passed to the aggregate tool
            if tool.aggregation_meta_data not in meta_data_diff:
                return "Aggregate tool meta data ({}) " \
                       "does not match the diff between source and sink plates ({})".format(
                        tool.aggregation_meta_data, list(meta_data_diff)[0])
        else:
            return "{} not in source's parent plates".format(sink_plate.plate_id)