##############################################################################
# Institute for the Design of Advanced Energy Systems Process Systems
# Engineering Framework (IDAES PSE Framework) Copyright (c) 2018-2020, by the
# software owners: The Regents of the University of California, through
# Lawrence Berkeley National Laboratory,  National Technology & Engineering
# Solutions of Sandia, LLC, Carnegie Mellon University, West Virginia
# University Research Corporation, et al. All rights reserved.
#
# Please see the files COPYRIGHT.txt and LICENSE.txt for full copyright and
# license information, respectively. Both files are also available online
# at the URL "https://github.com/IDAES/idaes-pse".
##############################################################################
# =============================================================================
"""
SCPC Boiler subflowsheet

Inputs:
    BFW - boiler feed water (from Feed water heaters)
    Coal from pulverizers

Main Assumptions:
    Coal flowrate as a function of load, coal HHV is fixed and heat dutty
    splitt from fire side to water wall and platen superheater is fixed.

    Boiler heat exchanger network:
        Water Flow:
            BFW -> ECONOMIZER -> Water Wall -> Primary SH -> Platen SH -> Finishing Superheate -> HP Turbine -> Reheater -> IP Turbine
        Flue Gas Flow:
            Fire Ball -> Platen SH -> Finishing SH -> Reheater  -> o -> Economizer -> Air Preheater
                                                   -> Primary SH --^

        *HP Turbine, IP Turbine, Air Preheater ==> not included in this release

    Models used:
        - Mixers: Attemperator, Flue gas mix
        - Heater: Platen SH, Fire/Water side (simplified model)
        - BoilerHeatExchanger: Economizer, Primary SH, Finishing SH, Reheater
            + Shell and tube heat exchanger
                - tube side: Steam (side 1 holdup)
                - shell side: flue gas (side 2 holdup)

    Property packages used:
        - IAPWS: Water/steam side
        - IDEAL GAS: Flue Gas side

Created: 1/10/2020 by Boiler subsystem team (M Zamarripa)

"""
__author__ = "Miguel Zamarripa"

# Import Pyomo libraries
from pyomo.environ import ConcreteModel, SolverFactory, value, \
    TransformationFactory
from pyomo.network import Arc
from idaes.core.util.misc import svg_tag
# Import IDAES core
from idaes.core import FlowsheetBlock

# Import Unit Model Modules
from idaes.generic_models.properties import iapws95

# Import Property Modules
from idaes.power_generation.properties import FlueGasParameterBlock

# Import Unit Model Modules
from idaes.generic_models.unit_models import Heater, Mixer
from idaes.power_generation.unit_models.boiler_heat_exchanger import (
    BoilerHeatExchanger, TubeArrangement, DeltaTMethod)
from idaes.generic_models.unit_models.separator import Separator, \
    SplittingType, EnergySplittingType
from pyomo.common.fileutils import this_file_dir
from collections import OrderedDict
import os
from idaes.core.util.model_statistics import degrees_of_freedom


def main():
    """
    Make the flowsheet object, fix some variables, and solve the problem
    """
    # Create a Concrete Model as the top level object
    m = ConcreteModel()

    # Add a flowsheet object to the model
    m.fs = FlowsheetBlock(default={"dynamic": False})

    m.fs.prop_water = iapws95.Iapws95ParameterBlock()

    build_boiler(m.fs)

    TransformationFactory("network.expand_arcs").apply_to(m)

    # Create a solver
    solver = SolverFactory('ipopt')
    return(m, solver)


def build_boiler(fs):

    # Add property packages to flowsheet library
    fs.prop_fluegas = FlueGasParameterBlock()

    # Create unit models
    # Boiler Economizer
    fs.ECON = BoilerHeatExchanger(default={
        "side_1_property_package": fs.prop_water,
        "side_2_property_package": fs.prop_fluegas,
        "has_pressure_change": True,
        "has_holdup": False,
        "delta_T_method": DeltaTMethod.counterCurrent,
        "tube_arrangement": TubeArrangement.inLine,
        "side_1_water_phase": "Liq",
        "has_radiation": False})
    # Primary Superheater
    fs.PrSH = BoilerHeatExchanger(default={
        "side_1_property_package": fs.prop_water,
        "side_2_property_package": fs.prop_fluegas,
        "has_pressure_change": True,
        "has_holdup": False,
        "delta_T_method": DeltaTMethod.counterCurrent,
        "tube_arrangement": TubeArrangement.inLine,
        "side_1_water_phase": "Vap",
        "has_radiation": True})

    # Finishing Superheater
    fs.FSH = BoilerHeatExchanger(default={
        "side_1_property_package": fs.prop_water,
        "side_2_property_package": fs.prop_fluegas,
        "has_pressure_change": True,
        "has_holdup": False,
        "delta_T_method": DeltaTMethod.counterCurrent,
        "tube_arrangement": TubeArrangement.inLine,
        "side_1_water_phase": "Vap",
        "has_radiation": True})

    # Reheater
    fs.RH = BoilerHeatExchanger(default={
        "side_1_property_package": fs.prop_water,
        "side_2_property_package": fs.prop_fluegas,
        "has_pressure_change": True,
        "has_holdup": False,
        "delta_T_method": DeltaTMethod.counterCurrent,
        "tube_arrangement": TubeArrangement.inLine,
        "side_1_water_phase": "Vap",
        "has_radiation": True})
    # Platen Superheater
    fs.PlSH = Heater(
            default={"property_package": fs.prop_water})

    # Boiler Water Wall
    fs.Water_wall = Heater(default={"property_package": fs.prop_water})

    # Boiler Splitter (splits FSH flue gas outlet to Reheater and PrSH)
    fs.Spl1 = Separator(default={"property_package": fs.prop_fluegas,
                                 "split_basis": SplittingType.totalFlow,
                                 "energy_split_basis":
                                     EnergySplittingType.equal_temperature
                                 })
    # Flue gas mixer (mixing FG from Reheater and Primary SH, inlet to ECON)
    fs.mix1 = Mixer(default={"property_package": fs.prop_fluegas,
                             "inlet_list": ['Reheat_out', 'PrSH_out'],
                             "dynamic": False})

    # Mixer for Attemperator #1 (between PrSH and PlSH)
    fs.ATMP1 = Mixer(default={"property_package": fs.prop_water,
                              "inlet_list": ['Steam', 'SprayWater'],
                              "dynamic": False})

    # Build connections (streams)

    # Steam Route (side 1 = tube side = steam/water side)
    # Boiler feed water to Economizer (to be imported in full plant model)
