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.

__init__(processname)[source]

Methods

__init__(processname)

attachstreams(*[, streamnames, flowrates, …])

Attach one or more streams to process.

degreesoffreedom()

Perform degree of freedom analysis on the physical process.

find_unknownflowrate()

Compute unknown flowrate “F” or “-F”.

find_unknownfractions([streamname])

Compute unknown fractions ‘x’.

perform_component_massbalance()

Execute component mass balance on a process.

solvesystem()

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

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

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

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

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

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:

    1. self : instance of Stream object (passed automatically by Python))

    2. instance of physical process

    3. 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

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.