PhysicalProcess¶
- class pychemengg.massbalances.physicalmassbalance.PhysicalProcess(processname)[source]¶
Bases:
object
Models a physical process such as a mixer, distillation column etc.
The physical process can have streams attached to it, and a mass balance can be performed on these streams.
- Parameters
- processnamestr
The name assigned to a physical process as its identifier.
Examples
First import the module physicalmassbalance
>>> from pychemengg.massbalances import physicalmassbalance as pmb >>> mixer1 = pmb.PhysicalProcess("mixer1") # This will create an instance of 'PhysicalProcess' with a name 'mixer1'. # Notice that here the same name is used for 'variable name' and 'process name'. # It does not have to be the same, however, it is less confusing if same # names are used.
- Attributes
- streamregistrydict {str: pychemengg.physicalmassbalance.PhysicalProcess.Stream}
The streamregistry is a dictionary that stores all streams attached to a physical process. The keys of the dictionary are names of streams and the values are the “Stream” object itself.
Methods
__init__
(processname)attachstreams
(*[, streamnames, flowrates, …])Attach one or more streams to process.
Perform degree of freedom analysis on the physical process.
Compute unknown flowrate “F” or “-F”.
find_unknownfractions
([streamname])Compute unknown fractions ‘x’.
Execute component mass balance on a process.
Solves the physical process to find “F”, “-F”, and “x”
update_streamflowrates
(newflow)Update flowrate of a stream.
update_streamfractions
(new_fractioninfo)Update mole/mass fraction of a stream.
- class Stream(instanceof_physical_process, streamname)[source]¶
Bases:
object
Creates a stream to be attached to a physical process.
- Parameters
- instance of physical processpychemengg.physicalmassbalance.PhysicalProcess
Physical process to which the created stream will be attached.
- streamnamestr
Name of stream to be created.
- Returns
- None
See also
pychemengg.massbalances.physicalmassbalance.PhysicalProcess.attachstreams
pychemengg.massbalances.physicalmassbalance.PhysicalProcess.Stream.setflow
pychemengg.massbalances.physicalmassbalance.PhysicalProcess.Stream.setfractions
pychemengg.massbalances.physicalmassbalance.PhysicalProcess.Stream.setextrainfo
Notes
Creates a stream and stores it in streamregistry (a dictionary) of the physical process.
~.streamregistry[streamname] = Stream object
Examples
To create an instance of 'Stream' use the 'attachstreams' method.
- Attributes
- streamnamestr
Name of stream.
- connectedprocesspychemengg.physicalmassbalance.PhysicalProcess
The instance of physical process to which the stream is attached.
- flowrateint or float or str
Stream flowrate.
- fractionsa list containing [int or float or str, int or float or str, … ]
Component mass/mole fractions of a stream.
- extrainfoa list of [str]
Extra information between component or total flowrates between streams.
- deleteextrainfo()[source]¶
Delete extra information of a stream.
- Parameters
- None
- Returns
- None
See also
Notes
Deletes ‘extrainfo’ attribute of a stream if it exists
Examples
>>> S1.deleteextrainfo()
- setequalto(streamtocopy, flowdirection='+')[source]¶
Copy data of one stream to another stream.
- Parameters
- streamtocopypychemengg.physicalmassbalance.PhysicalProcess.Stream
Stream to be copied.
- destinationpychemengg.physicalmassbalance.PhysicalProcess.Stream
Destination stream. “self” is the destination stream.
- flowdirectionstr
“+” or “-” depending on whether destination stream is entering or exiting the physical process, default = “+”
- Returns
- None
Examples
# Consider two physical processes 'process1' and 'process2'. # Consider a stream S1 from process1 enters process2 as S2. # Then it would be desirable to set S2 equal to S1 so that # data of S1 can be copied into S2. # This can be done as follows. # First import the module **physicalmassbalance** >>> from pychemengg.massbalances import physicalmassbalance as pmb # Next create instance of physical process process1 >>> process1 = pmb.PhysicalProcess("process1") # Attach S1 to process1. >>> process1.S1 = process1.attachstreams(streamnames=["S1"], flowrates=[-300], fractions=[[0.1, 0.3, 0.6]]) >>> print(process1) # Output is as follows: ==================================== Process streams for 'process1' are : ==================================== Stream = S1; Flowrate = -300.00; Fractions = ['0.1000', '0.3000', '0.6000']; Extra Info = None ------------------------------------ END # Next create instance of physical process process2 >>> process2 = pmb.PhysicalProcess("process2") # Attach S2 to process2 >>> process2.S2 = process2.attachstreams(streamnames=["S2"]) >>> process2.S2.setequalto(process1.S1, flowdirection="+") # S2 is inlet thus it has '+' sign for flowdirection >>> print(process2) ==================================== Process streams for 'process2' are : ==================================== Stream = S2; Flowrate = 300.00; Fractions = ['0.1000', '0.3000', '0.6000']; Extra Info = None ------------------------------------ END
- setextrainfo(info)[source]¶
Set extra information of a stream.
- Parameters
- extrainfo: A list of [`str`, …]
String representation of relationship between overall or component flowrates amongst different streams, or some other relationship that can help solve the mass balance.
- Returns
- None
See also
Notes
Creates an attribute ‘extrainfo’ for the stream and assigns it a value.
Examples
# For a physical process 'mixer1' and say a stream 'S1', # flowrate can be set as follows. # First import the module **physicalmassbalance** >>> from pychemengg.massbalances import physicalmassbalance as pmb # Next create instance of physical process, say a mixer >>> mixer1 = pmb.PhysicalProcess("mixer1") # Next, a single stream with a certain name can be attached # --------------------------------------------------------- >>> mixer1.attachstreams(["S1"]) # This will attach a stream named "S1" to "mixer1". # The returned '~.Stream' instance can be stored as follows. >>> S1 = mixer1.attachstreams(["S1"]) # Say that stream S1 overall flowrate is half that of S4 # then S1=0.5*S4 >>> S1.extrainfo[["S1=0.5*S4"]]
- setflow(flow)[source]¶
Sets flowrate of a stream.
- Parameters
- flowint or float or str
Flowrate of the stream.
Use positive int or float for inlet stream.
Use negative int or float for outlet stream.
If flowrate is unknown, use:
“F” or “+F” for inlet stream, and
“-F” for outlet stream
- Returns
- None
See also
Notes
Creates an attribute ‘flowrate’ for the stream and assigns it a value.
Examples
# For a physical process 'mixer1' and say a stream 'S1', # flowrate can be set as follows. # First import the module **physicalmassbalance** >>> from pychemengg.massbalances import physicalmassbalance as pmb # Next create instance of physical process, say a mixer >>> mixer1 = pmb.PhysicalProcess("mixer1") # Next, a single stream with a certain name can be attached # --------------------------------------------------------- >>> mixer1.attachstreams(["S1"]) # This will attach a stream named "S1" to "mixer1". # The returned '~.Stream' instance can be stored as follows. >>> S1 = mixer1.attachstreams(["S1"]) # If flowrate is 1335, it can be assigned as >>> S1.setflow(1335) >>> print(S1.flowrate) 1335 # If flowrate is -300 (assuming stream is outlet) >>> S1.setflow(-300) >>> print(S1.flowrate) -300
- setfractions(fracts)[source]¶
Set component mole/mass fractions of a stream.
- Parameters
- fracts: A list of [`int or float or str`, `int or float or str`, …]
Mass or mole fractions of stream components.
If a fraction for a component is unknown, use:
“x”
- Returns
- None
See also
Notes
Creates an attribute ‘fractions’ for the stream and assigns it a value.
Examples
# For a physical process 'mixer1' and say a stream 'S1', # flowrate can be set as follows. # First import the module **physicalmassbalance** >>> from pychemengg.massbalances import physicalmassbalance as pmb # Next create instance of physical process, say a mixer >>> mixer1 = pmb.PhysicalProcess("mixer1") # Next, a single stream with a certain name can be attached # --------------------------------------------------------- >>> mixer1.attachstreams(["S1"]) # This will attach a stream named "S1" to "mixer1". # The returned '~.Stream' instance can be stored as follows. >>> S1 = mixer1.attachstreams(["S1"]) # If the stream has four components and with mass fractions # 0.1, 0.3, 0.5, 0.1, then specify them as follows: >>> S1.setfractions([0.1, 0.3, 0.5, 0.1] # If one of them in an unknown, say component '3' >>> S.setfractions([0.1, 0.3, "x", 0.1])
- attachstreams(*, streamnames=None, flowrates=None, fractions=None, extrainfos=None)[source]¶
Attach one or more streams to process.
- Parameters
- streamnameslist [str, str, …]
A list containing streamnames.
- flowrateslist [str or int or float, str or int or float, …]. optional
A list containing flowrates for each stream.
Use positive int or float for inlet stream.
Use negative int or float for outlet stream.
If flowrate is unknown, use:
“F” or “+F” for inlet stream, and
“-F” for outlet stream
- fractionslist [ [str or int or float, str or int or float, …], [str or int or float, str or int or float, …], …]. optional
A list containing a list of component fractions for each stream. If the component fraction is unknown use the string “x”.
- extrainfoslist [ [str], [str], …], optional
A list containing extrainfo for a stream represented as a string.
Example:
CASE 1: Relation between component flowrates in different streams is provided.
Let extrainfo : [“2:S1=1.2*S4”]
This means that for component ‘2’, the ‘component flowrate’ in S1 is 1.2 times that in S4.
CASE 2: Relation between overall stream flowrates is provided.
Let extrainfo : [“S1=1.2*S4”]
This means overall flowrate of stream S1 is 1.2 times the overall flowrate of stream S4.
- Returns
- Single ~.Stream instance OR list of ~.Stream instances
If a single stream with just name is created, a single ‘~.Stream’ instance is returned. If more than one stream is created, a list of ~.Stream instances is returned.
Notes
The Stream class requires three arguments:
self : instance of Stream object (passed automatically by Python))
instance of physical process
streamname
By calling the Stream as PhysicalProcess.Stream(self, streamname), the arguments ii) and iii) are being provided. The ‘self’ in this statement is the physical process to which the stream is attached, which is argument ii) and streamname satisfies argument iii).
Attributes
There are three ~.Stream attributes set by this method.
- flowrateint or float or str
Stream flowrate.
- fractionsa list containing [int or float or str, int or float or str, … ]
Component mass/mole fractions of a stream.
- extrainfoa list of [str]
Extra information between component or total flowrates between streams.
Method of attaching ‘Stream’
‘flowrate’ attribute is set as
‘fractions’ attribute is set as
Just the keyword “streamnames” is used
“Not yet defined”
“Not yet defined”
The keywords “streamnames” , “flowrates” , “fractions” are used
User supplied values
User supplied values
extrainfo attribute is either not set or set to user defined values.
Examples
# First import the module **physicalmassbalance** >>> from pychemengg.massbalances import physicalmassbalance as pmb # Next create instance of physical process, say a mixer >>> mixer1 = pmb.PhysicalProcess("mixer1") # How to attach a single stream with a certain name # ------------------------------------------------- >>> mixer1.attachstreams(["S1"]) # This will attach a stream named "S1" to "mixer1". # The returned '~.Stream' instance can be stored as follows. >>> S1 = mixer1.attachstreams(["S1"]) # Flowrates and component fractions must be set # separately using setflow, setfractions, and setextrainfo methods. # How to attach many streams with given names # -------------------------------------------- >>> Water, NaOH, Product = mixer1.attachstreams(["Water", "NaOH", "Product"]) # The returned stream objects are each stored in the stream variables # Water, NaOH, and Product. # Flowrates and component fractions must be set # separately using setflow, setfractions, and setextrainfo methods. # How to attach one or many streams with given names and data # ----------------------------------------------------------- >>> streams = ["Water", "NaOH", "Product"] >>> flows = ["F", 1000, "-F"] # Use 'F' or '+F' if flowrate is unknown for inlet stream # Use '-F' if flowrate is unknown for outlet stream >>> fractions = [ ["x",0], [0,1], ["x", 0.1] ] # Use 'x' if a component fraction is unknown. >>> extrainfo = [ [], [], ["Water=0.9*Product"] ] >>> Water, NaOH, Product = mixer1.attachstreams(streamnames=streams, flowrates=flows, fractions=fractions, extrainfos=extrainfo) # Here three streams are created. # The returned stream objects are each stored in the stream variables # Water, NaOH, and Product. # The above process can be carried for one or many streams.
- degreesoffreedom()[source]¶
Perform degree of freedom analysis on the physical process.
- Parameters
- NoneMethod is applied to the physical process.
- Returns
- NoneNone.
Multiple “self” attributes are created. These are used to formulate balances that are required to solve for “F”, “-F”, and “x”
- Attributes
- unknown_x_countint
Count of total unknown fractions in streams attached to the process.
- unknownplusF_countint
Count of total unknown inlet stream flowrates.
- unknownminusF_countint
Count of total unknown outlet stream flowrates.
- unknown_Fs_countint
Count of total unknown inlet + outlet stream flowrates.
- totalunknownsint
Sum of unknown_Fs_count + unknown_x_count
- extrainfo_countint
Count of total extra information available for the process.
- component_countint
Total components in the process streams
- streamswith_unknownx_countint
Count of streams that contain at least one unknown component fraction.
- totalequations_possibleint
Sum of extrainfo_count + component_count + streamswith_unknownx_count
- find_unknownflowrate()[source]¶
Compute unknown flowrate “F” or “-F”.
- Parameters
- physical processInstance of ~.PhysicalProcess
Physical process for which ‘F’/’-F’ are to be computed.
- Returns
- A list. Each list can take one of the following form.
- - If ‘F’/’-F’ is computable[[True, streamname, missingflowrate_value]]
- - If ‘F’/’-F’ is not computable[[False, textmessage]]
Notes
Attempt is made to compute the unknown flowrate for a process using # ΣFin + ΣFout = 0. This approach can only work if there is just one unknown ‘F’ or ‘-F’ in a given process.
Examples
>>> from pychemengg.massbalances import physicalmassbalance as pmb # Next create instance of physical process, say it is called 'process1' >>> process1 = pmb.PhysicalProcess("process1") # Attach two inlet streams and one outlet stream to process1. >>> process1.S1 = process1.attachstreams(streamnames=["S1"], flowrates=[300], fractions=[[0.1, 0.3, 0.6]]) >>> process1.S2 = process1.attachstreams(streamnames=["S2"], flowrates=[200], fractions=[[0.1, 0.12, "x"]]) # The stream below is the outlet stream. Note the flowrate has a negative sign. >>> process1.S3 = process1.attachstreams(streamnames=["S3"], flowrates=["-F"], fractions=[[0.1, "x", "x"]]) # Apply the find_unknownflowrate() method on the process >>> unknownF = process1.find_unknownflowrate() >>> print(unknownF) Output is: [[True, 'S3', -500.0]]
- find_unknownfractions(streamname=None)[source]¶
Compute unknown fractions ‘x’.
- Parameters
- physical processInstance of ~.PhysicalProcess
Physical process for which ‘x’ are to be computed
- streamnameinstance of ~.PhysicalProcess.Stream, default = None
A specific stream of the PhysicalProcess for which ‘x’ is to be computed. If this parameter is not provided, all streams of the physical process are considered for finding the ‘x’.
- Returns
- A list of lists. Each sublist item can take one of the following form.
- - If ‘x’ is computable[True, streamname, location_wherexunknown, missingxvalue]
- - If ‘x’ are not computable[False, streamname, textmessage]
Notes
Attempt is made to compute the unknown ‘x’ fraction for each stream using Σx = 1. This approach can only work if there is just one unknown ‘x’ in a given stream.
Examples
>>> from pychemengg.massbalances import physicalmassbalance as pmb # Next create instance of physical process, say it is called 'process1' >>> process1 = pmb.PhysicalProcess("process1") # Attach three inlet streams to process1. >>> process1.S1 = process1.attachstreams(streamnames=["S1"], flowrates=[300], fractions=[[0.1, 0.3, 0.6]]) >>> process1.S2 = process1.attachstreams(streamnames=["S2"], flowrates=[200], fractions=[[0.1, 0.12, "x"]]) >>> process1.S3 = process1.attachstreams(streamnames=["S3"], flowrates=[700], fractions=[[0.1, "x", "x"]]) # Apply the find_unknownfractions() method on the process >>> unknownx = process1.find_unknownfractions() >>> print(unknownx) Output is: [[False, 'S1', 'There are no missing fractions.'], [True, 'S2', 2, 0.78], [False, 'S3', "More than one 'x' unknown fractions in the stream. Σx = 1 is insufficient to find them."]] # Apply the find_unknownfractions() method on one of the process stream >>> unkn = process1.find_unknownfractions(streamname="S1") >>> print(unkn) Output is: [[False, 'S1', 'There are no missing fractions.']]
- perform_component_massbalance()[source]¶
Execute component mass balance on a process.
\((ΣF_ix_{i,component})\) = 0; where i = stream index
- Parameters
- physical processInstance of ~.PhysicalProcess
Physical process for which component mass balance is to be performed.
- Returns
- None
See also
Notes
Logic for component mass balance
The nparray of stream flowrates has shape (n,1):
n : number of streams
The nparray of stream flowrates has shape (n,m):
n : number of streams
m : number of components per stream
For example consider three streams:
Stream
Flowrate
Fractions
Inlet 1
F1
[x11, x12, x13]
Inlet 2
F2
[x21, x22, x23]
Outlet 3
F3
[x31, x32, x33]
The nparray for flowrates is:
A =
[[F1]
[F2]
[F3]]
shape = (3, 1)
The nparray for component fractions is:
B =
[[x11, x12, x13]
[x21, x22, x23]
[x31, x32, x33]]
shape = (3, 3)
\(x_{m,n}\) :
m : stream index
n : component index
To perform component mass balance, the row of matrix ‘A’ is multiplied to row of matrix ‘B’
C =
[[F1*x11, F1*x12, F1*x13]
[F2*x21, F2*x22, F2*x23]
[F3*x31, F3*x32, F3*x33]]
Each column of ‘C’ represents flowrate of a component. By summing the column of ‘C’ the component mass balance can be performed.
Sum of column 1 = Flowrate balance for component 1
Sum of column 2 = Flowrate balance for component 2
Examples
>>> from pychemengg.massbalances import physicalmassbalance as pmb # Next create instance of physical process, say it is called 'process1' >>> process1 = pmb.PhysicalProcess("process1") # Attach two inlet streams and one outlet stream to process1. >>> process1.S1 = process1.attachstreams(streamnames=["S1"], flowrates=[300], fractions=[[0.4333, 0.3, 0.2667]]) >>> process1.S2 = process1.attachstreams(streamnames=["S2"], flowrates=[200], fractions=[[0.1, 0.12, 0.78]]) # The stream below is the outlet stream. Note the flowrate has a negative sign. >>> process1.S3 = process1.attachstreams(streamnames=["S3"], flowrates=[-500], fractions=[[0.3, 0.228, 0.472]]) # Apply the perform_component_massbalance() method on the process >>> componentbalance = process1.perform_component_massbalance() >>> print("component balance: ", componentbalance) Output is: component balance: [-0.01 0. 0.01] # This means that: # For component 1: ΣFx = -0.01 # For component 2: ΣFx = 0. # For component 3: ΣFx = 0.01 # The above balance can be set up as part of a function to solve # for unknown "F", "-F", and "x". See 'User Tutorial'
- solvesystem()[source]¶
Solves the physical process to find “F”, “-F”, and “x”
- Parameters
- NoneApply the method to the physical process to be solved.
- Returns
- solutionInstance of the class PhysicalProcess
A new instance of PhysicalProcess is created that stores the solution. The solution can be printed.
- update_streamflowrates(newflow)[source]¶
Update flowrate of a stream.
This method should be used after finding unknown ‘x’ using ‘find_unknownflowrate’.
- Parameters
- new_flowlist
A list containing information to update stream fractions.
The form of the list is: - [[False, textmessage]] - [[True, streamname, missingflowrate_value]]
- Returns
- None
Examples
>>> from pychemengg.massbalances import physicalmassbalance as pmb # Next create instance of physical process, say it is called 'process1' >>> process1 = pmb.PhysicalProcess("process1") # Attach two inlet streams and one outlet stream to process1. >>> process1.S1 = process1.attachstreams(streamnames=["S1"], flowrates=[300], fractions=[[0.1, 0.3, 0.6]]) >>> process1.S2 = process1.attachstreams(streamnames=["S2"], flowrates=[200], fractions=[[0.1, 0.12, "x"]]) # The stream below is the outlet stream. Note the flowrate has a negative sign. >>> process1.S3 = process1.attachstreams(streamnames=["S3"], flowrates=["-F"], fractions=[[0.1, "x", "x"]]) # Apply the find_unknownflowrate() method on the process >>> unknownF = process1.find_unknownflowrate() >>> process1.update_streamflowrates(unknownF) Output is: For " process1 " : the stream " S3 " has new flowrate = -500.0
- update_streamfractions(new_fractioninfo)[source]¶
Update mole/mass fraction of a stream.
This method should be used after finding unknown ‘x’ using ‘find_unknownfractions’.
- Parameters
- new_fractioninfolist
A list containing information to update stream fractions.
The form of the list is: - [True, streamname, location_wherexunknown, missingx_value] - [False, streamname, missingx_value]
- Returns
- None
Examples
>>> from pychemengg.massbalances import physicalmassbalance as pmb # Next create instance of physical process, say it is called 'process1' >>> process1 = pmb.PhysicalProcess("process1") # Attach three inlet streams to process1. >>> process1.S1 = process1.attachstreams(streamnames=["S1"], flowrates=[300], fractions=[[0.1, 0.3, 0.6]]) >>> process1.S2 = process1.attachstreams(streamnames=["S2"], flowrates=[200], fractions=[[0.1, 0.12, "x"]]) >>> process1.S3 = process1.attachstreams(streamnames=["S3"], flowrates=[700], fractions=[[0.1, "x", "x"]]) # Apply the find_unknownfractions() method on the process >>> unknownx = process1.find_unknownfractions() >>> print(unknownx) Output is: [[False, 'S1', 'There are no missing fractions.'], [True, 'S2', 2, 0.78], [False, 'S3', "More than one 'x' unknown fractions in the stream. Σx = 1 is insufficient to find them."]] # Use unknownx to update streams >>> process1.update_streamfractions(unknownx) Output is : For " process1 " : the stream " S1 " There are no missing fractions. For " process1 " : the stream " S2 " The new component fraction @ position = " 2 " is now = 0.78 For " process1 " : the stream " S3 " More than one 'x' unknown fractions in the stream. Σx = 1 is insufficient to find them.