#    fs.bfw2econ = Arc(source=fs.FWH8.outlet,
#                           destination=fs.ECON.side_1_inlet)
    fs.econ2ww = Arc(source=fs.ECON.side_1_outlet,
                     destination=fs.Water_wall.inlet)
    fs.ww2prsh = Arc(source=fs.Water_wall.outlet,
                     destination=fs.PrSH.side_1_inlet)
    fs.prsh2plsh = Arc(source=fs.PrSH.side_1_outlet,
                       destination=fs.PlSH.inlet)
    fs.plsh2fsh = Arc(source=fs.PlSH.outlet,
                      destination=fs.FSH.side_1_inlet)
    fs.FSHtoATMP1 = Arc(source=fs.FSH.side_1_outlet,
                        destination=fs.ATMP1.Steam)
#    fs.fsh2hpturbine=Arc(source=fs.ATMP1.outlet,
#                           destination=fs.HPTinlet)
    # (to be imported in full plant model)

    # Flue gas route ---------------------------------------------------------
    # water wall connected with boiler block (to fix the heat duty)
    # platen SH connected with boiler block (to fix the heat duty)
    # Finishing superheater connected with a flowsheet level constraint
    fs.fg_fsh2_separator = Arc(source=fs.FSH.side_2_outlet,
                               destination=fs.Spl1.inlet)
    fs.fg_fsh2rh = Arc(source=fs.Spl1.outlet_1,
                       destination=fs.RH.side_2_inlet)
    fs.fg_fsh2PrSH = Arc(source=fs.Spl1.outlet_2,
                         destination=fs.PrSH.side_2_inlet)
    fs.fg_rhtomix = Arc(source=fs.RH.side_2_outlet,
                        destination=fs.mix1.Reheat_out)
    fs.fg_prsh2mix = Arc(source=fs.PrSH.side_2_outlet,
                         destination=fs.mix1.PrSH_out)
    fs.fg_mix2econ = Arc(source=fs.mix1.outlet,
                         destination=fs.ECON.side_2_inlet)


