@@ -2149,6 +2149,10 @@ def _thorough_digraph(self, graph_attrs):
21492149 def _cluster_digraph (self , graph_attrs ):
21502150 return digraph_from_system (self , ** graph_attrs )
21512151
2152+ def _stage_digraph (self , graph_attrs ):
2153+ with self .stage_configuration (aggregated = False ) as conf :
2154+ return digraph_from_units (conf .stages , conf .streams , ** graph_attrs )
2155+
21522156 def diagram (self , kind : Optional [int | str ]= None , file : Optional [str ]= None ,
21532157 format : Optional [str ]= None , display : Optional [bool ]= True ,
21542158 number : Optional [bool ]= None , profile : Optional [bool ]= None ,
@@ -2163,9 +2167,10 @@ def diagram(self, kind: Optional[int|str]=None, file: Optional[str]=None,
21632167 ----------
21642168 kind :
21652169 * 0 or 'cluster': Display all units clustered by system.
2166- * 1 or 'thorough': Display every unit within the path .
2170+ * 1 or 'thorough': Display every unit within the system .
21672171 * 2 or 'surface': Display only elements listed in the path.
2168- * 3 or 'minimal': Display a single box representing all units.
2172+ * 3 or 'minimal': Display a single box representing the system.
2173+ * 4 or 'stage': Display every stage within the system.
21692174 file :
21702175 File name to save diagram.
21712176 format:
@@ -2203,6 +2208,8 @@ def diagram(self, kind: Optional[int|str]=None, file: Optional[str]=None,
22032208 f = self ._surface_digraph (graph_attrs )
22042209 elif kind == 3 or kind == 'minimal' :
22052210 f = self ._minimal_digraph (graph_attrs )
2211+ elif kind == 4 or kind == 'stage' :
2212+ f = self ._stage_digraph (graph_attrs )
22062213 else :
22072214 raise ValueError ("kind must be one of the following: "
22082215 "0 or 'cluster', 1 or 'thorough', 2 or 'surface', "
@@ -3486,7 +3493,14 @@ def results(self, with_units=True):
34863493 return series
34873494
34883495 # Report summary
3489- def save_report (self , file : Optional [str ]= 'report.xlsx' , dpi : Optional [str ]= '900' , ** stream_properties ):
3496+ def save_report (
3497+ self ,
3498+ file : Optional [str ]= 'report.xlsx' ,
3499+ dpi : Optional [str ]= '900' ,
3500+ sheets = None ,
3501+ stage = False ,
3502+ ** stream_properties
3503+ ):
34903504 """
34913505 Save a system report as an xlsx file.
34923506
@@ -3500,83 +3514,108 @@ def save_report(self, file: Optional[str]='report.xlsx', dpi: Optional[str]='900
35003514 Additional stream properties and units as key-value pairs (e.g. T='degC', flow='gpm', H='kW', etc..)
35013515
35023516 """
3517+ if sheets is None :
3518+ sheets = {
3519+ 'Flowsheet' ,
3520+ 'Itemized costs' ,
3521+ 'Stream table' ,
3522+ 'Utilities' ,
3523+ 'Design requirements' ,
3524+ 'Reactions' ,
3525+ # 'Specifications'
3526+ }
35033527 writer = pd .ExcelWriter (file )
35043528 units = sorted (self .units , key = lambda x : x .line )
35053529 cost_units = [i for i in units if i ._design or i ._cost ]
3506- try :
3507- with bst .preferences .temporary () as p :
3508- p .reset ()
3509- p .light_mode ()
3510- self .diagram ('thorough' , file = 'flowsheet' , dpi = str (dpi ), format = 'png' )
3511- except :
3512- diagram_completed = False
3513- warn (RuntimeWarning ('failed to generate diagram through graphviz' ), stacklevel = 2 )
3514- else :
3515- import PIL .Image
3530+ if 'Flowsheet' in sheets :
35163531 try :
3517- # Assume openpyxl is used
3518- worksheet = writer .book .create_sheet ('Flowsheet' )
3519- flowsheet = openpyxl .drawing .image .Image ('flowsheet.png' )
3520- worksheet .add_image (flowsheet , anchor = 'A1' )
3521- except PIL .Image .DecompressionBombError :
3522- PIL .Image .MAX_IMAGE_PIXELS = int (1e9 )
3523- flowsheet = openpyxl .drawing .image .Image ('flowsheet.png' )
3524- worksheet .add_image (flowsheet , anchor = 'A1' )
3532+ with bst .preferences .temporary () as p :
3533+ p .reset ()
3534+ p .light_mode ()
3535+ kind = 'stage' if stage else 'thorough'
3536+ self .diagram (kind , file = 'flowsheet' , dpi = str (dpi ), format = 'png' )
35253537 except :
3526- # Assume xlsx writer is used
3538+ diagram_completed = False
3539+ warn (RuntimeWarning ('failed to generate diagram through graphviz' ), stacklevel = 2 )
3540+ else :
3541+ import PIL .Image
35273542 try :
3528- worksheet = writer .book .add_worksheet ('Flowsheet' )
3543+ # Assume openpyxl is used
3544+ worksheet = writer .book .create_sheet ('Flowsheet' )
3545+ flowsheet = openpyxl .drawing .image .Image ('flowsheet.png' )
3546+ worksheet .add_image (flowsheet , anchor = 'A1' )
3547+ except PIL .Image .DecompressionBombError :
3548+ PIL .Image .MAX_IMAGE_PIXELS = int (1e9 )
3549+ flowsheet = openpyxl .drawing .image .Image ('flowsheet.png' )
3550+ worksheet .add_image (flowsheet , anchor = 'A1' )
35293551 except :
3530- warn ("problem in saving flowsheet; please submit issue to BioSTEAM with"
3531- "your current version of openpyxl and xlsx writer" , RuntimeWarning )
3532- worksheet .insert_image ('A1' , 'flowsheet.png' )
3533- diagram_completed = True
3552+ # Assume xlsx writer is used
3553+ try :
3554+ worksheet = writer .book .add_worksheet ('Flowsheet' )
3555+ except :
3556+ warn ("problem in saving flowsheet; please submit issue to BioSTEAM with"
3557+ "your current version of openpyxl and xlsx writer" , RuntimeWarning )
3558+ worksheet .insert_image ('A1' , 'flowsheet.png' )
3559+ diagram_completed = True
35343560
3535- tea = self .TEA
3536- if tea :
3561+ if 'Itemized costs' in sheets :
35373562 tea = self .TEA
3538- cost = report .cost_table (tea )
3539- cost .to_excel (writer , 'Itemized costs' )
3540- tea .get_cashflow_table ().to_excel (writer , 'Cash flow' )
3541- else :
3542- warn (f'Cannot find TEA object in { repr (self )} . Ignoring TEA sheets.' ,
3543- RuntimeWarning , stacklevel = 2 )
3544-
3545- # Stream tables
3546- # Organize streams by chemicals first
3547- streams_by_chemicals = {}
3548- for i in self .streams :
3549- if not i : continue
3550- chemicals = i .chemicals
3551- if chemicals in streams_by_chemicals :
3552- streams_by_chemicals [chemicals ].append (i )
3563+ if tea :
3564+ tea = self .TEA
3565+ cost = report .cost_table (tea )
3566+ cost .to_excel (writer , 'Itemized costs' )
3567+ tea .get_cashflow_table ().to_excel (writer , 'Cash flow' )
35533568 else :
3554- streams_by_chemicals [chemicals ] = [i ]
3555- stream_tables = []
3556- for chemicals , streams in streams_by_chemicals .items ():
3557- stream_tables .append (report .stream_table (streams , chemicals = chemicals , T = 'K' , ** stream_properties ))
3558- report .tables_to_excel (stream_tables , writer , 'Stream table' )
3569+ warn (f'Cannot find TEA object in { repr (self )} . Ignoring TEA sheets.' ,
3570+ RuntimeWarning , stacklevel = 2 )
35593571
3560- # Heat utility tables
3561- heat_utilities = report .heat_utility_tables (cost_units )
3562- n_row = report .tables_to_excel (heat_utilities , writer , 'Utilities' )
3572+ if 'Stream table' in sheets :
3573+ if stage :
3574+ with self .stage_configuration () as conf :
3575+ stream_tables = report .stream_tables (conf .streams , ** stream_properties )
3576+ else :
3577+ stream_tables = report .stream_tables (self .streams , ** stream_properties )
3578+ report .tables_to_excel (stream_tables , writer , 'Stream table' )
35633579
3564- # Power utility table
3565- power_utility = report .power_utility_table (cost_units )
3566- n_row = report .tables_to_excel ([power_utility ], writer , 'Utilities' , n_row = n_row )
3580+ if 'Utilities' in sheets :
3581+ # Heat utility tables
3582+ heat_utilities = report .heat_utility_tables (cost_units )
3583+ n_row = report .tables_to_excel (heat_utilities , writer , 'Utilities' )
35673584
3568- # Fees table
3569- other_utilities = report .other_utilities_table (cost_units )
3570- n_row = report .tables_to_excel (other_utilities , writer , 'Utilities' , n_row = n_row )
3585+ # Power utility table
3586+ power_utility = report .power_utility_table (cost_units )
3587+ n_row = report .tables_to_excel ([power_utility ], writer , 'Utilities' , n_row = n_row )
3588+
3589+ # Fees table
3590+ other_utilities = report .other_utilities_table (cost_units )
3591+ n_row = report .tables_to_excel (other_utilities , writer , 'Utilities' , n_row = n_row )
35713592
3572- # General desing requirements
3573- results = report .unit_result_tables (cost_units )
3574- report .tables_to_excel (results , writer , 'Design requirements' )
3593+ if 'Design requirements' in sheets :
3594+ # General desing requirements
3595+ results = report .unit_result_tables (cost_units )
3596+ report .tables_to_excel (results , writer , 'Design requirements' )
35753597
3576- # Reaction tables
3577- reactions = report .unit_reaction_tables (units )
3578- report .tables_to_excel (reactions , writer , 'Reactions' )
3598+ if 'Reactions' in sheets :
3599+ # Reaction tables
3600+ reactions = report .unit_reaction_tables (units )
3601+ report .tables_to_excel (reactions , writer , 'Reactions' )
35793602
3603+ if 'Specifications' in sheets :
3604+ if stage :
3605+ with self .stage_configuration () as conf :
3606+ specifications = [
3607+ u ._mass_and_energy_balance_specifications_table ()
3608+ for u in conf .stages
3609+ ]
3610+ else :
3611+ specifications = [
3612+ u ._mass_and_energy_balance_specifications_table ()
3613+ for u in units
3614+ ]
3615+ report .tables_to_excel (
3616+ specifications , writer ,
3617+ 'Specifications'
3618+ )
35803619 writer .close ()
35813620 if diagram_completed : os .remove ("flowsheet.png" )
35823621
0 commit comments