Flow Sheet#

The connectivity of unit operations is defined in the FlowSheet class. This class provides a directed graph structure that allows for the simple definition of configurations for multiple columns or reactor-separator networks, even when they are cyclic. Furthermore, unit operation models can be used to model tubing and other external volumes.

To ensure that all unit operations in the process have a consistent components, a ComponentSystem is required to instantiate a FlowSheet. For more information about the ComponentSystem, refer to Component System.

Hide code cell content
from CADETProcess.processModel import ComponentSystem
component_system = ComponentSystem(2)
from CADETProcess.processModel import FlowSheet

flow_sheet = FlowSheet(component_system)

Adding Unit Operations#

To add unit operations, use add_unit(). For demonstration purposes, consider an Inlet and an Outlet unit which are connected.

../../_images/io.svg

Simple Flow Sheet.#

Hide code cell content
from CADETProcess.processModel import Inlet, Outlet
inlet = Inlet(component_system, 'inlet')
outlet = Outlet(component_system, 'outlet')
flow_sheet.add_unit(inlet)
flow_sheet.add_unit(outlet)

Note that all unit operations in the FlowSheet must have a unique name since the name is later used for defining connections (and events). The FlowSheet can be indexed with the unit operation name to access that unit.

print(flow_sheet['inlet'])
inlet

To list all units of a FlowSheet, use the units.

print(flow_sheet.units)
[Inlet(n_comp=2, name=inlet), Outlet(n_comp=2, name=outlet)]

Adding Connections#

Every unit operation can have any number of in- and output streams except for Inlets that represent streams entering the system, and Outlets that represent those exiting. To allow connecting the units use add_connection().

flow_sheet.add_connection(inlet, outlet)

If a unit operation has more than one input, all ingoing streams are mixed before entering the unit.

../../_images/mixer.svg

Multiple Input Streams.#

Hide code cell content
inlet_a = Inlet(component_system, 'inlet_a')
inlet_b = Inlet(component_system, 'inlet_b')
outlet = Outlet(component_system, 'outlet')

flow_sheet = FlowSheet(component_system)

flow_sheet.add_unit(inlet_a)
flow_sheet.add_unit(inlet_b)
flow_sheet.add_unit(outlet)
flow_sheet.add_connection(inlet_a, outlet)
flow_sheet.add_connection(inlet_b, outlet)

Note that it is straightforward to also include internal recycles in the FlowSheet, which is important for systems such as SSR (see example in Section Cyclic Stationarity) or SMB (see here).

Output State#

If a unit operation has multiple outputs, the distribution of those streams needs to be specified. Consider the following FlowSheet.

../../_images/splitter.svg

Multiple Output Streams.#

Hide code cell content
inlet = Inlet(component_system, 'inlet')
inlet.flow_rate = 1e-6
outlet_a = Outlet(component_system, 'outlet_a')
outlet_b = Outlet(component_system, 'outlet_b')

flow_sheet = FlowSheet(component_system)

flow_sheet.add_unit(inlet)
flow_sheet.add_unit(outlet_a)
flow_sheet.add_unit(outlet_b)

flow_sheet.add_connection(inlet, outlet_a)
flow_sheet.add_connection(inlet, outlet_b)

To set the output state, use the set_output_state() method. The first argument of the method is the corresponding unit. The second argument is a dictionary of outgoing units with the fraction of the stream that is transported to that destination. Note that the sum of fractions must add up to \(1\). For example, if \(100 \%\) of the flow should be directed to outlet_a, set the following:

flow_sheet.set_output_state('inlet', {'outlet_a': 1})

For a 50/50 split, set the following:

flow_sheet.set_output_state('inlet', {'outlet_a': 0.5, 'outlet_b': 0.5})

The current state of all units in the system is stored in the output_states attribute:

print(flow_sheet.output_states)
{Inlet(n_comp=2, name=inlet): [0.5, 0.5], Outlet(n_comp=2, name=outlet_a): [], Outlet(n_comp=2, name=outlet_b): []}

Note that the output_state can also be changed dynamically during simulation using Events. For more information, refer to Process.

Flow Rates#

In CADET-Process, the Inlet model acts as source unit that “generates” flow. This flow is then transported to subsequent unit operations downstream. Since all fluids in CADET-Process are considered incompressible, all flow that enters a unit must also leave that same unit. The exception is the Cstr which can have a variable volume. If the flow_rate of a Cstr is not None, the outgoing streams can be decoupled from the ingoing streams. This can be useful to e.g. model holdup volumes. However, it is important that the volume of a Cstr never becomes \(0\) or CADET will raise an Exception. If the flow_rate of a Cstr is None, the unit is treated like all other unit operations models and the outgoing flow rate equals the ingoing flow rate. This can be useful when e.g. modelling valves.

Since internal recycles are also possible in CADET-Process, the actual flow rates for every unit operation can be calculated with the get_flow_rates(). Considering the previous example depicted above and assuming a flow rate of \(1~mL\cdot s^{-1}\), the flow rate is evenly split:

print(flow_sheet.get_flow_rates())
{'inlet': {'total_out': [1e-06, 0.0, 0.0, 0.0], 'destinations': {'outlet_a': [5e-07, 0.0, 0.0, 0.0], 'outlet_b': [5e-07, 0.0, 0.0, 0.0]}}, 'outlet_a': {'total_in': [5e-07, 0.0, 0.0, 0.0], 'origins': {'inlet': [5e-07, 0.0, 0.0, 0.0]}}, 'outlet_b': {'total_in': [5e-07, 0.0, 0.0, 0.0], 'origins': {'inlet': [5e-07, 0.0, 0.0, 0.0]}}}

Note that this calculation is performed automatically for all time sections before running a simulation to account for dynamic changes of flow rates and output states.

Marking Unit Operations for Performance Indicators#

To properly determine process performance indicators such as yield or eluent consumption, Inlet unit operations can be registered as feed_inlet or eluent_inlet. This way, the total mass injected and the total eluent volume used are automatically determined from those units. For more information, also refer to Product Fractionation.

../../_images/batch_elution.svg

Batch Elution Flow Sheet#

Hide code cell content
feed = Inlet(component_system, 'feed')
eluent = Inlet(component_system, 'eluent')
outlet = Outlet(component_system, 'outlet')

flow_sheet = FlowSheet(component_system)

flow_sheet.add_unit(feed)
flow_sheet.add_unit(eluent)
flow_sheet.add_unit(outlet)
flow_sheet.add_feed_inlet('feed')
flow_sheet.add_eluent_inlet('eluent')
flow_sheet.add_product_outlet('outlet')

Alternatively, this flag can also be set when adding the unit operations.

Hide code cell content
feed = Inlet(component_system, 'feed')
eluent = Inlet(component_system, 'eluent')
outlet = Outlet(component_system, 'outlet')

flow_sheet = FlowSheet(component_system)
flow_sheet.add_unit(feed, feed_inlet=True)
flow_sheet.add_unit(eluent, eluent_inlet=True)
flow_sheet.add_unit(outlet, product_outlet=True)