# Set inputs ==========================
def initialize(m):
    # ------------- ECONOMIZER -----------------------------------------------
    # BFW Boiler Feed Water inlet temeperature = 555 F = 563.706 K
    m.fs.ECON.side_1_inlet.flow_mol[0].fix(24194.177)  # mol/s
    m.fs.ECON.side_1_inlet.enth_mol[0].fix(14990.97)
    m.fs.ECON.side_1_inlet.pressure[0].fix(26922222.222)  # Pa

    # FLUE GAS Inlet from Primary Superheater
    FGrate = 21290.6999  # mol/s
    # Use FG molar composition to set component flow rates (baseline report)
    m.fs.ECON.side_2_inlet.flow_component[0, "H2O"].fix(FGrate*8.69/100)
    m.fs.ECON.side_2_inlet.flow_component[0, "CO2"].fix(FGrate*14.49/100)
    m.fs.ECON.side_2_inlet.flow_component[0, "N2"].fix(FGrate*74.34/100)
    m.fs.ECON.side_2_inlet.flow_component[0, "O2"].fix(FGrate*2.47/100)
    m.fs.ECON.side_2_inlet.flow_component[0, "NO"].fix(FGrate*0.0006)
    m.fs.ECON.side_2_inlet.flow_component[0, "SO2"].fix(FGrate*0.002)
    m.fs.ECON.side_2_inlet.temperature[0].fix(682.335)  # K
    m.fs.ECON.side_2_inlet.pressure[0].fix(100145)  # Pa

    # economizer design variables and parameters
    ITM = 0.0254  # inch to meter conversion
    # Based on NETL Baseline Report Rev3
    m.fs.ECON.tube_di.fix((2-2*0.188)*ITM)  # calc inner diameter
    #                                   (2 = outer diameter, thickness = 0.188)
    m.fs.ECON.tube_thickness.fix(0.188*ITM)  # tube thickness
    m.fs.ECON.pitch_x.fix(3.5*ITM)
    # pitch_y = (54.5) gas path transverse width /columns
    m.fs.ECON.pitch_y.fix(5.03*ITM)
    m.fs.ECON.tube_length.fix(53.41*12*ITM)  # use tube length (53.41 ft)
    m.fs.ECON.tube_nrow.fix(36*2.5)         # use to match baseline performance
    m.fs.ECON.tube_ncol.fix(130)            # 130 from NETL
    m.fs.ECON.nrow_inlet.fix(2)
    m.fs.ECON.delta_elevation.fix(50)
    # parameters
    # heat transfer resistance due to tube side fouling (water scales)
    m.fs.ECON.tube_r_fouling = 0.000176
    # heat transfer resistance due to tube shell fouling (ash deposition)
    m.fs.ECON.shell_r_fouling = 0.00088
    if m.fs.ECON.config.has_radiation is True:
        m.fs.ECON.emissivity_wall.fix(0.7)  # wall emissivity
    # correction factor for overall heat transfer coefficient
    m.fs.ECON.fcorrection_htc.fix(1.5)
    # correction factor for pressure drop calc tube side
    m.fs.ECON.fcorrection_dp_tube.fix(1.0)
    # correction factor for pressure drop calc shell side
    m.fs.ECON.fcorrection_dp_shell.fix(1.0)

    # --------- Primary Superheater ------------
    # Steam from water wall
    hprsh = iapws95.htpx(773.15, 24865516.722)
    print(hprsh)
    m.fs.PrSH.side_1_inlet.flow_mol[0].fix(24190.26)   # mol/s
    m.fs.PrSH.side_1_inlet.enth_mol[0].fix(hprsh)      # J/mol
    m.fs.PrSH.side_1_inlet.pressure[0].fix(2.5449e7)   # Pascals

    # FLUE GAS Inlet from Primary Superheater
    FGrate = 21290.6999*0.18  # mol/s
    # Use FG molar composition to set component flow rates (baseline report)
    m.fs.PrSH.side_2_inlet.flow_component[0, "H2O"].fix(FGrate*8.69/100)
    m.fs.PrSH.side_2_inlet.flow_component[0, "CO2"].fix(FGrate*14.49/100)
    m.fs.PrSH.side_2_inlet.flow_component[0, "N2"].fix(FGrate*74.34/100)
    m.fs.PrSH.side_2_inlet.flow_component[0, "O2"].fix(FGrate*2.47/100)
    m.fs.PrSH.side_2_inlet.flow_component[0, "NO"].fix(FGrate*0.0006)
    m.fs.PrSH.side_2_inlet.flow_component[0, "SO2"].fix(FGrate*0.002)
    m.fs.PrSH.side_2_inlet.temperature[0].fix(1180.335)
    m.fs.PrSH.side_2_inlet.pressure[0].fix(100145)

    # Primary Superheater
    ITM = 0.0254  # inch to meter conversion
    m.fs.PrSH.tube_di.fix((2.5-2*0.165)*ITM)
    m.fs.PrSH.tube_thickness.fix(0.165*ITM)
    m.fs.PrSH.pitch_x.fix(3*ITM)
    # gas path transverse width 54.78 ft / number of columns
    m.fs.PrSH.pitch_y.fix(54.78/108*12*ITM)
    m.fs.PrSH.tube_length.fix(53.13*12*ITM)
    m.fs.PrSH.tube_nrow.fix(20*2)
    m.fs.PrSH.tube_ncol.fix(108)
    m.fs.PrSH.nrow_inlet.fix(4)
    m.fs.PrSH.delta_elevation.fix(50)
    m.fs.PrSH.tube_r_fouling = 0.000176   # (0.001 h-ft^2-F/BTU)
    m.fs.PrSH.shell_r_fouling = 0.003131  # (0.03131 - 0.1779 h-ft^2-F/BTU)
    if m.fs.PrSH.config.has_radiation is True:
        m.fs.PrSH.emissivity_wall.fix(0.7)  # wall emissivity
    # correction factor for overall heat transfer coefficient
    m.fs.PrSH.fcorrection_htc.fix(1.5)
    # correction factor for pressure drop calc tube side
    m.fs.PrSH.fcorrection_dp_tube.fix(1.0)
    # correction factor for pressure drop calc shell side
    m.fs.PrSH.fcorrection_dp_shell.fix(1.0)

    #  ----- Finishing Superheater ----------------------
    # Steam from Platen Supeheater
    hfsh = iapws95.htpx(823.15, 24790249.01)
    m.fs.FSH.side_1_inlet.flow_mol[0].fix(24194.177)    # mol/s
    m.fs.FSH.side_1_inlet.enth_mol[0].fix(hfsh)         # J/mol
    m.fs.FSH.side_1_inlet.pressure[0].fix(24790249.01)  # Pascals

    # FLUE GAS Inlet from Primary Superheater
    FGrate = 21290.6999  # mol/s
    # Use FG molar composition to set component flow rates (baseline report)
    m.fs.FSH.side_2_inlet.flow_component[0, "H2O"].fix(FGrate*8.69/100)
    m.fs.FSH.side_2_inlet.flow_component[0, "CO2"].fix(FGrate*14.49/100)
    m.fs.FSH.side_2_inlet.flow_component[0, "N2"].fix(FGrate*74.34/100)
    m.fs.FSH.side_2_inlet.flow_component[0, "O2"].fix(FGrate*2.47/100)
    m.fs.FSH.side_2_inlet.flow_component[0, "NO"].fix(FGrate*0.0006)
    m.fs.FSH.side_2_inlet.flow_component[0, "SO2"].fix(FGrate*0.002)
    m.fs.FSH.side_2_inlet.temperature[0].fix(1300.335)
    m.fs.FSH.side_2_inlet.pressure[0].fix(100145)

    # Finishing Superheater
    ITM = 0.0254  # inch to meter conversion
    m.fs.FSH.tube_di.fix((2.5-2*0.165)*ITM)
    m.fs.FSH.tube_thickness.fix(0.165*ITM)
    m.fs.FSH.pitch_x.fix(3*ITM)
    # gas path transverse width 54.78 ft / number of columns
    m.fs.FSH.pitch_y.fix(54.78/108*12*ITM)
    m.fs.FSH.tube_length.fix(53.13*12*ITM)
    m.fs.FSH.tube_nrow.fix(8*2)
    m.fs.FSH.tube_ncol.fix(85)
    m.fs.FSH.nrow_inlet.fix(2)
    m.fs.FSH.delta_elevation.fix(50)
    m.fs.FSH.tube_r_fouling = 0.000176  # (0.001 h-ft^2-F/BTU)
    m.fs.FSH.shell_r_fouling = 0.003131  # (0.03131 - 0.1779 h-ft^2-F/BTU)
    if m.fs.FSH.config.has_radiation is True:
        m.fs.FSH.emissivity_wall.fix(0.7)  # wall emissivity
    # correction factor for overall heat transfer coefficient
    m.fs.FSH.fcorrection_htc.fix(1.0)
    # correction factor for pressure drop calc tube side
    m.fs.FSH.fcorrection_dp_tube.fix(1.0)
    # correction factor for pressure drop calc shell side
    m.fs.FSH.fcorrection_dp_shell.fix(1.0)

