Is it possible to run three pioreactors in parallel/tandem with coordination among them?

Dear developers,
I intend to run three pioreactors in coordination: the first two are each run as a turbidostat of bacterial culture, and their “waste” are independently fed into the the same (third) pioreactor in which bacterial virus, aka phage, feast on the bacteria.

Now I want to control the waste pump of the third pioreactor by a OR-gate: 10 seconds after either of the first two pioreactors dumps “waste” to the third, the pump turns on to take out the added volume.

Is it possible? If yes, can you at least point out the major steps.

Cheers,
Xiao

Hi @xykb86,

This is definitely possible, but requires a custom job or automation to do it.

First, the two chemostat Pioreactors will have the software available - it’s just the chemostat automation, but with your sink tube from the waste pump going to the third Pioreactor.

On that third Pioreactor, you’ll need to create a custom dosing automation. There are probably a few ways to solve this, but here’s a simple solution. At high level, this automation updates it’s internal tracking of the liquid volume by listening to the other Pioreactor’s dosing events. In parallel, every 20 seconds, see if the current liquid volume > desired volume (probably the initial volume), and if so, remove waste to get back to desired volume.

This, on average, removes the added volume after 10 seconds, but it can be any duration between 0 and 20 seconds.

Put the following code in ~/.pioreactor/plugins/pioreactor_as_sink.py:

# -*- coding: utf-8 -*-
from __future__ import annotations

from pioreactor.config import config
from pioreactor.automations.dosing.base import DosingAutomationJobContrib
from pioreactor.structs import DosingEvent
from msgspec.json import decode

__plugin_name__ = "pioreactor_as_sink"
__plugin_author__ = "Cam DP"
__plugin_summary__ = "This Pioreactors will be the sink for other Pioreactor's waste. This automation controls removing that waste."

class PioreactorAsSink(DosingAutomationJobContrib):

    automation_name = "pioreactor_as_sink"

    def __init__(self, **kwargs) -> None:
        super().__init__(**kwargs)

        self.desired_volume = config.getfloat("bioreactor", "initial_volume_ml")

    def _update_dosing_metrics(self, message) -> None:
        dosing_event = decode(message.payload, type=DosingEvent)
        self._update_liquid_volume(dosing_event)

    def start_passive_listeners(self):
        super().start_passive_listeners()
        self.subscribe_and_callback(
            self._update_dosing_metrics,
            "pioreactor/+/+/dosing_events", # listen to any pioreactors dosing events and update.
        )

    def execute(self):
        if self.liquid_volume > self.desired_volume:
            self.execute_io_action(waste_ml=self.liquid_volume - self.desired_volume)

And put the following in ~/.pioreactor/plugins/ui/contrib/automations/dosing/pioreactor_as_sink.yaml

---
display_name: Pioreactor as Sink
automation_name: pioreactor_as_sink
description: This Pioreactors will be the sink for other Pioreactor's waste. This automation controls removing that waste.
fields:
  - key: duration
    default: 0.333
    unit: min
    label: Duration between checks
    disabled: True
    type: numeric

@CamDavidsonPilon Thanks a lot! I will definitely try!

Hi Cam,
I followed the instruction above and did see “Pioreactor as Sink” in the dosing setup page as showed in your picture.
However, when I tried to start an experiment, clicking “start” did not result in any response such as the appearance of Pause and Stop buttoms. Also, when either of the first two reactors dump waste to the third one, the waste pump of the third reactor did not move.

Also, there is not any error ar warning messages in the log.
I tried both creating a single experiment with four reactors assigned to it (three workers running the experiment and one leader just to provide wifi for them), and creating four experiments each assigned with one reactor. Neither worked.

Any suggestion?
Thanks!

However, when I tried to start an experiment, clicking “start” did not result in any response such as the appearance of Pause and Stop buttoms.

Hm, this sounds like there is a problem with the execution of the automation, and hence it won’t remove waste. It could be a software version issue.

Can you ssh into that “sink” worker and try:

pio run dosing_automation --automation-name pioreactor_as_sink --duration 0.33

Does that error out, or run?

Thanks, Cam!
I found the problem: I need to create the file of pioreactor_as_sink.py for each worker individually. I did not do it and so got no response.
Now it is working!
Thanks a lot for your help!

Ah yes that’s correct! I forgot to mention that in my instructions above: the yaml can go on the leader, but the .py needs to go on the Pioreactor that is running the new plugin.

I am sorry to bother you again.
The sink encounters an error: ErrorOccurred: all kwargs should end in _ml. Example: execute_io_action(salty_media_ml=1.0)

I did not see any move of the sink pump.

Sorry, there was a small error in the code. The last line should be:

            self.execute_io_action(waste_ml=self.liquid_volume - self.desired_volume)

(remove_wastewaste_ml)