1- from pathsim .blocks import Block , ODE , Wrapper
1+ from pathsim .blocks import ODE , Wrapper
2+ from pathsim_chem import Splitter
23import pathsim .blocks
34import pathsim .events
4- from pathsim import Subsystem , Interface , Connection
55import numpy as np
66
77
@@ -41,33 +41,14 @@ def update(self, t):
4141 self .outputs .update_from_array (outputs )
4242
4343
44- class Splitter (Block ):
45- """Splitter block that splits the input signal into multiple outputs based on specified fractions."""
46-
47- def __init__ (self , n = 2 , fractions = None ):
48- super ().__init__ ()
49- self .n = n # number of splits
50- self .fractions = np .ones (n ) / n if fractions is None else np .array (fractions )
51- assert len (self .fractions ) == n , "Fractions must match number of outputs"
52- assert np .isclose (np .sum (self .fractions ), 1 ), (
53- f"Fractions must sum to 1, not { np .sum (self .fractions )} "
54- )
55-
56- def update (self , t ):
57- # get the input from port '0'
58- u = self .inputs [0 ]
59- # mult by fractions and update outputs
60- self .outputs .update_from_array (self .fractions * u )
61-
62-
6344class Splitter2 (Splitter ):
6445 _port_map_out = {"source1" : 0 , "source2" : 1 }
6546
6647 def __init__ (self , f1 = 0.5 , f2 = 0.5 ):
6748 """
6849 Splitter with two outputs, fractions are f1 and f2.
6950 """
70- super ().__init__ (n = 2 , fractions = [f1 , f2 ])
51+ super ().__init__ (fractions = [f1 , f2 ])
7152
7253
7354class Splitter3 (Splitter ):
@@ -77,7 +58,7 @@ def __init__(self, f1=1 / 3, f2=1 / 3, f3=1 / 3):
7758 """
7859 Splitter with three outputs, fractions are f1, f2 and f3.
7960 """
80- super ().__init__ (n = 3 , fractions = [f1 , f2 , f3 ])
61+ super ().__init__ (fractions = [f1 , f2 , f3 ])
8162
8263
8364class Integrator (pathsim .blocks .Integrator ):
@@ -154,147 +135,6 @@ def func_act(_):
154135 return [event ]
155136
156137
157- # BUBBLER SYSTEM
158-
159-
160- class Bubbler (Subsystem ):
161- """
162- Subsystem representing a tritium bubbling system with 4 vials.
163-
164- Args:
165- conversion_efficiency: Conversion efficiency from insoluble to soluble (between 0 and 1).
166- vial_efficiency: collection efficiency of each vial (between 0 and 1).
167- replacement_times: List of times at which each vial is replaced. If None, no replacement
168- events are created. If a single value is provided, it is used for all vials.
169- If a single list of floats is provided, it will be used for all vials.
170- If a list of lists is provided, each sublist corresponds to the replacement times for each vial.
171- """
172-
173- vial_efficiency : float
174- conversion_efficiency : float
175- n_soluble_vials : float
176- n_insoluble_vials : float
177-
178- _port_map_out = {
179- "vial1" : 0 ,
180- "vial2" : 1 ,
181- "vial3" : 2 ,
182- "vial4" : 3 ,
183- "sample_out" : 4 ,
184- }
185- _port_map_in = {
186- "sample_in_soluble" : 0 ,
187- "sample_in_insoluble" : 1 ,
188- }
189-
190- def __init__ (
191- self ,
192- conversion_efficiency = 0.9 ,
193- vial_efficiency = 0.9 ,
194- replacement_times = None ,
195- ):
196- self .reset_times = replacement_times
197- self .n_soluble_vials = 2
198- self .n_insoluble_vials = 2
199- self .vial_efficiency = vial_efficiency
200- col_eff1 = Splitter (n = 2 , fractions = [vial_efficiency , 1 - vial_efficiency ])
201- vial_1 = pathsim .blocks .Integrator ()
202- col_eff2 = Splitter (n = 2 , fractions = [vial_efficiency , 1 - vial_efficiency ])
203- vial_2 = pathsim .blocks .Integrator ()
204-
205- conversion_eff = Splitter (
206- n = 2 , fractions = [conversion_efficiency , 1 - conversion_efficiency ]
207- )
208-
209- col_eff3 = Splitter (n = 2 , fractions = [vial_efficiency , 1 - vial_efficiency ])
210- vial_3 = pathsim .blocks .Integrator ()
211- col_eff4 = Splitter (n = 2 , fractions = [vial_efficiency , 1 - vial_efficiency ])
212- vial_4 = pathsim .blocks .Integrator ()
213-
214- add1 = pathsim .blocks .Adder ()
215- add2 = pathsim .blocks .Adder ()
216- interface = Interface ()
217-
218- self .vials = [vial_1 , vial_2 , vial_3 , vial_4 ]
219-
220- blocks = [
221- vial_1 ,
222- col_eff1 ,
223- vial_2 ,
224- col_eff2 ,
225- conversion_eff ,
226- vial_3 ,
227- col_eff3 ,
228- vial_4 ,
229- col_eff4 ,
230- add1 ,
231- add2 ,
232- interface ,
233- ]
234- connections = [
235- Connection (interface [0 ], col_eff1 ),
236- Connection (col_eff1 [0 ], vial_1 ),
237- Connection (col_eff1 [1 ], col_eff2 ),
238- Connection (col_eff2 [0 ], vial_2 ),
239- Connection (col_eff2 [1 ], conversion_eff ),
240- Connection (conversion_eff [0 ], add1 [0 ]),
241- Connection (conversion_eff [1 ], add2 [0 ]),
242- Connection (interface [1 ], add1 [1 ]),
243- Connection (add1 , col_eff3 ),
244- Connection (col_eff3 [0 ], vial_3 ),
245- Connection (col_eff3 [1 ], col_eff4 ),
246- Connection (col_eff4 [0 ], vial_4 ),
247- Connection (col_eff4 [1 ], add2 [1 ]),
248- Connection (vial_1 , interface [0 ]),
249- Connection (vial_2 , interface [1 ]),
250- Connection (vial_3 , interface [2 ]),
251- Connection (vial_4 , interface [3 ]),
252- Connection (add2 , interface [4 ]),
253- ]
254- super ().__init__ (blocks , connections )
255-
256- def _create_reset_events_one_vial (
257- self , block , reset_times
258- ) -> pathsim .events .ScheduleList :
259- def reset_itg (_ ):
260- block .reset ()
261-
262- event = pathsim .events .ScheduleList (times_evt = reset_times , func_act = reset_itg )
263- return event
264-
265- def create_reset_events (self ) -> list [pathsim .events .ScheduleList ]:
266- """Create reset events for all vials based on the replacement times.
267-
268- Raises:
269- ValueError: If reset_times is not valid.
270-
271- Returns:
272- list of reset events.
273- """
274- reset_times = self .reset_times
275- events = []
276- # if reset_times is a single list use it for all vials
277- if reset_times is None :
278- return events
279- if isinstance (reset_times , (int , float )):
280- reset_times = [reset_times ]
281- # if it's a flat list use it for all vials
282- elif isinstance (reset_times , list ) and all (
283- isinstance (t , (int , float )) for t in reset_times
284- ):
285- reset_times = [reset_times ] * len (self .vials )
286- elif isinstance (reset_times , np .ndarray ) and reset_times .ndim == 1 :
287- reset_times = [reset_times .tolist ()] * len (self .vials )
288- elif isinstance (reset_times , list ) and len (reset_times ) != len (self .vials ):
289- raise ValueError (
290- "reset_times must be a single value or a list with the same length as the number of vials"
291- )
292- for i , vial in enumerate (self .vials ):
293- events .append (self ._create_reset_events_one_vial (vial , reset_times [i ]))
294-
295- return events
296-
297-
298138# FESTIM wall
299139from pathsim .utils .register import Register
300140
0 commit comments