#  ----- Reheater Superheater ----------------------
    #   Steam from HP Turbine outlet
    m.fs.RH.side_1_inlet.flow_mol[0].fix(21235.27)         # mol/s
    m.fs.RH.side_1_inlet.enth_mol[0].fix(53942.7569)       # J/mol
    m.fs.RH.side_1_inlet.pressure[0].fix(3677172.33638)    # Pascals

    # FLUE GAS Inlet from Finishing Superheater
    FGrate = 21290.6999*0.85  # mol/s
    # Use FG molar composition to set component flow rates (baseline report)
    m.fs.RH.side_2_inlet.flow_component[0, "H2O"].fix(FGrate*8.69/100)
    m.fs.RH.side_2_inlet.flow_component[0, "CO2"].fix(FGrate*14.49/100)
    m.fs.RH.side_2_inlet.flow_component[0, "N2"].fix(FGrate*74.34/100)
    m.fs.RH.side_2_inlet.flow_component[0, "O2"].fix(FGrate*2.47/100)
    m.fs.RH.side_2_inlet.flow_component[0, "NO"].fix(FGrate*0.0006)
    m.fs.RH.side_2_inlet.flow_component[0, "SO2"].fix(FGrate*0.002)
    m.fs.RH.side_2_inlet.temperature[0].fix(1180.335)
    m.fs.RH.side_2_inlet.pressure[0].fix(100145)

    # Reheater Superheater
    ITM = 0.0254  # inch to meter conversion
    m.fs.RH.tube_di.fix((2.5-2*0.165)*ITM)
    m.fs.RH.tube_thickness.fix(0.11*ITM)
    m.fs.RH.pitch_x.fix(3*ITM)
    # gas path transverse width 54.08 ft / number of columns
    m.fs.RH.pitch_y.fix(54.08/108*12*ITM)
    m.fs.RH.tube_length.fix(53.82*12*ITM)
    m.fs.RH.tube_nrow.fix(18*2)
    m.fs.RH.tube_ncol.fix(82+70)
    m.fs.RH.nrow_inlet.fix(2)
    m.fs.RH.delta_elevation.fix(30)
    m.fs.RH.tube_r_fouling = 0.000176  # (0.001 h-ft^2-F/BTU)
    m.fs.RH.shell_r_fouling = 0.00088  # (0.03131 - 0.1779 h-ft^2-F/BTU)
    if m.fs.RH.config.has_radiation is True:
        m.fs.RH.emissivity_wall.fix(0.7)  # wall emissivity
    # correction factor for overall heat transfer coefficient
    m.fs.RH.fcorrection_htc.fix(1.8)
    # correction factor for pressure drop calc tube side
    m.fs.RH.fcorrection_dp_tube.fix(1.0)
    # correction factor for pressure drop calc shell side
    m.fs.RH.fcorrection_dp_shell.fix(1.0)

    #    Platen Superheater ------------------------------------------
    hpl = iapws95.htpx(798.15, 24790249.01)
    m.fs.PlSH.inlet[:].flow_mol.fix(24194.177)
    m.fs.PlSH.inlet[:].enth_mol.fix(hpl)
    m.fs.PlSH.inlet[:].pressure.fix(24790249.01)
    m.fs.PlSH.heat_duty[:].fix(5.5e7)

    #    Water wall Superheater ------------------------------------------
    hww = iapws95.htpx(588.15, 2.5449e7)
    m.fs.Water_wall.inlet[:].flow_mol.fix(24194.177)
    m.fs.Water_wall.inlet[:].enth_mol.fix(hww)
    m.fs.Water_wall.inlet[:].pressure.fix(24865516.722)
    m.fs.Water_wall.heat_duty[:].fix(7.51e8)  # 8.76e8

    #   splitter flue gas from Finishing SH to Reheater and Primary SH
    m.fs.Spl1.split_fraction[0, 'outlet_1'].fix(0.75)  # 0.85)
    # FLUE GAS Inlet from Primary Superheater
    FGrate = 21290.6999  # mol/s
    # Use FG molar composition to set component flow rates (baseline report)
    m.fs.Spl1.inlet.flow_component[0, "H2O"].fix(FGrate*8.69/100)
    m.fs.Spl1.inlet.flow_component[0, "CO2"].fix(FGrate*14.49/100)
    m.fs.Spl1.inlet.flow_component[0, "N2"].fix(FGrate*74.34/100)
    m.fs.Spl1.inlet.flow_component[0, "O2"].fix(FGrate*2.47/100)
    m.fs.Spl1.inlet.flow_component[0, "NO"].fix(FGrate*0.0006)
    m.fs.Spl1.inlet.flow_component[0, "SO2"].fix(FGrate*0.002)
    m.fs.Spl1.inlet.temperature[0].fix(1180.335)
    m.fs.Spl1.inlet.pressure[0].fix(100145)

    # mixer (econ inlet) fluegas outlet from reheater and primary superheater
    #   Mixer inlets  ['Reheat_out', 'PrSH_out']
    m.fs.mix1.Reheat_out.flow_component[0, "H2O"].fix(FGrate*8.69/100)
    m.fs.mix1.Reheat_out.flow_component[0, "CO2"].fix(FGrate*14.49/100)
    m.fs.mix1.Reheat_out.flow_component[0, "N2"].fix(FGrate*74.34/100)
    m.fs.mix1.Reheat_out.flow_component[0, "O2"].fix(FGrate*2.47/100)
    m.fs.mix1.Reheat_out.flow_component[0, "NO"].fix(FGrate*0.0006)
    m.fs.mix1.Reheat_out.flow_component[0, "SO2"].fix(FGrate*0.002)
    m.fs.mix1.Reheat_out.pressure.fix(100145)
    m.fs.mix1.Reheat_out.temperature.fix(731.5)
    # Fixed SprayWater as small flow and arbitrary conditions
    # (will be connected from FW pump splitter)
    m.fs.mix1.PrSH_out.flow_component[0, "H2O"].fix(FGrate*8.69/100)
    m.fs.mix1.PrSH_out.flow_component[0, "CO2"].fix(FGrate*14.49/100)
    m.fs.mix1.PrSH_out.flow_component[0, "N2"].fix(FGrate*74.34/100)
    m.fs.mix1.PrSH_out.flow_component[0, "O2"].fix(FGrate*2.47/100)
    m.fs.mix1.PrSH_out.flow_component[0, "NO"].fix(FGrate*0.0006)
    m.fs.mix1.PrSH_out.flow_component[0, "SO2"].fix(FGrate*0.002)
    m.fs.mix1.PrSH_out.pressure.fix(100145)
    m.fs.mix1.PrSH_out.temperature.fix(731.15)

    # Attemperator inputs
    hatt = iapws95.htpx(875.15, 24865516.722)
    m.fs.ATMP1.Steam.flow_mol.fix(24194.177)
    m.fs.ATMP1.Steam.enth_mol.fix(hatt)
    m.fs.ATMP1.Steam.pressure.fix(24865516.722)
    # Fixed SprayWater from FW pump splitter (splitter is needded)
    hatt2 = iapws95.htpx(563.15, 2.5449e7)
    m.fs.ATMP1.SprayWater_state[:].flow_mol.fix(0.001)
    m.fs.ATMP1.SprayWater_state[:].pressure.fix(1.22e8)
    m.fs.ATMP1.SprayWater_state[:].enth_mol.fix(hatt2)

