docs: def execute(self, time_interval):
          """
          Execute the factor over the given time interval. Note that this is normally done by the workflow,
          but can also be done on the factor directly
  
          :param time_interval: The time interval
          :return: self (for chaining)
          """
          logging.info('{} running from {} to {}'.format(
              self.tool.__class__.__name__, time_interval.start, time_interval.end))
  
          # Execute the tool to produce the output plate values
          output_plate_values = {}
          if self.input_plate:
              for ipv in self.input_plate.values:
                  if ipv in self.source.streams:
                      source = self.source.streams[ipv]
                  else:
                      logging.warn("{} with value {} not valid for source {}".format(
                          self.input_plate, ipv, self.source))
                      continue
  
                  output_plate_values[ipv] = self.tool.execute(
                      source=source, interval=time_interval, input_plate_value=ipv)
          else:
              source = self.source.streams[None] if self.source else None
              if "parent_plate" in self.output_plate:
                  # Get the parent plate values
                  parent_plate = self._plate_manager.plates[self.output_plate["parent_plate"]]
                  for ppv in parent_plate.values:
                      output_plate_values[ppv] = self.tool.execute(
                          source=source, interval=time_interval, input_plate_value=ppv)
              else:
                  output_plate_values[None] = self.tool.execute(
                      source=source, interval=time_interval, input_plate_value=None)
  
          # Ensure that the output plate values exist
          for ipv, opv in output_plate_values.items():
              input_plate_value = ".".join("_".join(i) for i in ipv) if ipv else None
              for pv in opv:
                  if ipv:
                      identifier = input_plate_value + "." + self.output_plate["meta_data_id"] + "_" + pv
                  else:
                      identifier = self.output_plate["meta_data_id"] + "_" + pv
                  if not self._meta_data_manager.contains(identifier):
                      self._meta_data_manager.insert(
                          tag=self.output_plate["meta_data_id"],
                          identifier=identifier,
                          parent=input_plate_value if ipv else "root",
                          data=pv
                      )
  
          if self.output_plate["use_provided_values"]:
              raise NotImplementedError("Currently only support using empty set and complement=True for the new plate")
  
          if self.output_plate["plate_id"] not in self._plate_manager.plates:
              # Create the output plate
              if self.input_plate:
                  parent_plate = self.input_plate.plate_id
              else:
                  if "parent_plate" in self.output_plate:
                      parent_plate = self.output_plate['parent_plate']
                  else:
                      parent_plate = None
              self._plate_manager.create_plate(
                  plate_id=self.output_plate["plate_id"],
                  description=self.output_plate["description"],
                  meta_data_id=self.output_plate["meta_data_id"],
                  values=[],
                  complement=True,
                  parent_plate=parent_plate
              )
  
              logging.info("Plate with ID {} created".format(self.output_plate["plate_id"]))
  
          return self