Scheduled OD Sampling of External Bio Reactor Culture

I’m trying to create a script/plugin to pull a sample of culture from a external source, dilute it, get an OD measurement then empty and rinse the pioreactor vial (I’m a little surprised that I haven’t seen code for a similar sampling OD procedure on the forum or as a default dosing automations, maybe I missed something).

I’m new to python and have tried using examples from the forum, other python resources and AI to help me get this code working. I’ve attached a simple run file for testing (“run”) along with my current script (“Chemostat In OD Out”)

Chemostat In OD Out.py (4.1 KB)
AIrun Rev.1.py (364 Bytes)

(NOTE: My errors have often related to the DosingAutomationJobContrib superclass, “not removing enough waste” error incorrectly being raised, and errors involving Worker Units (when code is intended for a single Leader). The nature of these errors makes me suspect I have something fairly basic wrong which is causing issues further along.

Any advice or help would be greatly appreciated!

Hi @Karm.RFA,

Welcome to the forums!

Thanks for sharing your Python code. I can certainly help, but I’d like to ask you some questions first:

  1. Is your protocol suppose to run in loops (ex: every X minutes)?
  2. If it is looped, do you expect to be changing parameters mid-way through? Ex: do you expect to change how much media is added, or the length of tubing, during an experiment?
  3. do you need stirring to start and stop? Any problem with keeping stirring constantly running?

Thanks for the quick response.

I would like to have it loop every X minutes either infinitely, or have it run until stopped by schedule or some user override.

Also to have the ability it to attenuate to different files, a situation where the pioreactor is sampling from multiple external sources, though the pioreactor would simply be sampling through its media pump. Length of tubing could be something that changes but at this point unlikely we need to change that input without stopping the pioreactor anyways. (Things I didn’t want to try and code until I got this part running).

I guess code wise there would be cause to create the script so runs this OD measuring cycle for various sources (each source being an instant of a class that would have tube lengths, sample frequency, print file and maybe one or 2 more variables). Though well beyond my coding abilities currently

I had the stirring stopped for the purpose of the OD measuring, but I’m guessing from your question that’s not necessary

So this is a very different solution than the one you are attempting, but I think this is how I want users to solve something like this.

Instead of writing a custom dosing-automation, which as you can tell is not easy, I propose writing an experiment profile. A profile is like a script, and in that script, you can run jobs on a scheduale, and even loop actions. You can even have some basic input variables.

But first, we are going to create a new job that can be called from the experiment profile. This job simply wraps some pump logic so it’s easy to run a command like:

pio run pumps --media 10 --waste 12

which will, as you might guess, run media for 10ml and then run waste for 12ml.

However, in a profile, we specify the job-name (pumps), and the options. Consider the following (simplified) profile:

common:
   jobs:
      pumps: 
         actions:
             - type: start
               hours_elapsed: 0
               options:
                   media: 10
                   waste: 12

This does the same thing as the pio command above. And we can extend the profile add more actions, logging, etc.


Let’s first add this new job, pumps. (FYI, I like this pumps job so much I’ll be adding it to the core software in the next release).

  1. SSH into your Pioreactor(s)
  2. run nano .pioreactor/plugins/pumps.py
  3. Copy and paste the following code: pumps.py · GitHub
  4. Save and exit with ctrl-x

Now all your pioreactors can run a command like:

pio run pumps --media 10 --waste 12

pretty cool!


Next, we’ll add a experiment profile that mimics your goal.

experiment_profile_name: chemostat in OD out 2

metadata:
  author: Cam DP
  description: "
   1. purges media line from photobioreactor (PBR)to pioreactor vial,
  2. rinses the vial with alternative media (filtered water),
  3. empties vial,
  4. fills vial with media (algae in nutrient media) and alt media for a dilution of 1 to 10,
  5. takes the OD measurement,
  6. empty vial,
  7. rinse vial with alt media,
  8. empty vial
  9. purge waste line with alt media
"

inputs:
  media_length: 24
  waste_length: 24
  
common:
  jobs:  
    stirring:
      actions:
        - type: start
          hours_elapsed: 0
    dosing_automation:
      actions:
        - type: start
          hours_elapsed: 0
          options:
             automation_name: silent
    pumps:
      actions:
        - type: repeat
          repeat_every_hours: 1
          hours_elapsed: 0
          actions:
            - type: log
              hours_elapsed: 0
              options:
                message: Purging tubing
            - type: start
              hours_elapsed: 0
              options: 
                media: ${{media_length/12}}
                waste: ${{media_length/12 + 1}}

            - type: log
              hours_elapsed: 0.01666667
              options:
                message: Rinsing vial
            - type: start 
              hours_elapsed: 0.01666667
              options: 
                alt_media: 18

            - type: log
              hours_elapsed: 0.0333332
              options:
                message: Emptying vial
            - type: start 
              hours_elapsed: 0.0333332
              options: 
                waste: 19
                
                
            - type: log
              hours_elapsed: 0.0499998
              options:
                message: Adding fresh media
            - type: start 
              hours_elapsed: 0.0499998
              options: 
                media: 1.5
                alt_media: 15
            
            - type: log
              hours_elapsed: 0.05
              options:
                message: Mixing solution

            - type: log
              hours_elapsed: 0.0666664
              options:
                message: Emptying and rinsing
            - type: start 
              hours_elapsed: 0.0666664
              options: 
                waste: 19
                alt_media: 18

            - type: log
              hours_elapsed: 0.083333
              options:
                message: Emptying and rinsing
            - type: start 
              hours_elapsed: 0.083333
              options: 
                waste: 19
                alt_media: ${{ waste_length/12 }}

    od_reading:
       actions:
          - type: repeat
            hours_elapsed: 0 
            repeat_every_hours: 1
            actions:
              - type: log
                hours_elapsed: 0.0583331
                options:
                   message: Taking OD snapshot
              - type: start
                hours_elapsed: 0.0583331
              - type: stop
                hours_elapsed: 0.0593331

In your UI, visit the Profiles page, and Create New Profile, and paste this code above. On the right hand side is a high-level view of the profile you can review.

Some notes:

  • I’ve put in some input variables, media_length and waste_length - you can change these as you wish.
  • Currently, it runs once an hour, starting when you start the Profile. You can change this in the repeat block.
  • I also couldn’t recreate the max logic in your Python (there’s no max function in profiles).

The profile also starts stirring, and the silent dosing_automation. This is because when dosing_automation is one, you can get better metrics and visibility into any pump behaviour.


One last thing! I am very concerned with adding 19ml to the vial. Anything above 18ml makes me nervous of an overflow. If a pump hiccups, or slows for some reason, it can cause a disaster. For this application, I would recommend something lower.

Thank you very much for that thorough response and instructions. (18mL upper limit noted)

I followed your instructions and setup the profile. The unit seems to go through the order of operations fine however something strange is happening with the volume calculation for the pumps.

I get an error message when the profile is adding the alt media for rinse. A warning in the UI says the volume has been calculated to 32mL (something similar happens when filling the vial for the OD measurement). I don’t understand why/how the program is “calculating” a volume to pump when already given a volume (18mL). I’m not sure if the profile’s equations involving media_length and waste_length have something to do with this, but that doesn’t seem to explain the issue. I tried running the profile with the preset media and waste length set to 2, same error. Any advice?

I think this has to do with the initial vial volume. There’s a config setting, initial_volume_ml under section [bioreactor] that should probably be set to 0 (default is 14). I’m guessing your vial is empty to start? Try that. Note that you will need to start a new experiment to “reset” the vial volume to 0.