# ---------  Initialization ------------------------------------------

    # Initialize Units
    m.fs.ECON.initialize(outlvl=4)
    m.fs.PrSH.initialize(outlvl=4)
    m.fs.FSH.initialize(outlvl=4)
    m.fs.RH.initialize(outlvl=4)
    m.fs.PlSH.initialize(outlvl=4)
    m.fs.Water_wall.initialize(outlvl=4)
    m.fs.Spl1.initialize(outlvl=4)
    m.fs.mix1.initialize(outlvl=4)
    m.fs.ATMP1.initialize(outlvl=4)
    print('initialization done')


def unfix_inlets(m):

    # Use FG molar composition to set component flow rates (baseline report)
    m.fs.ECON.side_2_inlet.flow_component[0, "H2O"].unfix()
    m.fs.ECON.side_2_inlet.flow_component[0, "CO2"].unfix()
    m.fs.ECON.side_2_inlet.flow_component[0, "N2"].unfix()
    m.fs.ECON.side_2_inlet.flow_component[0, "O2"].unfix()
    m.fs.ECON.side_2_inlet.flow_component[0, "NO"].unfix()
    m.fs.ECON.side_2_inlet.flow_component[0, "SO2"].unfix()
    m.fs.ECON.side_2_inlet.temperature[0].unfix()
    m.fs.ECON.side_2_inlet.pressure[0].unfix()

    # PrSH Primary superheater inlets (steam and flue gas) --------------------
    m.fs.PrSH.side_1_inlet.flow_mol.unfix()
    m.fs.PrSH.side_1_inlet.enth_mol[0].unfix()
    m.fs.PrSH.side_1_inlet.pressure[0].unfix()

    # Use FG molar composition to set component flow rates (baseline report)
    m.fs.PrSH.side_2_inlet.flow_component[0, "H2O"].unfix()
    m.fs.PrSH.side_2_inlet.flow_component[0, "CO2"].unfix()
    m.fs.PrSH.side_2_inlet.flow_component[0, "N2"].unfix()
    m.fs.PrSH.side_2_inlet.flow_component[0, "O2"].unfix()
    m.fs.PrSH.side_2_inlet.flow_component[0, "NO"].unfix()
    m.fs.PrSH.side_2_inlet.flow_component[0, "SO2"].unfix()
    m.fs.PrSH.side_2_inlet.temperature[0].unfix()
    m.fs.PrSH.side_2_inlet.pressure[0].unfix()

    # WaterWall  water from economizer  ---------------------------------------
    m.fs.Water_wall.inlet[:].flow_mol.unfix()
    m.fs.Water_wall.inlet[:].enth_mol.unfix()
    m.fs.Water_wall.inlet[:].pressure.unfix()

    # Use FG molar composition to set component flow rates (baseline report)
    m.fs.RH.side_2_inlet.flow_component[0, "H2O"].unfix()
    m.fs.RH.side_2_inlet.flow_component[0, "CO2"].unfix()
    m.fs.RH.side_2_inlet.flow_component[0, "N2"].unfix()
    m.fs.RH.side_2_inlet.flow_component[0, "O2"].unfix()
    m.fs.RH.side_2_inlet.flow_component[0, "NO"].unfix()
    m.fs.RH.side_2_inlet.flow_component[0, "SO2"].unfix()
    m.fs.RH.side_2_inlet.temperature[0].unfix()
    m.fs.RH.side_2_inlet.pressure[0].unfix()

    # Finishing Superheater (steam from Platen SH)-----------------------------
    m.fs.FSH.side_1_inlet.flow_mol.unfix()
    m.fs.FSH.side_1_inlet.enth_mol[0].unfix()
    m.fs.FSH.side_1_inlet.pressure[0].unfix()

    # Platen SH steam inlet conditions from Water Wall-------------------------
    m.fs.PlSH.inlet[:].flow_mol.unfix()
    m.fs.PlSH.inlet[:].enth_mol.unfix()
    m.fs.PlSH.inlet[:].pressure.unfix()

    # unfix Splitter flue gas inlet to RH and PrSH
    m.fs.Spl1.inlet.flow_component[0, "H2O"].unfix()
    m.fs.Spl1.inlet.flow_component[0, "CO2"].unfix()
    m.fs.Spl1.inlet.flow_component[0, "N2"].unfix()
    m.fs.Spl1.inlet.flow_component[0, "O2"].unfix()
    m.fs.Spl1.inlet.flow_component[0, "NO"].unfix()
    m.fs.Spl1.inlet.flow_component[0, "SO2"].unfix()
    m.fs.Spl1.inlet.temperature[0].unfix()
    m.fs.Spl1.inlet.pressure[0].unfix()

    # unfix mixer inlets (now connected)
    m.fs.mix1.Reheat_out.flow_component[0, "H2O"].unfix()
    m.fs.mix1.Reheat_out.flow_component[0, "CO2"].unfix()
    m.fs.mix1.Reheat_out.flow_component[0, "N2"].unfix()
    m.fs.mix1.Reheat_out.flow_component[0, "O2"].unfix()
    m.fs.mix1.Reheat_out.flow_component[0, "NO"].unfix()
    m.fs.mix1.Reheat_out.flow_component[0, "SO2"].unfix()
    m.fs.mix1.Reheat_out.pressure.unfix()
    m.fs.mix1.Reheat_out.temperature.unfix()
    # PrSH output
    m.fs.mix1.PrSH_out.flow_component[0, "H2O"].unfix()
    m.fs.mix1.PrSH_out.flow_component[0, "CO2"].unfix()
    m.fs.mix1.PrSH_out.flow_component[0, "N2"].unfix()
    m.fs.mix1.PrSH_out.flow_component[0, "O2"].unfix()
    m.fs.mix1.PrSH_out.flow_component[0, "NO"].unfix()
    m.fs.mix1.PrSH_out.flow_component[0, "SO2"].unfix()
    m.fs.mix1.PrSH_out.pressure.unfix()
    m.fs.mix1.PrSH_out.temperature.unfix()

    # unfix attemperator inlet from (Finishing SH)
    m.fs.ATMP1.Steam.flow_mol.unfix()
    m.fs.ATMP1.Steam.enth_mol.unfix()
    m.fs.ATMP1.Steam.pressure.unfix()


