@@ -83,10 +83,10 @@ def update(
8383
8484
8585class LiquidVolumeCalculator :
86- max_volume = config .getfloat ("bioreactor" , "max_volume_ml" )
87-
8886 @classmethod
89- def update (cls , new_dosing_event : structs .DosingEvent , current_liquid_volume : float ) -> float :
87+ def update (
88+ cls , new_dosing_event : structs .DosingEvent , current_liquid_volume : float , max_volume : float
89+ ) -> float :
9090 assert current_liquid_volume >= 0
9191 volume , event = float (new_dosing_event .volume_change ), new_dosing_event .event
9292 if event == "add_media" :
@@ -97,14 +97,14 @@ def update(cls, new_dosing_event: structs.DosingEvent, current_liquid_volume: fl
9797 if new_dosing_event .source_of_event == "manually" :
9898 # we assume the user has extracted what they want, regardless of level or tube height.
9999 return max (current_liquid_volume - volume , 0.0 )
100- elif current_liquid_volume <= cls . max_volume :
100+ elif current_liquid_volume <= max_volume :
101101 # if the current volume is less than the outflow tube, no liquid is removed
102102 return current_liquid_volume
103103 else :
104104 # since we do some additional "removing" after adding, we don't want to
105105 # count that as being removed (total volume is limited by position of outflow tube).
106106 # hence we keep an lowerbound here.
107- return max (current_liquid_volume - volume , cls . max_volume )
107+ return max (current_liquid_volume - volume , max_volume )
108108 else :
109109 raise ValueError ("Unknown event type" )
110110
@@ -235,7 +235,8 @@ def __init__(
235235 initial_alt_media_fraction : float = config .getfloat (
236236 "bioreactor" , "initial_alt_media_fraction" , fallback = 0.0
237237 ),
238- initial_liquid_volume : float = config .getfloat ("bioreactor" , "initial_volume_ml" , fallback = 14 ),
238+ initial_liquid_volume_ml : float = config .getfloat ("bioreactor" , "initial_volume_ml" , fallback = 14 ),
239+ max_volume_ml : float = config .getfloat ("bioreactor" , "max_volume_ml" , fallback = 14 ),
239240 ** kwargs ,
240241 ) -> None :
241242 super (DosingAutomationJob , self ).__init__ (unit , experiment )
@@ -250,14 +251,15 @@ def __init__(
250251 )
251252
252253 self .skip_first_run = skip_first_run
254+ self .max_volume_ml = max_volume_ml
253255
254256 self .latest_normalized_od_at = current_utc_datetime ()
255257 self .latest_growth_rate_at = current_utc_datetime ()
256258 self .latest_od_at = current_utc_datetime ()
257259
258260 self ._init_alt_media_fraction (float (initial_alt_media_fraction ))
259261 self ._init_volume_throughput ()
260- self ._init_liquid_volume (float (initial_liquid_volume ))
262+ self ._init_liquid_volume (float (initial_liquid_volume_ml ))
261263
262264 self .set_duration (duration )
263265
@@ -266,6 +268,12 @@ def __init__(
266268 "It's recommended to have stirring on to improve mixing during dosing events."
267269 )
268270
271+ if self .max_volume_ml > self .MAX_VIAL_VOLUME_TO_STOP :
272+ # possibly the user messed up thier configuration. We warn them.
273+ self .logger .warning (
274+ "The parameter max_volume_ml should be less than max_volume_to_stop (otherwise your pumping will stop too soon)."
275+ )
276+
269277 def set_duration (self , duration : Optional [float ]) -> None :
270278 if duration :
271279 self .duration = float (duration )
@@ -355,9 +363,7 @@ def block_until_not_sleeping(self) -> bool:
355363 def execute_io_action (
356364 self ,
357365 waste_ml : float = 0.0 ,
358- media_ml : float = 0.0 ,
359- alt_media_ml : float = 0.0 ,
360- ** other_pumps_ml : float ,
366+ ** all_pumps_ml : float ,
361367 ) -> SummableDict :
362368 """
363369 This function recursively reduces the amount to add so that we don't end up adding 5ml,
@@ -366,7 +372,7 @@ def execute_io_action(
366372 will slow dosing down.
367373
368374
369- Users can call additional pumps (other than media and alt_media) by providing them as kwargs. Ex:
375+ Users can call additional pumps by providing them as kwargs. Ex:
370376
371377 > dc.execute_io_action(waste_ml=2, media_ml=1, salt_media_ml=0.5, media_from_sigma_ml=0.5)
372378
@@ -386,21 +392,19 @@ def execute_io_action(
386392 sub-call. This keeps the ratio of alt_media to media the same in the vial.
387393
388394 A problem is if the there is skew in the different mLs, then it's possible that one or more pumps
389- most dose a very small amount, where our pumps have poor accuracy.
395+ must dose a very small amount, where our pumps have poor accuracy.
390396
391397
392398 Returns
393399 ---------
394400 A dict of volumes that were moved, in mL. This may be different than the request mLs, if a error in a pump occurred.
395401
396402 """
397- if not all (other_pump_ml .endswith ("_ml" ) for other_pump_ml in other_pumps_ml .keys ()):
403+ if not all (other_pump_ml .endswith ("_ml" ) for other_pump_ml in all_pumps_ml .keys ()):
398404 raise ValueError (
399405 "all kwargs should end in `_ml`. Example: `execute_io_action(salty_media_ml=1.0)`"
400406 )
401407
402- all_pumps_ml = {** {"media_ml" : media_ml , "alt_media_ml" : alt_media_ml }, ** other_pumps_ml }
403-
404408 sum_of_volumes = sum (ml for ml in all_pumps_ml .values ())
405409 if not (waste_ml >= sum_of_volumes - 1e-9 ):
406410 # why close? account for floating point imprecision, ex: .6299999999999999 != 0.63
@@ -422,13 +426,14 @@ def execute_io_action(
422426 )
423427
424428 else :
425- # iterate through pumps, and dose required amount. First media, then alt_media, then any others , then waste.
429+ # iterate through pumps, and dose required amount. First *_media , then waste.
426430 for pump , volume_ml in all_pumps_ml .items ():
427431 if (self .liquid_volume + volume_ml ) >= self .MAX_VIAL_VOLUME_TO_STOP :
428432 self .logger .error (
429- f"Stopping all pumping since { self .liquid_volume } + { volume_ml } mL is beyond safety threshold { self .MAX_VIAL_VOLUME_TO_STOP } mL."
433+ f"Pausing all pumping since { self .liquid_volume } + { volume_ml } mL is beyond safety threshold { self .MAX_VIAL_VOLUME_TO_STOP } mL."
430434 )
431435 self .set_state (self .SLEEPING )
436+ return volumes_moved
432437
433438 if (volume_ml > 0 ) and (self .state in (self .READY ,)) and self .block_until_not_sleeping ():
434439 pump_function = getattr (self , f"add_{ pump .removesuffix ('_ml' )} _to_bioreactor" )
@@ -590,7 +595,9 @@ def _update_alt_media_fraction(self, dosing_event: structs.DosingEvent) -> None:
590595 cache [self .experiment ] = self .alt_media_fraction
591596
592597 def _update_liquid_volume (self , dosing_event : structs .DosingEvent ) -> None :
593- self .liquid_volume = LiquidVolumeCalculator .update (dosing_event , self .liquid_volume )
598+ self .liquid_volume = LiquidVolumeCalculator .update (
599+ dosing_event , self .liquid_volume , self .max_volume_ml
600+ )
594601
595602 # add to cache
596603 with local_persistent_storage ("liquid_volume" ) as cache :
@@ -633,39 +640,32 @@ def _init_alt_media_fraction(self, initial_alt_media_fraction: float) -> None:
633640
634641 return
635642
636- def _init_liquid_volume (self , initial_liquid_volume : float ) -> None :
637- assert initial_liquid_volume >= 0
643+ def _init_liquid_volume (self , initial_liquid_volume_ml : float ) -> None :
644+ assert initial_liquid_volume_ml >= 0
638645
639646 self .add_to_published_settings (
640647 "liquid_volume" ,
641648 {
642649 "datatype" : "float" ,
643650 "settable" : False , # modify using dosing_events, ex: pio run add_media --ml 1 --manually
644651 "unit" : "mL" ,
652+ "persist" : True , # keep around so the UI can see it
645653 },
646654 )
647655
648656 with local_persistent_storage ("liquid_volume" ) as cache :
649- self .liquid_volume = cache .get (self .experiment , initial_liquid_volume )
657+ self .liquid_volume = cache .get (self .experiment , initial_liquid_volume_ml )
650658
651659 return
652660
653661 def _init_volume_throughput (self ) -> None :
654662 self .add_to_published_settings (
655663 "alt_media_throughput" ,
656- {
657- "datatype" : "float" ,
658- "settable" : False ,
659- "unit" : "mL" ,
660- },
664+ {"datatype" : "float" , "settable" : False , "unit" : "mL" , "persist" : True },
661665 )
662666 self .add_to_published_settings (
663667 "media_throughput" ,
664- {
665- "datatype" : "float" ,
666- "settable" : False ,
667- "unit" : "mL" ,
668- },
668+ {"datatype" : "float" , "settable" : False , "unit" : "mL" , "persist" : True },
669669 )
670670
671671 with local_persistent_storage ("alt_media_throughput" ) as cache :
0 commit comments