99General functional algorithms for batch design.
1010
1111"""
12+ from math import ceil
13+ from flexsolve import wegstein
1214
1315__all__ = ('size_batch' ,)
1416
15- def size_batch (F_vol , tau_reaction , tau_cleaning , N_reactors , V_wf , loading_time = None ) -> dict :
17+ def size_batch (F_vol , tau_reaction , tau_cleaning , V_wf ,
18+ V_max = None , N_reactors = None , loading_time = None ) -> dict :
1619 r"""
1720 Solve for batch reactor volume, cycle time, and loading time.
1821
@@ -24,10 +27,12 @@ def size_batch(F_vol, tau_reaction, tau_cleaning, N_reactors, V_wf, loading_time
2427 Reaction time.
2528 tau_cleaning : float
2629 Cleaning in place time.
27- N_reactors : int
28- Number of reactors.
2930 V_wf : float
3031 Fraction of working volume.
32+ V_max : int
33+ Maximum volume of a reactor.
34+ N_reactors : int
35+ Number of reactors.
3136 loading_time :
3237 Loading time of batch reactor. If not given, it will assume each vessel is constantly
3338 being filled.
@@ -44,24 +49,34 @@ def size_batch(F_vol, tau_reaction, tau_cleaning, N_reactors, V_wf, loading_time
4449 By assuming no downtime, the total volume of all reactors is:
4550
4651 .. math::
52+
4753 V_T = F_{vol}(\tau_{reaction} + \tau_{cleaning} + \tau_{loading})
4854
49- where :math:`V_T` is the total volume of all reactors , :math:`F_{vol}` is the
55+ where :math:`V_T` is the total volume required , :math:`F_{vol}` is the
5056 volumetric flow rate of the feed, :math:`\tau_{reaction}` is the
5157 reaction time, :math:`\tau_{cleaning}` is the cleaning and unloading time,
5258 and :math:`\tau_{loading}` is the time required to load a vessel. This
5359 equation makes the conservative assumption that no reaction takes place
5460 when the tank is being filled.
5561
62+ The number of reactors is:
63+
64+ .. math::
65+
66+ N_{reactors} = ceil(\frac{V_T}{V_{max} \cdot V_{wf}})
67+
68+ where :math:`N_{reactors}` is the number of reactor vessels,
69+ :math:`V_{max}` is the maximum reactor volume, and
70+ :math:`V_{wf}` is the fraction of working volume in a reactor.
71+
5672 The working volume of an individual reactor is:
5773
5874 .. math::
5975
6076 V_{i,working} = \frac{V_T}{N_{reactors}}
6177
62- where :math:`N_{reactors}` is the number of reactor vessels.
63-
64- The time required to load a reactor (assuming no downtime) is:
78+ If there is no upstream storage and a vessel is constantly being filled,
79+ the time required to load a reactor is:
6580
6681 .. math::
6782
@@ -72,39 +87,116 @@ def size_batch(F_vol, tau_reaction, tau_cleaning, N_reactors, V_wf, loading_time
7287 .. math::
7388
7489 V_i = \frac{V_{i,working}}{f}
75-
76- where f is the fraction of working volume in a reactor.
7790
7891 Plugging in and solving for the total volume, :math:`V_{T}`, we have:
7992
8093 .. math::
8194
8295 V_T = F_{vol}\frac{\tau_{reaction} + \tau_{cleaning}}{1 - \frac{1}{N_{reactors}}}
8396
84- Using this equation, :math:`V_T` is first calculated, then :math:`V_{i, working}`,
85- :math:`\tau_{loading}`, and :math:`V_i`.
97+ If the number of reactors is specified but not the loading time, :math:`V_T` is first calculated,
98+ then :math:`V_{i, working}`, :math:`\tau_{loading}`, and :math:`V_i`.
99+
100+ If neither the number of reactors nor the loading time are specified, we solve the equations iteratively
101+ until :math:`\tau_{loading}` converges.
102+
103+ If the loading time is given but not the number of reactors,
104+ then the equation for :math:`tau_{loading}` does not apply and
105+ we first compute :math:`N_reactors` given :math:`V_{max}.
86106
87107 Units of measure may vary so long as they are consistent. The loading time
88108 can be considered the cycle time in this scenario.
89109
110+ Examples
111+ --------
112+ Size batch given a maximum reactor volume of 1,000 m3 and zero loading time:
113+
114+ >>> from biosteam.units.design_tools import size_batch
115+ >>> F_vol = 1e3; tau_reaction = 30; tau_cleaning = 3; V_wf = 0.95
116+ >>> size_batch(
117+ ... F_vol, tau_reaction, tau_cleaning, V_wf,
118+ ... V_max=1e3, loading_time=0
119+ ... )
120+ {'Reactor volume': 992.48,
121+ 'Batch time': 33,
122+ 'Loading time': 0,
123+ 'Number of reactors': 35}
124+
125+ Size batch given 35 reactors and zero loading time:
126+
127+ >>> size_batch(
128+ ... F_vol, tau_reaction, tau_cleaning, V_wf,
129+ ... N_reactors=35, loading_time=0
130+ ... )
131+ {'Reactor volume': 992.48,
132+ 'Batch time': 33,
133+ 'Loading time': 0,
134+ 'Number of reactors': 35}
135+
136+ Size batch given a maximum reactor volume of 1,000 m3 and assume
137+ the constant loading:
138+
139+ >>> size_batch(
140+ ... F_vol, tau_reaction, tau_cleaning, V_wf,
141+ ... V_max=1000,
142+ ... )
143+ {'Reactor volume': 992.4812030075188,
144+ 'Batch time': 33.94285714285714,
145+ 'Loading time': 0.9428571428571428,
146+ 'Number of reactors': 36}
147+
148+ Size batch given 36 reactors and assume
149+ the constant loading:
150+
151+ >>> size_batch(
152+ ... F_vol, tau_reaction, tau_cleaning, V_wf,
153+ ... N_reactors=36
154+ ... )
155+ {'Reactor volume': 992.4812030075188,
156+ 'Batch time': 33.94285714285714,
157+ 'Loading time': 0.9428571428571428,
158+ 'Number of reactors': 36}
159+
160+
90161 """
162+ if sum ([i is None for i in [N_reactors , V_max ]]) != 1 :
163+ raise ValueError ('must pass either `N_reactors` or `V_max`' )
91164 if loading_time is None :
92- # Total volume of all reactors, assuming no downtime
93- V_T = F_vol * (tau_reaction + tau_cleaning ) / (1 - 1 / N_reactors )
94-
95- # Volume of an individual reactor
96- V_i = V_T / N_reactors
97-
98- # Time required to load a reactor
99- tau_loading = V_i / F_vol
165+ if N_reactors is None :
166+ # Solve iteratively
167+ def f (tau_loading ):
168+ N_reactors = ceil (F_vol * (tau_reaction + tau_cleaning + tau_loading ) / (V_max * V_wf ))
169+ if N_reactors == 1 : N_reactors = 2 # Minimum
170+ V_T = F_vol * (tau_reaction + tau_cleaning ) / (1 - 1 / N_reactors )
171+ V_i = V_T / N_reactors
172+ tau_loading = V_i / F_vol
173+ return tau_loading
174+
175+ tau_loading = wegstein (f , 0 )
176+ N_reactors = ceil (F_vol * (tau_reaction + tau_cleaning + tau_loading ) / (V_max * V_wf ))
177+ V_T = F_vol * (tau_reaction + tau_cleaning + tau_loading )
178+ V_i = V_T / N_reactors
179+ else :
180+ # Total volume of all reactors, assuming no downtime
181+ V_T = F_vol * (tau_reaction + tau_cleaning ) / (1 - 1 / N_reactors )
182+
183+ # Volume of an individual reactor
184+ V_i = V_T / N_reactors
185+
186+ # Time required to load a reactor
187+ tau_loading = V_i / F_vol
100188 else :
101189 tau_loading = loading_time
102190
191+ if N_reactors is None :
192+ # Number of reactors
193+ N_reactors = ceil (F_vol * (tau_reaction + tau_cleaning + tau_loading ) / (V_max * V_wf ))
194+
103195 # Total volume of all reactors
104196 V_T = F_vol * (tau_reaction + tau_cleaning + tau_loading )
105197
106198 # Volume of an individual reactor
107- V_i = V_T / N_reactors
199+ V_i = V_T / N_reactors
108200
109201 # Total batch time
110202 tau_batch = tau_reaction + tau_cleaning + tau_loading
@@ -114,6 +206,7 @@ def size_batch(F_vol, tau_reaction, tau_cleaning, N_reactors, V_wf, loading_time
114206
115207 return {'Reactor volume' : V_i ,
116208 'Batch time' : tau_batch ,
117- 'Loading time' : tau_loading }
209+ 'Loading time' : tau_loading ,
210+ 'Number of reactors' : N_reactors }
118211
119212
0 commit comments