def pfd_result(outfile, m, df):
    tags = {}
    for i in df.index:
        tags[i + "_F"] = df.loc[i, "Molar Flow (mol/s)"]
        tags[i + "_T"] = df.loc[i, "T (K)"]
        tags[i + "_P"] = df.loc[i, "P (Pa)"]
        tags[i + "_X"] = df.loc[i, "Vapor Fraction"]

    tags['FG_2_RH_Fm'] = value(m.fs.RH.side_2.properties_in[0].flow_mass)
    tags['FG_2_RH_T'] = value(m.fs.RH.side_2.properties_in[0].temperature)
    tags['FG_2_RH_P'] = value(m.fs.RH.side_2.properties_in[0].pressure)

    tags['FG_RH_2_Mix_Fm'] = value(m.fs.RH.side_2.properties_out[0].flow_mass)
    tags['FG_RH_2_Mix_T'] = value(m.fs.RH.side_2.properties_out[0].temperature)
    tags['FG_RH_2_Mix_P'] = value(m.fs.RH.side_2.properties_out[0].pressure)

    tags['FG_2_FSH_Fm'] = value(m.fs.FSH.side_2.properties_in[0].flow_mass)
    tags['FG_2_FSH_T'] = value(m.fs.FSH.side_2.properties_in[0].temperature)
    tags['FG_2_FSH_P'] = value(m.fs.FSH.side_2.properties_in[0].pressure)

    tags['FG_2_PrSH_Fm'] = value(m.fs.PrSH.side_2.properties_in[0].flow_mass)
    tags['FG_2_PrSH_T'] = value(m.fs.PrSH.side_2.properties_in[0].temperature)
    tags['FG_2_PrSH_P'] = value(m.fs.PrSH.side_2.properties_in[0].pressure)

    tags['FG_PrSH_2_Mix_Fm'] = \
        value(m.fs.PrSH.side_2.properties_out[0].flow_mass)
    tags['FG_PrSH_2_Mix_T'] = \
        value(m.fs.PrSH.side_2.properties_out[0].temperature)
    tags['FG_PrSH_2_Mix_P'] = \
        value(m.fs.PrSH.side_2.properties_out[0].pressure)

    tags['FG_2_ECON_Fm'] = value(m.fs.ECON.side_2.properties_in[0].flow_mass)
    tags['FG_2_ECON_T'] = value(m.fs.ECON.side_2.properties_in[0].temperature)
    tags['FG_2_ECON_P'] = value(m.fs.ECON.side_2.properties_in[0].pressure)

    tags['FG_2_AIRPH_Fm'] = value(m.fs.ECON.side_2.properties_out[0].flow_mass)
    tags['FG_2_AIRPH_T'] = \
        value(m.fs.ECON.side_2.properties_out[0].temperature)
    tags['FG_2_AIRPH_P'] = value(m.fs.ECON.side_2.properties_out[0].pressure)

    tags['FG_2_STACK_Fm'] = value(m.fs.ECON.side_2.properties_out[0].flow_mass)
    tags['FG_2_STACK_T'] = \
        value(m.fs.ECON.side_2.properties_out[0].temperature)
    tags['FG_2_STACK_P'] = value(m.fs.ECON.side_2.properties_out[0].pressure)

    original_svg_file = os.path.join(this_file_dir(), "Boiler_scpc_PFD.svg")
    with open(original_svg_file, "r") as f:
        s = svg_tag(tags, f, outfile=outfile)


def _stream_dict(m):
    """Adds _streams to m, which contains a dictionary of streams for display

    Args:
        m (ConcreteModel): A Pyomo model from create_model()

    Returns:
        None
    """

    m._streams = OrderedDict(
        [
            ('MS', m.fs.ATMP1.mixed_state),
            ('ATMP_In', m.fs.FSH.side_1.properties_out),
            ('FSH_In', m.fs.FSH.side_1.properties_in),
            ('PrSH_IN', m.fs.PrSH.side_1.properties_in),
            ('RHT_COLD', m.fs.RH.side_1.properties_in),
            ('RHT_HOT', m.fs.RH.side_1.properties_out),
            ('PlatenSH_IN', m.fs.PlSH.control_volume.properties_in),
            ('BFW', m.fs.ECON.side_1.properties_in),
            ('ECON_OUT', m.fs.ECON.side_1.properties_out),
        ]
    )


def print_results(m):
    print()
    print("Results")
    print()

    print('viscosity gas side = ',
          m.fs.PrSH.side_2.properties_in[0].visc_d_mix.value)
    print('conductivity gas side = ',
          m.fs.PrSH.side_2.properties_in[0].therm_cond_mix.value)
    print('velocity_tube = ',
          m.fs.PrSH.v_tube[0].value)
    print('velocity_shell = ', m.fs.PrSH.v_shell[0].value)
    print('Re_tube = ', m.fs.PrSH.N_Re_tube[0].value)
    print('Re_shell = ', m.fs.PrSH.N_Re_shell[0].value)
    print('hconv_tube = ', m.fs.PrSH.hconv_tube[0].value)
    print('hconv_shell_rad = ', m.fs.PrSH.hconv_shell_rad[0].value)
    print('hconv_shell_conv = ', m.fs.PrSH.hconv_shell_conv[0].value)
    print('hconv_shell_total = ', m.fs.PrSH.hconv_shell_total[0].value)
    print('driving force = ', m.fs.PrSH.temperature_driving_force[0].value)
    print('dT_inlet = ', m.fs.PrSH.deltaT_1[0].value)
    print('dT_outlet = ', m.fs.PrSH.deltaT_2[0].value)
    print('deltaP tube = ', m.fs.PrSH.deltaP_tube[0].value)
    print('deltaP shell = ', m.fs.PrSH.deltaP_shell[0].value)
    print('mbl = ', value(m.fs.PrSH.mbl))

    if m.fs.PrSH.config.has_radiation is True:
        print('gas emissivity = ', m.fs.PrSH.gas_emissivity[0].value)
        print('gas emissivity div2 = ', m.fs.PrSH.gas_emissivity_div2[0].value)
        print('gas emissivity mul2 = ', m.fs.PrSH.gas_emissivity_mul2[0].value)
        print('gas gray fraction = ', m.fs.PrSH.gas_gray_fraction[0].value)
    print('liquid density in = ',
          value(m.fs.PrSH.side_1.properties_in[0].dens_mass_phase["Liq"]))

    print('liquid density out = ',
          value(m.fs.PrSH.side_1.properties_out[0].dens_mass_phase["Liq"]))
    print('heat transfer area = ',
          value(m.fs.PrSH.area_heat_transfer))
    print('overal heat transfer = ',
          value(m.fs.PrSH.overall_heat_transfer_coefficient[0]))

    print('\n\n ------------- Economizer   ---------')
    print('liquid temp in = ',
          value(m.fs.ECON.side_1.properties_in[0].temperature))
    print('liquid temp out = ',
          value(m.fs.ECON.side_1.properties_out[0].temperature))
    print('gas temp in = ',
          value(m.fs.ECON.side_2.properties_in[0].temperature))
    print('gas temp out = ',
          value(m.fs.ECON.side_2.properties_out[0].temperature))

    print('\n\n ------------- water wall  ---------')
    print('liquid temp in = ',
          value(m.fs.Water_wall.control_volume.properties_in[0].temperature))
    print('steam temp out = ',
          value(m.fs.Water_wall.control_volume.properties_out[0].temperature))

    print('\n\n ------------- Primary Superheater  ---------')
    print('steam temp in = ',
          value(m.fs.PrSH.side_1.properties_in[0].temperature))
    print('steam temp out = ',
          value(m.fs.PrSH.side_1.properties_out[0].temperature))
    print('gas temp in = ',
          value(m.fs.PrSH.side_2.properties_in[0].temperature))
    print('gas temp out = ',
          value(m.fs.PrSH.side_2.properties_out[0].temperature))

    print('\n\n ------------- Platen SH  ---------')
    print('steam temp in = ',
          value(m.fs.PlSH.control_volume.properties_in[0].temperature))
    print('steam temp out = ',
          value(m.fs.PlSH.control_volume.properties_out[0].temperature))

    print('\n\n ------------- Finishing Superheater  ---------')
    print('steam temp in = ',
          value(m.fs.FSH.side_1.properties_in[0].temperature))
    print('steam temp out (to attmp) = ',
          value(m.fs.FSH.side_1.properties_out[0].temperature))
    print('gas temp in = ',
          value(m.fs.FSH.side_2.properties_in[0].temperature))
    print('gas temp out = ',
          value(m.fs.FSH.side_2.properties_out[0].temperature))

    print('\n\n ------------- Attemperator  ---------')
    print('steam temp in = ',
          value(m.fs.ATMP1.Steam.enth_mol[0]),
          value(m.fs.FSH.side_1.properties_out[0].temperature))
    print('steam temp out (to HP turbine) = ',
          value(m.fs.ATMP1.outlet.enth_mol[0]),
          value(m.fs.ATMP1.mixed_state[0].temperature))
    print()

    print('\n\n ------------- Reheater  ---------')
    print('liquid temp in = ',
          value(m.fs.RH.side_1.properties_in[0].temperature))
    print('liquid temp out = ',
          value(m.fs.RH.side_1.properties_out[0].temperature))
    print('gas temp in = ',
          value(m.fs.RH.side_2.properties_in[0].temperature))
    print('gas temp out = ',
          value(m.fs.RH.side_2.properties_out[0].temperature))


if __name__ == "__main__":
    m, solver = main()

    solver.options = {"tol": 1e-6,
                      "linear_solver": "ma27",
                      "max_iter": 100,
                      "halt_on_ampl_error": 'yes'}
    # initialize each unit at the time
    initialize(m)
    print('flowsheet degrees of freedom = ' + str(degrees_of_freedom(m)))
    # unfix inlets to build arcs at the flowsheet level
    unfix_inlets(m)
    # users can change the following fixed values to obtain certain performance
    # m.fs.ATMP1.outlet.enth_mol[0].fix(62710.01)
    # m.fs.Water_wall.heat_duty.unfix()
    # m.fs.RH.side_1_outlet.enth_mol.fix(66043.35)
    # m.fs.PlSH.heat_duty.unfix()
    #    m.fs.ATMP1.SprayWater.flow_mol[0].unfix()
    print('flowsheet degrees of freedom = ' + str(degrees_of_freedom(m)))
    results = solver.solve(m, tee=True, symbolic_solver_labels=True)
    print_results(m)

    #    print results in the PFD file (activate if needed)
    #    _stream_dict(m)
    #    df = create_stream_table_dataframe(streams=m._streams, orient="index")
    #    pfd_result("Boiler_Results.svg", m, df)
