class ThermodynamicalProcess(ThermodynamicalCycle): ############# Inputs ############# fluidSource = F.SubModelGroup(TC.FluidSource, 'FG', label='Initial State') fluidSink = F.SubModelGroup(TC.FluidSink, 'FG', label='Final State') inputs = F.SuperGroup([fluidSource, fluidSink]) # Model View inputView = F.ModelView(ioType="input", superGroups=[inputs], autoFetch=True) ############# Results ############# w = F.Quantity('SpecificEnergy', default=(0, 'kJ/kg'), label='specific work') qIn = F.Quantity('SpecificEnergy', default=(0, 'kJ/kg'), label='specific heat in') delta_h = F.Quantity('SpecificEnthalpy', label='enthalpy change (fluid)') WDot = F.Quantity('Power', default=(0, 'kW'), label='power') QDotIn = F.Quantity('HeatFlowRate', default=(0, 'kW'), label='heat flow rate in') deltaHDot = F.Quantity('Power', label='enthalpy change (fluid)') # Specific energy quantities specificEnergyResults = F.FieldGroup( [w, delta_h, qIn], label="Heat/work (specific quantities)") # Energy flow quantities energyFlowResults = F.FieldGroup([WDot, deltaHDot, QDotIn], label="Heat/work flows") energyBalanceResults = F.SuperGroup( [specificEnergyResults, energyFlowResults], label="Energy balance") # Model View resultView = F.ModelView( ioType="output", superGroups=['resultDiagrams', 'resultStates', energyBalanceResults]) ############# Page structure ######## modelBlocks = [inputView, resultView] ############# Methods ############### def postProcess(self, component): super(ThermodynamicalProcess, self).postProcess(300) component.postProcess() self.w = component.w self.qIn = component.qIn self.delta_h = component.delta_h self.WDot = component.WDot self.QDotIn = component.QDotIn self.deltaHDot = component.deltaHDot
class RegenerativeRankineCycle(RankineCycle): label = "Regenerative Rankine cycle" figure = F.ModelFigure(src="ThermoFluids/img/ModuleImages/RankineCycle_Recuperator.svg", height = 300) description = F.ModelDescription("Rankine cycle with recuperator, \ using the temperature of the hot steam before the condenser to pre-heat the fluid before entering the boiler", show = True) #================ Inputs ================# #---------------- Fields ----------------# # FieldGroup recuperator = F.SubModelGroup(TC.HeatExchangerTwoStreams, 'FG', label = 'Recuperator') inputs = F.SuperGroup(['workingFluidGroup', 'pump', recuperator, 'boiler', 'turbine', 'condenser'], label = 'Cycle defininition') #--------------- Model view ---------------# #================ Results ================# #---------------- Energy flows -----------# recuperatorHeat = F.Quantity('HeatFlowRate', default = (0, 'kW'), label = 'recuperator heat rate') flowFieldGroup = F.FieldGroup(['pumpPower', recuperatorHeat, 'boilerHeat', 'turbinePower', 'condenserHeat'], label = 'Energy flows') #---------------- Scheme -----------------# scheme_Rankine_recup = F.Image(default="static/ThermoFluids/img/ModuleImages/RankineCycle_Recuperator.png") SchemeVG = F.ViewGroup([scheme_Rankine_recup], label="Process Scheme") SchemeSG = F.SuperGroup([SchemeVG], label="Scheme") resultView = F.ModelView(ioType = "output", superGroups = ['resultDiagrams', SchemeSG, 'resultStates', 'resultEnergy', 'solverStats']) #================ Methods ================# def compute(self): self.initCompute(self.fluidName) # Connect components self.connectPorts(self.condenser.outlet, self.pump.inlet) self.connectPorts(self.pump.outlet, self.recuperator.inlet1) self.connectPorts(self.recuperator.outlet1, self.boiler.inlet) self.connectPorts(self.boiler.outlet, self.turbine.inlet) self.connectPorts(self.turbine.outlet, self.recuperator.inlet2) self.connectPorts(self.recuperator.outlet2, self.condenser.inlet) # Initial guess for fl in self.flows: fl.mDot = self.mDot self.condenser.outlet.state.update_pq(self.pLow, 0) self.boiler.outlet.state.update_pq(self.pHigh, 1) self.turbine.compute(self.pLow) # Cycle iterations self.solver.run() # Results self.postProcess() def computeCycle(self): self.pump.compute(self.pHigh) self.recuperator.compute() self.recuperator.computeStream1() self.boiler.compute() self.turbine.compute(self.pLow) self.recuperator.computeStream2() self.condenser.compute() def postProcess(self): super(RegenerativeRankineCycle, self).postProcess() self.recuperatorHeat = self.recuperator.QDot def SteamPlant(self): RankineCycle.SteamPlant(self) self.pLow = (2, 'bar') self.boiler.TOutlet = (600, 'degC')
class HeatExchangerTwoStreams(ThermodynamicalProcessTwoStreams): label = "Heat Exchanger (two streams)" description = F.ModelDescription( "Parameteric model for heat exchange between two streams", show=True) figure = F.ModelFigure( src="ThermoFluids/img/ModuleImages/HeatExchanger.svg") heatExchanger = F.SubModelGroup(TC.HeatExchangerTwoStreams, 'FG', label='Heat exchanger') inputs = F.SuperGroup(['fluidSource1', 'fluidSource2', heatExchanger]) def __init__(self): self.fluidSource2.T2 = (350, 'K') def compute(self): self.cycleDiagram.enable = False self.initCompute(self.fluidSource1.fluidName) # Connect components self.connectPorts(self.fluidSource1.outlet, self.heatExchanger.inlet1) self.connectPorts(self.heatExchanger.outlet1, self.fluidSink1.inlet) self.connectPorts(self.fluidSource2.outlet, self.heatExchanger.inlet2, fluid=self.fluidSource2.fluidName) self.connectPorts(self.heatExchanger.outlet2, self.fluidSink2.inlet, fluid=self.fluidSource2.fluidName) self.fluidSource1.compute() self.fluidSource2.compute() self.heatExchanger.compute() self.heatExchanger.computeStream1() self.heatExchanger.computeStream2() self.postProcess(self.heatExchanger)
class Expansion(ThermodynamicalProcess): label = "Expansion" description = F.ModelDescription( "Parameteric model for expansion process: isentropic and isenthalpic", show=True) figure = F.ModelFigure(src="ThermoFluids/img/ModuleImages/Expansion.svg") turbine = F.SubModelGroup(TC.Turbine, 'FG', label='Turbine', show="self.processType == 'S'") throttleValve = F.SubModelGroup(TC.ThrottleValve, 'FG', show="false") processType = F.Choices(options=OrderedDict(( ('S', 'isentropic'), ('H', 'isenthalpic'), )), default='S', label="process type") processTypeFG = F.FieldGroup([processType], label="Process type") inputs = F.SuperGroup( ['fluidSource', 'fluidSink', processTypeFG, turbine, throttleValve]) def __init__(self): self.fluidSource.p1 = (10, 'bar') self.fluidSource.p2 = (10, 'bar') self.fluidSink.p = (1, 'bar') def compute(self): if (self.processType == 'S'): component = self.turbine elif (self.processType == 'H'): component = self.throttleValve self.initCompute(self.fluidSource.fluidName) # Connect components self.connectPorts(self.fluidSource.outlet, component.inlet) self.connectPorts(component.outlet, self.fluidSink.inlet) self.fluidSource.compute() component.compute(self.fluidSink.p) self.postProcess(component)
class ThermodynamicalProcessTwoStreams(ThermodynamicalCycle): ############# Inputs ############# fluidSource1 = F.SubModelGroup(TC.FluidSource, 'FG', label='Inlet 1') fluidSource2 = F.SubModelGroup(TC.FluidSource, 'FG', label='Inlet 2') fluidSink1 = F.SubModelGroup(TC.FluidSink, 'FG', label='Outlet 1') fluidSink2 = F.SubModelGroup(TC.FluidSink, 'FG', label='Outlet 2') inputs = F.SuperGroup([fluidSource1, fluidSource2, fluidSink1, fluidSink2]) # Model View inputView = F.ModelView(ioType="input", superGroups=[inputs], autoFetch=True) ############# Results ############# QDot = F.Quantity('HeatFlowRate', default=(0, 'kW'), label='heat flow rate in') NTU = F.Quantity(label='NTU') Cr = F.Quantity(label='capacity ratio') energyResults = F.FieldGroup([QDot, NTU, Cr], label="Energy") energyBalanceResults = F.SuperGroup([energyResults], label="Energy balance") # Model View resultView = F.ModelView( ioType="output", superGroups=['resultStates', energyBalanceResults]) ############# Page structure ######## modelBlocks = [inputView, resultView] ############# Methods ############### def postProcess(self, component): super(ThermodynamicalProcessTwoStreams, self).postProcess(300) self.QDot = component.QDot self.NTU = component.NTU self.Cr = component.Cr
class Compression(ThermodynamicalProcess): label = "Compression" description = F.ModelDescription( "Parameteric model for compression process: isentropic and isothermal", show=True) figure = F.ModelFigure(src="ThermoFluids/img/ModuleImages/Compression.svg") compressor = F.SubModelGroup(TC.Compressor, 'FG', label='Compressor') inputs = F.SuperGroup(['fluidSource', 'fluidSink', compressor]) def compute(self): self.initCompute(self.fluidSource.fluidName) # Connect components self.connectPorts(self.fluidSource.outlet, self.compressor.inlet) self.connectPorts(self.compressor.outlet, self.fluidSink.inlet) self.fluidSource.compute() self.compressor.compute(self.fluidSink.p) self.postProcess(self.compressor)
class Cooling(ThermodynamicalProcess): label = "Cooling" description = F.ModelDescription("Cooling process at constant pressure", show=True) figure = F.ModelFigure(src="ThermoFluids/img/ModuleImages/Cooling.svg") condenser = F.SubModelGroup(TC.Condenser, 'FG', label='Condenser') inputs = F.SuperGroup(['fluidSource', condenser]) ############# Methods ############### def compute(self): self.initCompute(self.fluidSource.fluidName) # Connect components self.connectPorts(self.fluidSource.outlet, self.condenser.inlet) self.connectPorts(self.condenser.outlet, self.fluidSink.inlet) self.fluidSource.compute() self.condenser.compute() self.postProcess(self.condenser)
class ABC(NumericalModel): showOnHome = False label = 'ABC' compressor = F.SubModelGroup(TC.Compressor, ['etaS', 'fQ', 'modelType'], label='Compressor') #fields = ) inputs = F.SuperGroup([compressor]) inputView = F.ModelView(ioType="input", superGroups=[inputs], autoFetch=True) resultView = F.ModelView(ioType="output", superGroups=[]) testJs = JsBlock(srcType="file", src="TestPage.js") testHtml = HtmlBlock(srcType="file", src="TestPage.html") modelBlocks = [inputView, resultView, testJs, testHtml] def __init__(self): self.compressor.modelType = 'S' self.compressor.declared_fields['modelType'].show = 'false' def compute(self): pass
class ADM1H2CH4Bioreactors(NumericalModel): label = "(H2,CH4) Bioreactors" description = F.ModelDescription( "A model of the process of hydrogen and methane production by anaerobic digestion of organic wastes in a cascade of two continuously stirred bioreactors.", show=True) figure = F.ModelFigure( src="BioReactors/img/ModuleImages/ADM1H2CH4Bioreactors.png", show=True) async = True progressOptions = {'suffix': 'day', 'fractionOutput': True} #1. ############ Inputs ############### #1.1 Fields - Input values parametersRH2 = F.SubModelGroup(H2Bioreactor, 'parametersSG', label='Parameters (R-H2)') concentrationsRH2 = F.SubModelGroup(H2Bioreactor, 'concentrationsSG', label='Concentrations (R-H2)') parametersRCH4 = F.SubModelGroup(CH4Bioreactor, 'parametersSG', label='Parameters (R-CH4)') concentrationsRCH4 = F.SubModelGroup(CH4Bioreactor, 'concentrationsSG', label='Concentrations (R-CH4)') #1.3 Fields - Settings solverSettings = F.SubModelGroup(SolverSettings, 'FG', label='H2 & CH4 - Bioreactors') settingsSG = F.SuperGroup([solverSettings], label='Solver settings') #1.4 Model view exampleAction = A.ServerAction( "loadEg", label="Examples", options=( #('exampleDef', 'Reset to default values'), ('exampleADM1', 'Reset to ADM1 values'), )) inputView = F.ModelView( ioType="input", superGroups=[ parametersRH2, concentrationsRH2, parametersRCH4, concentrationsRCH4, settingsSG ], autoFetch=True, actionBar=A.ActionBar([exampleAction]), ) #2. ############ Results ############### storage = F.HdfStorage(hdfFile=DM.dataStorageFilePath, hdfGroup=DM.dataStorageDatasetPath) varTuples = ( ('time', F.Quantity('Bio_Time', default=(1, 'day'))), #0 ('S_su_RH2', F.Quantity('Bio_CODConcentration', default=(1, 'gCOD/L'))), #1 ('S_aa_RH2', F.Quantity('Bio_CODConcentration', default=(1, 'gCOD/L'))), #2 ('S_fa_RH2', F.Quantity('Bio_CODConcentration', default=(1, 'gCOD/L'))), #3 ('S_ac_RH2', F.Quantity('Bio_CODConcentration', default=(1, 'gCOD/L'))), #4 ('X_c_RH2', F.Quantity('Bio_CODConcentration', default=(1, 'gCOD/L'))), #5 ('X_ch_RH2', F.Quantity('Bio_CODConcentration', default=(1, 'gCOD/L'))), #6 ('X_pr_RH2', F.Quantity('Bio_CODConcentration', default=(1, 'gCOD/L'))), #7 ('X_li_RH2', F.Quantity('Bio_CODConcentration', default=(1, 'gCOD/L'))), #8 ('X_suaa_RH2', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))), #9 ('X_fa_RH2', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))), #10 ('intQ_h2_RH2', F.Quantity('Bio_CODConcentration', default=(1, 'gCOD/L'))), #11 ('S_ac_RCH4', F.Quantity('Bio_CODConcentration', default=(1, 'gCOD/L'))), #12 ('X_ac_RCH4', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))), #13 ('intQ_ch4_RCH4', F.Quantity('Bio_CODConcentration', default=(1, 'gCOD/L'))), #14 ('Q_h2_RH2', F.Quantity('Bio_CODConcentrationFlowRate', default=(1, 'gCOD/L/day'))), #15 ('Q_ch4_RCH4', F.Quantity('Bio_CODConcentrationFlowRate', default=(1, 'gCOD/L/day'))), #16 ('D_RH2', F.Quantity('Bio_TimeRate', default=(1, '1/day'))), #17 ('D_RCH4', F.Quantity('Bio_TimeRate', default=(1, '1/day'))), #18 ) plot1RH2 = F.PlotView( varTuples, label='R-H2 (S<sub>su</sub>, S<sub>aa</sub>, S<sub>fa</sub>)', options={'ylabel': None}, visibleColumns=[0, 1, 2, 3], useHdfStorage=True, storage='storage', ) plot2RH2 = F.PlotView( varTuples, label='R-H2 (X<sub>ch</sub>, X<sub>pr</sub>, X<sub>li</sub>)', options={'ylabel': None}, visibleColumns=[0, 6, 7, 8], useHdfStorage=True, storage='storage', ) plot3RH2 = F.PlotView( varTuples, label='R-H2 (X<sub>su-aa</sub>,X<sub>fa</sub>)', options={'ylabel': None}, visibleColumns=[0, 9, 10], useHdfStorage=True, storage='storage', ) plot4RH2 = F.PlotView( varTuples, label='R-H2 (H<sub>2</sub>)', options={'ylabel': None}, visibleColumns=[0, 11, 15], useHdfStorage=True, storage='storage', ) plot1RCH4 = F.PlotView( varTuples, label='R-CH4 (S<sub>ac</sub>, X<sub>ac</sub>)', options={'ylabel': None}, visibleColumns=[0, 12, 13], useHdfStorage=True, storage='storage', ) plot2RCH4 = F.PlotView( varTuples, label='R-CH4 (CH<sub>4</sub>)', options={'ylabel': None}, visibleColumns=[0, 14, 16], useHdfStorage=True, storage='storage', ) plotD = F.PlotView( varTuples, label='(D<sub>RH2</sub>, D<sub>RCH4</sub>)', options={'ylabel': None}, visibleColumns=[0, 17, 18], useHdfStorage=True, storage='storage', ) table = F.TableView( varTuples, label='Table', options={ 'title': 'Bioreactors', 'formats': ['0.000'] }, useHdfStorage=True, storage='storage', ) storageVG = F.ViewGroup([storage], show="false") resultsVG = F.ViewGroup([ plot1RH2, plot2RH2, plot3RH2, plot4RH2, plot1RCH4, plot2RCH4, plotD, table ]) resultsSG = F.SuperGroup([resultsVG, storageVG], label='Results') #2.1 Model view resultView = F.ModelView(ioType="output", superGroups=[resultsSG]) ############# Page structure ######## modelBlocks = [inputView, resultView] ############# Methods ############### def __init__(self): self.exampleADM1() def exampleDef(self): ############# H2 Bioreactor ############### #Stoichiometric parameter values self.parametersRH2.f_ch_xc = 0.5 #- self.parametersRH2.f_pr_xc = 0.15 #- self.parametersRH2.f_li_xc = 0.15 #- self.parametersRH2.f_su_li = 0.05 #- self.parametersRH2.f_fa_li = 0.9 #- self.parametersRH2.f_ac_su = 0.41 #- self.parametersRH2.f_ac_aa = 0.4 #- self.parametersRH2.Y_suaa = 0.1 #- self.parametersRH2.Y_fa = 0.06 #- #Biochemical parameter values self.parametersRH2.k_dis = (0.5, '1/day') self.parametersRH2.k_hyd_ch = (10.0, '1/day') self.parametersRH2.k_hyd_pr = (10.0, '1/day') self.parametersRH2.k_hyd_li = (10.0, '1/day') self.parametersRH2.k_m_suaa = (30.0, '1/day') self.parametersRH2.K_S_suaa = (0.5, 'g/L') self.parametersRH2.k_m_fa = (6.0, '1/day') self.parametersRH2.K_S_fa = (0.4, 'g/L') # Physiochemical parameter values self.parametersRH2.Y_h2_su = 1.0 #- self.parametersRH2.Y_h2_aa = 1.0 #- self.parametersRH2.Y_h2_fa = 1.0 #- # Volumetric flow rate values self.parametersRH2.D_liq_arr[0] = (10.0, 0.0) #(day, 1/day) self.parametersRH2.D_liq_arr[1] = (10.0, 0.1) #(day, 1/day) self.parametersRH2.D_liq_arr[2] = (10.0, 0.05) #(day, 1/day) self.parametersRH2.D_liq_arr[3] = (10.0, 0.15) #(day, 1/day) # Input concentrations self.concentrationsRH2.S_su_in = (0.0, 'gCOD/L') self.concentrationsRH2.S_aa_in = (0.0, 'gCOD/L') self.concentrationsRH2.S_fa_in = (0.0, 'gCOD/L') self.concentrationsRH2.S_ac_in = (0.0, 'gCOD/L') self.concentrationsRH2.X_c_in = (2.0, 'gCOD/L') self.concentrationsRH2.X_ch_in = (0.0, 'gCOD/L') self.concentrationsRH2.X_pr_in = (0.0, 'gCOD/L') self.concentrationsRH2.X_li_in = (0.0, 'gCOD/L') self.concentrationsRH2.X_suaa_in = (0.0, 'g/L') self.concentrationsRH2.X_fa_in = (0.0, 'g/L') # Initial values of state variables self.concentrationsRH2.S_su_0 = (0.012, 'gCOD/L') self.concentrationsRH2.S_aa_0 = (0.005, 'gCOD/L') self.concentrationsRH2.S_fa_0 = (0.099, 'gCOD/L') self.concentrationsRH2.S_ac_0 = (0.20, 'gCOD/L') self.concentrationsRH2.X_c_0 = (30.0, 'gCOD/L') self.concentrationsRH2.X_ch_0 = (0.028, 'gCOD/L') self.concentrationsRH2.X_pr_0 = (0.10, 'gCOD/L') self.concentrationsRH2.X_li_0 = (0.03, 'gCOD/L') self.concentrationsRH2.X_suaa_0 = (0.42, 'g/L') self.concentrationsRH2.X_fa_0 = (0.24, 'g/L') ############# CH4 Bioreactor ############### #Stoichiometric parameter values self.parametersRCH4.Y_ac = 0.5 #- #Biochemical parameter values self.parametersRCH4.k_m_ac = (8.0, '1/day') self.parametersRCH4.K_S_ac = (0.15, 'g/L') # Physiochemical parameter values self.parametersRCH4.Y_ch4_ac = 1.0 #- # Physical parameter values self.parametersRCH4.V_liq_RCH4_del_V_liq_RH2 = (50.0, '-') #L/L # Input concentrations self.concentrationsRCH4.X_ac_in = (0.0, 'g/L') # Initial values of state variables self.concentrationsRCH4.S_ac_0 = (0.0, 'gCOD/L') self.concentrationsRCH4.X_ac_0 = (2.0, 'g/L') ############# Solver settings ############### self.solverSettings.tFinal = (50.0, 'day') self.solverSettings.tPrint = (0.1, 'day') self.solverSettings.absTol = (1e-9, 'day') self.solverSettings.relTol = (1e-7, 'day') def exampleADM1(self): ############# H2 Bioreactor ############### #Stoichiometric parameter values self.parametersRH2.f_ch_xc = 0.2 #- self.parametersRH2.f_pr_xc = 0.2 #- self.parametersRH2.f_li_xc = 0.3 #- self.parametersRH2.f_su_li = 0.05 #- self.parametersRH2.f_fa_li = 0.95 #- self.parametersRH2.f_ac_su = 0.41 #- self.parametersRH2.f_ac_aa = 0.4 #- self.parametersRH2.Y_suaa = 0.1 #- self.parametersRH2.Y_fa = 0.06 #- #Biochemical parameter values self.parametersRH2.k_dis = (0.5, '1/day') self.parametersRH2.k_hyd_ch = (10.0, '1/day') self.parametersRH2.k_hyd_pr = (10.0, '1/day') self.parametersRH2.k_hyd_li = (10.0, '1/day') self.parametersRH2.k_m_suaa = (30.0, '1/day') self.parametersRH2.K_S_suaa = (0.5, 'g/L') self.parametersRH2.k_m_fa = (6.0, '1/day') self.parametersRH2.K_S_fa = (0.4, 'g/L') # Physiochemical parameter values self.parametersRH2.Y_h2_su = 1.0 #- self.parametersRH2.Y_h2_aa = 1.0 #- self.parametersRH2.Y_h2_fa = 1.0 #- # Volumetric flow rate values self.parametersRH2.D_liq_arr[0] = (10.0, 0.0) #(day, 1/day) self.parametersRH2.D_liq_arr[1] = (10.0, 0.1) #(day, 1/day) self.parametersRH2.D_liq_arr[2] = (10.0, 0.05) #(day, 1/day) self.parametersRH2.D_liq_arr[3] = (10.0, 0.15) #(day, 1/day) # Input concentrations self.concentrationsRH2.S_su_in = (0.01, 'gCOD/L') self.concentrationsRH2.S_aa_in = (0.001, 'gCOD/L') self.concentrationsRH2.S_fa_in = (0.001, 'gCOD/L') self.concentrationsRH2.S_ac_in = (0.001, 'gCOD/L') self.concentrationsRH2.X_c_in = (2.0, 'gCOD/L') self.concentrationsRH2.X_ch_in = (5.0, 'gCOD/L') self.concentrationsRH2.X_pr_in = (20.0, 'gCOD/L') self.concentrationsRH2.X_li_in = (5.0, 'gCOD/L') self.concentrationsRH2.X_suaa_in = (0.0, 'g/L') self.concentrationsRH2.X_fa_in = (0.01, 'g/L') # Initial values of state variables self.concentrationsRH2.S_su_0 = (0.012, 'gCOD/L') self.concentrationsRH2.S_aa_0 = (0.005, 'gCOD/L') self.concentrationsRH2.S_fa_0 = (0.099, 'gCOD/L') self.concentrationsRH2.S_ac_0 = (0.20, 'gCOD/L') self.concentrationsRH2.X_c_0 = (0.31, 'gCOD/L') self.concentrationsRH2.X_ch_0 = (0.028, 'gCOD/L') self.concentrationsRH2.X_pr_0 = (0.10, 'gCOD/L') self.concentrationsRH2.X_li_0 = (0.03, 'gCOD/L') self.concentrationsRH2.X_suaa_0 = (0.42, 'g/L') self.concentrationsRH2.X_fa_0 = (0.24, 'g/L') ############# CH4 Bioreactor ############### #Stoichiometric parameter values self.parametersRCH4.Y_ac = 0.05 #- #Biochemical parameter values self.parametersRCH4.k_m_ac = (8.0, '1/day') self.parametersRCH4.K_S_ac = (0.15, 'g/L') # Physiochemical parameter values self.parametersRCH4.Y_ch4_ac = 1.0 #- # Physical parameter values self.parametersRCH4.V_liq_RCH4_del_V_liq_RH2 = (50.0, '-') #L/L # Input concentrations self.concentrationsRCH4.X_ac_in = (0.0, 'g/L') # Initial values of state variables self.concentrationsRCH4.S_ac_0 = (0.0, 'gCOD/L') self.concentrationsRCH4.X_ac_0 = (0.76, 'g/L') ############# Solver settings ############### self.solverSettings.tFinal = (50.0, 'day') self.solverSettings.tPrint = (0.1, 'day') self.solverSettings.absTol = (1e-12, 'day') self.solverSettings.relTol = (1e-10, 'day') def computeAsync(self): # Simulate RH2 and RCH4 Bioreactors bioreactor = DM.ADM1H2CH4Bioreactors(self, self.parametersRH2, self.concentrationsRH2, self.parametersRCH4, self.concentrationsRCH4) bioreactor.prepareSimulation(self.solverSettings) bioreactor.run(self.solverSettings) # Show results self.storage = bioreactor.resultStorage.simulationName
class VaporCompressionCycleWithRecuperator(VaporCompressionCycle): label = "Vapor compression cycle (recuperator)" figure = F.ModelFigure( src= "ThermoFluids/img/ModuleImages/VaporCompressionCycle_Recuperator.svg") description = F.ModelDescription( "Vapor compression cycle with a recuperator. The stream \ before the throttle valve is precooled, using the cold stream at the evaporator outlet. \ This increases the compressor cooling/heating capacity of the cycle and improves slightly the COP", show=True) #================ Inputs ================# #---------------- Fields ----------------# recuperator = F.SubModelGroup(TC.HeatExchangerTwoStreams, 'FG', label='Recuperator') inputs = F.SuperGroup([ 'workingFluidGroup', 'compressor', recuperator, 'condenser', 'evaporator' ], label='Cycle definition') #--------------- Model view ---------------# #================ Results ================# #---------------- Energy flows -----------# recuperatorHeat = F.Quantity('HeatFlowRate', default=(0, 'kW'), label='recuperator heat rate') flowFieldGroup = F.FieldGroup([ 'compressorPower', recuperatorHeat, 'condenserHeat', 'evaporatorHeat' ], label='Energy flows') #---------------- Scheme -----------------# scheme_VaporCompression_recup = F.Image( default= "static/ThermoFluids/img/ModuleImages/VaporCompressionCycle_Recuperator.png" ) SchemeVG = F.ViewGroup([scheme_VaporCompression_recup], label="Process Scheme") SchemeSG = F.SuperGroup([SchemeVG], label="Scheme") resultView = F.ModelView(ioType="output", superGroups=[ 'resultDiagrams', SchemeSG, 'resultStates', 'resultEnergy', 'solverStats' ]) #================ Methods ================# def compute(self): if (self.cycleTranscritical and self.condenser.computeMethod == 'dT'): raise ValueError( 'In transcritical cycle, condenser sub-cooling cannot be used as input' ) self.initCompute(fluid=self.fluidName) # Connect components self.connectPorts(self.evaporator.outlet, self.recuperator.inlet1) self.connectPorts(self.recuperator.outlet1, self.compressor.inlet) self.connectPorts(self.compressor.outlet, self.condenser.inlet) self.connectPorts(self.condenser.outlet, self.recuperator.inlet2) self.connectPorts(self.recuperator.outlet2, self.throttleValve.inlet) self.connectPorts(self.throttleValve.outlet, self.evaporator.inlet) # Initial guess for fl in self.flows: fl.mDot = self.mDot self.evaporator.outlet.state.update_pq(self.pLow, 1) if (self.cycleTranscritical): self.condenser.outlet.state.update_Tp( 1.05 * self.fluid.critical['T'], self.pHigh) else: self.condenser.outlet.state.update_pq(self.pHigh, 0) # Cycle iterations self.solver.run() # Results self.postProcess() def computeCycle(self): self.recuperator.compute() self.recuperator.computeStream1() self.compressor.compute(self.pHigh) self.condenser.compute() self.recuperator.computeStream2() self.throttleValve.compute(self.pLow) self.evaporator.compute() if (self.cycleTranscritical and self.condenser.computeMethod == 'dT'): raise ValueError( 'In transcritical cycle, condenser sub-cooling cannot be used as input' ) def postProcess(self): super(VaporCompressionCycleWithRecuperator, self).postProcess() self.recuperatorHeat = self.recuperator.QDot def R134aCycle(self): super(VaporCompressionCycleWithRecuperator, self).R134aCycle() self.recuperator.eta = 0.7
class VaporCompressionCycle(HeatPumpCycle): label = "Vapor compression cycle" figure = F.ModelFigure( src="ThermoFluids/img/ModuleImages/VaporCompressionCycle.svg") description = F.ModelDescription( "Basic vapor compression cycle used in refrigerators and air conditioners", show=True) #================ Inputs ================# #---------------- Fields ----------------# # FieldGroup compressor = F.SubModelGroup(TC.Compressor, 'FG', label='Compressor') condenser = F.SubModelGroup(TC.Condenser, 'FG', label='Condenser') throttleValve = F.SubModelGroup(TC.ThrottleValve, 'FG', label='JT valve') evaporator = F.SubModelGroup(TC.Evaporator, 'FG', label='Evaporator') inputs = F.SuperGroup( ['workingFluidGroup', compressor, condenser, evaporator], label='Cycle definition') #---------------- Actions ----------------# exampleAction = ServerAction( "loadEg", label="Examples", options=( ('R134aCycle', 'Typical fridge with R134a as a refrigerant'), ('CO2TranscriticalCycle', 'CO2 transcritial cycle'), )) #--------------- Model view ---------------# inputView = F.ModelView(ioType="input", superGroups=[inputs, 'cycleDiagram', 'solver'], actionBar=ActionBar([exampleAction]), autoFetch=True) #================ Results ================# compressorPower = F.Quantity('Power', default=(1, 'kW'), label='compressor power') condenserHeat = F.Quantity('HeatFlowRate', default=(1, 'kW'), label='condenser heat out') evaporatorHeat = F.Quantity('HeatFlowRate', default=(1, 'kW'), label='evaporator heat in') flowFieldGroup = F.FieldGroup( [compressorPower, condenserHeat, evaporatorHeat], label='Energy flows') resultEnergy = F.SuperGroup([flowFieldGroup, 'efficiencyFieldGroup'], label='Energy') resultView = F.ModelView(ioType="output", superGroups=[ 'resultDiagrams', 'resultStates', resultEnergy, 'solverStats' ]) #============= Page structure =============# modelBlocks = [inputView, resultView] #================ Methods ================# def __init__(self): self.R134aCycle() def compute(self): if (self.cycleTranscritical and self.condenser.computeMethod == 'dT'): raise ValueError( 'In transcritical cycle, condenser sub-cooling cannot be used as input' ) if (self.cycleSupercritical and self.evaporator.computeMethod == 'dT'): raise ValueError( 'In supercritical cycle, evaporator super-heating cannot be used as input' ) self.initCompute(fluid=self.fluidName) # Connect components self.connectPorts(self.evaporator.outlet, self.compressor.inlet) self.connectPorts(self.compressor.outlet, self.condenser.inlet) self.connectPorts(self.condenser.outlet, self.throttleValve.inlet) self.connectPorts(self.throttleValve.outlet, self.evaporator.inlet) # Initial guess for fl in self.flows: fl.mDot = self.mDot self.evaporator.outlet.state.update_pq(self.pLow, 1) # Cycle iterations self.solver.run() # Results self.postProcess() def computeCycle(self): self.compressor.compute(self.pHigh) self.condenser.compute() self.throttleValve.compute(self.pLow) self.evaporator.compute() def postProcess(self): super(VaporCompressionCycle, self).postProcess(self.TAmbient) # Flows self.compressorPower = self.mDot * self.compressor.w self.compressorHeat = -self.mDot * self.compressor.qIn self.condenserHeat = -self.mDot * self.condenser.qIn self.evaporatorHeat = self.mDot * self.evaporator.qIn # Efficiencies self.COPCooling = self.evaporatorHeat / self.compressorPower self.COPHeating = self.condenserHeat / self.compressorPower def CO2TranscriticalCycle(self): self.fluidName = 'CarbonDioxide' self.pHighMethod = 'P' self.pHigh = (130, 'bar') self.pLowMethod = 'T' self.TEvaporation = (10, 'degC') self.compressor.modelType = 'S' self.compressor.etaS = 0.8 self.compressor.fQ = 0.2 self.condenser.computeMethod = 'T' self.condenser.TOutlet = (50, 'degC') self.evaporator.computeMethod = 'dT' self.evaporator.dTOutlet = (5, 'degC') def R134aCycle(self): self.fluidName = 'R134a' self.pHighMethod = 'P' self.pHigh = (10, 'bar') self.pLowMethod = 'T' self.TEvaporation = (-20, 'degC') self.compressor.modelType = 'S' self.compressor.etaS = 0.75 self.compressor.fQ = 0.0 self.condenser.computeMethod = 'T' self.condenser.TOutlet = (36, 'degC') self.evaporator.computeMethod = 'Q' self.evaporator.qOutlet = 1.0
class ThermodynamicalCycle(NumericalModel): abstract = True #================ Inputs ================# # Cycle diagram cycleDiagram = F.SubModelGroup(TC.CycleDiagram, 'inputs', label='Diagram settings') # Solver settings solver = F.SubModelGroup(CycleIterator, 'solverSettings', label='Solver') #================ Results ================# #---------------- Fields ----------------# cycleStatesTable = F.TableView( (('T', F.Quantity('Temperature', default=(1, 'degC'))), ('p', F.Quantity('Pressure', default=(1, 'bar'))), ('rho', F.Quantity('Density', default=(1, 'kg/m**3'))), ('h', F.Quantity('SpecificEnthalpy', default=(1, 'kJ/kg'))), ('s', F.Quantity('SpecificEntropy', default=(1, 'kJ/kg-K'))), ('q', F.Quantity()), ('dT', F.Quantity('TemperatureDifference', default=(1, 'degC'))), ('mDot', F.Quantity('MassFlowRate', default=(1, 'kg/min'))), ('b', F.Quantity('SpecificEnergy', default=(1, 'kJ/kg')))), label="Cycle states") cycleStates = F.ViewGroup([cycleStatesTable], label="States") resultStates = F.SuperGroup([cycleStates], label="States") #---------------- Cycle diagram -----------# phDiagram = F.Image(default='') cycleDiagramVG = F.ViewGroup([phDiagram], label="P-H Diagram") resultDiagrams = F.SuperGroup([cycleDiagramVG], label="Diagrams") #---------------- Convergence parameters -----------# residualPlot = F.PlotView((('iteration #', F.Quantity('Dimensionless')), ('h change', F.Quantity('SpecificEnthalpy'))), label='Residual (plot)', ylog=True) iterationTable = F.TableView(( ('h1', F.Quantity('SpecificEnthalpy')), ('h2', F.Quantity('SpecificEnthalpy')), ('h3', F.Quantity('SpecificEnthalpy')), ('h4', F.Quantity('SpecificEnthalpy')), ('h5', F.Quantity('SpecificEnthalpy')), ('h6', F.Quantity('SpecificEnthalpy')), ('h7', F.Quantity('SpecificEnthalpy')), ('h8', F.Quantity('SpecificEnthalpy')), ('h9', F.Quantity('SpecificEnthalpy')), ('h10', F.Quantity('SpecificEnthalpy')), ('h11', F.Quantity('SpecificEnthalpy')), ('h12', F.Quantity('SpecificEnthalpy')), ('h13', F.Quantity('SpecificEnthalpy')), ('h14', F.Quantity('SpecificEnthalpy')), ('h15', F.Quantity('SpecificEnthalpy')), ('h16', F.Quantity('SpecificEnthalpy')), ('h17', F.Quantity('SpecificEnthalpy')), ('h18', F.Quantity('SpecificEnthalpy')), ('h19', F.Quantity('SpecificEnthalpy')), ('h20', F.Quantity('SpecificEnthalpy')), ), label='Residual (table)', options={'formats': (['0.0000E0'] * 20)}) residualGroup = F.ViewGroup([residualPlot, iterationTable], label='Iterations') solverStats = F.SuperGroup([residualGroup], label='Convergence') # Info fields cycleTranscritical = F.Boolean(default=False) cycleSupercritical = F.Boolean(default=False) def initCompute(self, fluid): # Create fluid points self.fluid = Fluid(fluid) self.fp = [] #[FluidState(fluid) for _ in range(numPoints)] self.flows = [] self.solver.cycle = self def connectPorts(self, port1, port2, fluid=None): if fluid == None: fluid = self.fluid fp = FluidState(fluid) flow = P.FluidFlow() self.fp.append(fp) self.flows.append(flow) port1.state = fp port2.state = fp port1.flow = flow port2.flow = flow def solve(self): self.solver.run() self.residualPlot.resize(len(self.solver.change_hHistory)) for i in range(len(self.solver.change_hHistory)): self.residualPlot[i] = (i + 1, self.solver.change_hHistory[i]) self.iterationTable.resize(len(self.solver.hHistory)) v = self.iterationTable.view(dtype=np.float).reshape( -1, len(self.iterationTable.dtype)) numCols = len(self.solver.hHistory[0]) for i in range(len(self.solver.hHistory)): v[i, :numCols] = self.solver.hHistory[i] # print self.iterationTable # iterRecord = np.zeros(1, dtype = self.iterationTable.dtype) # numCols = len(self.solver.hHistory[0]) # for i in range(len(self.solver.hHistory)): # for j in range(numCols): # iterRecord[j] = self.solver.hHistory[i][j] # self.iterationTable[i] = iterRecord def postProcess(self, TAmbient): ## State diagram if (self.cycleDiagram.enable): self.createStateDiagram() ## Table of states self.cycleStatesTable.resize(len(self.fp)) for i in range(len(self.fp)): fp = self.fp[i] self.cycleStatesTable[i] = (fp.T, fp.p, fp.rho, fp.h, fp.s, fp.q, fp.dT, self.flows[i].mDot, fp.b(TAmbient)) # Select the zero for the exergy scale fp = FluidState(self.fluid) fp.update_Tp(TAmbient, 1e5) b0 = fp.b(TAmbient) self.cycleStatesTable['b'] -= b0 def createStateDiagram(self): ncp = len(self.fp) fluidLines = [] for i in range(ncp): fluidLines.append((self.fp[i], self.fp[(i + 1) % ncp])) self.phDiagram = self.cycleDiagram.draw(self.fluid, self.fp, fluidLines) def setPLow(self, p): if (self.fluid.tripple['p'] < p < self.fluid.critical['p']): self.pLow = p sat = self.fluid.saturation_p(p) self.TEvaporation = sat['TsatL'] elif (p > self.fluid.critical['p']): self.pLow = p self.cycleSupercritical = True else: raise ValueError( 'PLow ({} bar) must be between {} bar and {} bar'.format( p / 1e5, self.fluid.tripple['p'] / 1e5, self.fluid.critical['p'] / 1e5)) def setPHigh(self, p): if (self.fluid.tripple['p'] < p < self.fluid.critical['p']): self.pHigh = p sat = self.fluid.saturation_p(p) self.TCondensation = sat['TsatL'] elif (p > self.fluid.critical['p']): self.pHigh = p self.cycleTranscritical = True else: raise ValueError( 'PHigh ({} bar) must be between {} bar and {} bar'.format( p / 1e5, self.fluid.tripple['p'] / 1e5, self.fluid.critical['p'] / 1e5))
class ClaudeCycle(LindeHampsonCycle): label = "Claude cycle" figure = F.ModelFigure(src="ThermoFluids/img/ModuleImages/ClaudeCycle.svg", height=300) description = F.ModelDescription( "Liquefaction cycle using an expander and 3 recurperators for increased efficiency", show=True) #================ Inputs ================# #---------------- Fields ----------------# # FieldGroup recuperator2 = F.SubModelGroup(TC.HeatExchangerTwoStreams, 'FG', label='Recuperator 2') recuperator3 = F.SubModelGroup(TC.HeatExchangerTwoStreams, 'FG', label='Recuperator 3') expander = F.SubModelGroup(TC.Turbine, 'FG', label='Expander') expanderFlowFraction = F.Quantity('Fraction', label='flow fraction') expanderEta = F.Quantity('Efficiency', label='efficiency') expanderFG = F.FieldGroup([expanderFlowFraction, expanderEta], label='Expander') expanderSplitter = F.SubModelGroup(TC.FlowSplitter, 'FG') expanderJunction = F.SubModelGroup(TC.FlowJunction, 'FG') inputs = F.SuperGroup([ 'workingFluidGroup', 'compressor', 'cooler', expanderFG, 'recuperator', recuperator2, recuperator3 ], label='Cycle definition') #---------------- Actions ----------------# exampleAction = ServerAction( "loadEg", label="Examples", options=( ('NitrogenMediumP', 'Nitrogen medium pressure (30 bar) liquefaction cycle'), ('NitrogenLowP', 'Nitrogen low pressure (8 bar) liquefacton cycle'), )) #--------------- Model view ---------------# inputView = F.ModelView(ioType="input", superGroups=[inputs, 'cycleDiagram', 'solver'], actionBar=ActionBar([exampleAction]), autoFetch=True) #================ Results ================# #---------------- Scheme -----------------# scheme_Claude = F.Image( default="static/ThermoFluids/img/ModuleImages/ClaudeCycle.png") SchemeVG = F.ViewGroup([scheme_Claude], label="Process Scheme") SchemeSG = F.SuperGroup([SchemeVG], label="Scheme") resultView = F.ModelView(ioType="output", superGroups=[ 'resultDiagrams', SchemeSG, 'resultStates', 'resultEnergy', 'solverStats' ]) def __init__(self): self.cooler.computeMethod = 'eta' self.cooler.TExt = self.TAmbient self.NitrogenMediumP() def compute(self): self.initCompute(self.fluidName) self.expanderSplitter.frac1 = 1 - self.expanderFlowFraction self.expanderSplitter.frac2 = self.expanderFlowFraction self.expander.eta = self.expanderEta # Connect components self.connectPorts(self.gasSource.outlet, self.inletJunct.inletMain) self.connectPorts(self.inletJunct.outlet, self.compressor.inlet) self.connectPorts(self.compressor.outlet, self.cooler.inlet) self.connectPorts(self.cooler.outlet, self.recuperator.inlet1) self.connectPorts(self.recuperator.outlet1, self.expanderSplitter.inlet) self.connectPorts(self.expanderSplitter.outlet1, self.recuperator2.inlet1) self.connectPorts(self.recuperator2.outlet1, self.recuperator3.inlet1) self.connectPorts(self.recuperator3.outlet1, self.throttleValve.inlet) self.connectPorts(self.throttleValve.outlet, self.liqSeparator.inlet) self.connectPorts(self.liqSeparator.outletVapor, self.secThrottleValve.inlet) self.connectPorts(self.secThrottleValve.outlet, self.recuperator3.inlet2) self.connectPorts(self.recuperator3.outlet2, self.expanderJunction.inletMain) self.connectPorts(self.expanderJunction.outlet, self.recuperator2.inlet2) self.connectPorts(self.recuperator2.outlet2, self.recuperator.inlet2) self.connectPorts(self.recuperator.outlet2, self.inletJunct.inlet2) self.connectPorts(self.expanderSplitter.outlet2, self.expander.inlet) self.connectPorts(self.expander.outlet, self.expanderJunction.inlet2) self.connectPorts(self.liqSeparator.outletLiquid, self.liquidSink.inlet) # Initial guess liqFractionGuess = 0.5 for fl in self.flows: fl.mDot = 1.0 / liqFractionGuess * self.mDot for fp in self.fp: fp.update_Tp(self.TAmbient, self.pIn) # Initialize source self.gasSource.T = self.TIn self.gasSource.p = self.pIn self.gasSource.mDot = self.mDot self.gasSource.compute() # Run solver self.solve() # Results self.postProcess(self.TAmbient) def computeCycle(self): self.compressor.compute(self.pHigh) self.cooler.compute() self.recuperator.compute() self.recuperator.computeStream1() self.expanderSplitter.compute() self.recuperator2.compute() self.recuperator2.computeStream1() self.recuperator3.compute() self.recuperator3.computeStream1() self.throttleValve.compute(self.pLiquid) self.liqSeparator.compute() self.secThrottleValve.compute(self.pIn) self.recuperator3.computeStream2() self.expander.compute(self.pIn) self.expanderJunction.compute() self.recuperator2.computeStream2() self.recuperator.computeStream2() self.inletJunct.compute() def createStateDiagram(self): lineNumbers = [(2, 3), (3, 4), (4, 5), (6, 7), (7, 8), (8, 9), (9, 10), (9, 18), (10, 11), (11, 12), (13, 14), (14, 15), (16, 17)] fluidLines = [] for i, j in lineNumbers: fluidLines.append((self.fp[i - 1], self.fp[j - 1])) self.phDiagram = self.cycleDiagram.draw(self.fluid, self.fp, fluidLines) def NitrogenLiquefaction(self): self.fluidName = "Nitrogen" self.pIn = (1, 'bar') self.TIn = self.TAmbient self.pHigh = (30, 'bar') self.pLiquid = (1.2, 'bar') self.compressor.modelType = 'T' self.compressor.etaT = 0.8 self.compressor.dT = 50. self.cycleDiagram.defaultMaxP = False self.cycleDiagram.defaultMaxT = False self.cycleDiagram.maxPressure = (500, 'bar') self.cycleDiagram.maxTemperature = 350 def NitrogenMediumP(self): self.fluidName = "Nitrogen" self.pIn = (1, 'bar') self.TIn = self.TAmbient self.mDot = (0.188, 'kg/s') self.pHigh = (30, 'bar') self.pLiquid = (1.2, 'bar') self.compressor.modelType = 'T' self.compressor.etaT = 0.8 self.compressor.dT = 50 self.cooler.computeMethod = 'eta' self.cooler.etaThermal = 1. self.cooler.TExt = (30, 'degC') self.expanderEta = 0.8 self.expanderFlowFraction = 0.66 self.recuperator.computeMethod = 'EG' self.recuperator.epsGiven = 0.99 self.recuperator2.computeMethod = 'EN' self.recuperator2.UA = (1950, 'W/K') self.recuperator3.computeMethod = 'EG' self.recuperator3.epsGiven = 0 self.cycleDiagram.defaultMaxT = False self.cycleDiagram.maxTemperature = 400 def NitrogenLowP(self): self.fluidName = "Nitrogen" self.pIn = (1.1, 'bar') self.TIn = (300, 'K') self.mDot = (12.8, 'kg/h') self.pHigh = (8, 'bar') self.pLiquid = (1.2, 'bar') self.TAmbient = (300, 'K') self.compressor.modelType = 'S' self.compressor.etaS = 0.8 self.compressor.fQ = 0.3 self.cooler.computeMethod = 'eta' self.cooler.etaThermal = 1. self.cooler.TExt = (310, 'K') self.expanderEta = 0.5 self.expanderFlowFraction = 0.94 self.recuperator.computeMethod = 'EN' self.recuperator.UA = (1280, 'W/K') self.recuperator2.computeMethod = 'EN' self.recuperator2.UA = (90, 'W/K') self.recuperator3.computeMethod = 'EG' self.recuperator3.epsGiven = 0 self.cycleDiagram.defaultMaxT = False self.cycleDiagram.maxTemperature = 550
class LindeHampsonCycle(LiquefactionCycle): label = "Linde-Hampson cycle" figure = F.ModelFigure( src="ThermoFluids/img/ModuleImages/LindeHampson.svg", height=300) description = F.ModelDescription( "Basic liquefaction cycle used e.g. for air liquefaction", show=True) #================ Inputs ================# #---------------- Fields ----------------# # FieldGroup gasSource = F.SubModelGroup(TC.FluidSource_TP, 'FG') inletJunct = F.SubModelGroup(TC.FlowJunction, 'FG') compressor = F.SubModelGroup(TC.Compressor, 'FG', label='Compressor') cooler = F.SubModelGroup(TC.Condenser, 'FG', label='Cooler') recuperator = F.SubModelGroup(TC.HeatExchangerTwoStreams, 'FG', label='Recuperator') throttleValve = F.SubModelGroup(TC.ThrottleValve, 'FG', label='JT valve') liqSeparator = F.SubModelGroup(TC.PhaseSeparator, 'FG', label='Liquid separator') secThrottleValve = F.SubModelGroup(TC.ThrottleValve, 'FG', label='JT valve') liquidSink = F.SubModelGroup(TC.FluidSink, 'FG') inputs = F.SuperGroup( ['workingFluidGroup', compressor, cooler, recuperator], label='Cycle definition') #---------------- Actions ----------------# exampleAction = ServerAction("loadEg", label="Examples", options=( ('ArgonLiquefaction', 'Argon liquefaction cycle'), ('NitrogenLiquefaction', 'Nitrogen liquefacton cycle'), )) #--------------- Model view ---------------# inputView = F.ModelView(ioType="input", superGroups=[inputs, 'cycleDiagram', 'solver'], actionBar=ActionBar([exampleAction]), autoFetch=True) #================ Results ================# compressorPower = F.Quantity('Power', default=(1, 'kW'), label='compressor power') compressorHeat = F.Quantity('HeatFlowRate', default=(1, 'kW'), label='compressor heat') coolerHeat = F.Quantity('HeatFlowRate', default=(1, 'kW'), label='cooler heat out') flowFieldGroup = F.FieldGroup( [compressorPower, compressorHeat, coolerHeat], label='Flows') resultEnergy = F.SuperGroup([flowFieldGroup, 'efficiencyFieldGroup'], label='Energy') #---------------- Scheme -----------------# scheme_LindeHampson = F.Image( default="static/ThermoFluids/img/ModuleImages/LindeHampson.png") SchemeVG = F.ViewGroup([scheme_LindeHampson], label="Process Scheme") SchemeSG = F.SuperGroup([SchemeVG], label="Scheme") resultView = F.ModelView(ioType="output", superGroups=[ 'resultDiagrams', SchemeSG, 'resultStates', resultEnergy, 'solverStats' ]) #============= Page structure =============# modelBlocks = [inputView, resultView] #================ Methods ================# def __init__(self): self.cooler.computeMethod = 'eta' self.cooler.TExt = self.TAmbient self.ArgonLiquefaction() def compute(self): self.initCompute(self.fluidName) # Connect components self.connectPorts(self.gasSource.outlet, self.inletJunct.inletMain) self.connectPorts(self.inletJunct.outlet, self.compressor.inlet) self.connectPorts(self.compressor.outlet, self.cooler.inlet) self.connectPorts(self.cooler.outlet, self.recuperator.inlet1) self.connectPorts(self.recuperator.outlet1, self.throttleValve.inlet) self.connectPorts(self.throttleValve.outlet, self.liqSeparator.inlet) self.connectPorts(self.liqSeparator.outletVapor, self.secThrottleValve.inlet) self.connectPorts(self.secThrottleValve.outlet, self.recuperator.inlet2) self.connectPorts(self.recuperator.outlet2, self.inletJunct.inlet2) self.connectPorts(self.liqSeparator.outletLiquid, self.liquidSink.inlet) # Initialize source self.gasSource.T = self.TIn self.gasSource.p = self.pIn self.gasSource.mDot = self.mDot self.gasSource.compute() # Initial guess liqFractionGuess = 0.5 for fl in self.flows: fl.mDot = 1.0 / liqFractionGuess * self.mDot for fp in self.fp: fp.update_Tp(self.TAmbient, self.pIn) # liqFractionGuess = 0.2 # self.compressor.inlet.flow.mDot = 1.0 / liqFractionGuess * self.gasSource.outlet.flow.mDot # self.liqSeparator.outletVapor.flow.mDot = liqFractionGuess * self.mDot # # self.compressor.inlet.state.update_Tp( # self.gasSource.outlet.state.T, self.gasSource.outlet.state.p) # self.secThrottleValve.outlet.state.update_pq(self.pIn, 1) # Run solver self.solve() # Results self.postProcess(self.TAmbient) def computeCycle(self): self.compressor.compute(self.pHigh) self.cooler.compute() self.recuperator.compute() self.recuperator.computeStream1() self.throttleValve.compute(self.pLiquid) self.liqSeparator.compute() self.secThrottleValve.compute(self.pIn) self.recuperator.computeStream2() self.inletJunct.compute() def postProcess(self, TAmbient): LiquefactionCycle.postProcess(self, TAmbient) self.compressor.postProcess() self.cooler.postProcess() # Flows self.compressorPower = self.compressor.WDot self.compressorHeat = -self.compressor.QDotIn self.coolerHeat = -self.cooler.QDotIn # Efficiencies self.liqEnergy = self.compressor.WDot / self.liqSeparator.outletLiquid.flow.mDot self.minLiqEnergy = self.liqSeparator.outletLiquid.state.b(self.TAmbient) \ - self.gasSource.outlet.state.b(self.TAmbient) self.etaSecondLaw = self.minLiqEnergy / self.liqEnergy def createStateDiagram(self): lineNumbers = [(2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (6, 10), (7, 8), (8, 9)] fluidLines = [] for i, j in lineNumbers: fluidLines.append((self.fp[i - 1], self.fp[j - 1])) self.phDiagram = self.cycleDiagram.draw(self.fluid, self.fp, fluidLines) def ArgonLiquefaction(self): self.fluidName = "Argon" self.pIn = (1, 'bar') self.TIn = self.TAmbient self.pHigh = (200, 'bar') self.pLiquid = (1.2, 'bar') self.compressor.modelType = 'T' self.compressor.etaT = 0.8 self.compressor.dT = 50. self.recuperator.eta = 0.99 self.cycleDiagram.defaultMaxP = False self.cycleDiagram.maxPressure = (300, 'bar') def NitrogenLiquefaction(self): self.fluidName = "Nitrogen" self.pIn = (1, 'bar') self.TIn = self.TAmbient self.pHigh = (300, 'bar') self.pLiquid = (1.2, 'bar') self.compressor.modelType = 'T' self.compressor.etaT = 0.8 self.compressor.dT = 50. self.recuperator.eta = 0.99 self.cycleDiagram.defaultMaxP = False self.cycleDiagram.defaultMaxT = False self.cycleDiagram.maxPressure = (500, 'bar') self.cycleDiagram.maxTemperature = 350
class RankineCycle(HeatEngineCycle): label = "Rankine cycle" figure = F.ModelFigure(src="ThermoFluids/img/ModuleImages/RankineCycle.png", height = 300) description = F.ModelDescription("Basic Rankine cycle used in power generation", show = True) #================ Inputs ================# #---------------- Fields ----------------# # FieldGroup pump = F.SubModelGroup(TC.Compressor, 'FG', label = 'Pump') boiler = F.SubModelGroup(TC.Evaporator, 'FG', label = 'Boiler') turbine = F.SubModelGroup(TC.Turbine, 'FG', label = 'Turbine') condenser = F.SubModelGroup(TC.Condenser, 'FG', label = 'Condenser') inputs = F.SuperGroup(['workingFluidGroup', pump, boiler, turbine, condenser], label = 'Cycle definition') #---------------- Actions ----------------# exampleAction = ServerAction("loadEg", label = "Examples", options = ( ('SteamPlant', 'Typical steam power plant'), ('ORC1', 'Geothermal Organic Rankine Cycle with R134a'), )) #--------------- Model view ---------------# inputView = F.ModelView(ioType = "input", superGroups = [inputs, 'cycleDiagram', 'solver'], actionBar = ActionBar([exampleAction]), autoFetch = True) #================ Results ================# #---------------- Energy flows -----------# pumpPower = F.Quantity('Power', default = (1, 'kW'), label = 'pump power') boilerHeat = F.Quantity('HeatFlowRate', default = (1, 'kW'), label = 'boiler heat in') turbinePower = F.Quantity('HeatFlowRate', default = (1, 'kW'), label = 'turbine power') condenserHeat = F.Quantity('HeatFlowRate', default = (1, 'kW'), label = 'condenser heat out') flowFieldGroup = F.FieldGroup([pumpPower, boilerHeat, turbinePower, condenserHeat], label = 'Energy flows') resultEnergy = F.SuperGroup([flowFieldGroup, 'efficiencyFieldGroup'], label = 'Energy') resultView = F.ModelView(ioType = "output", superGroups = ['resultDiagrams', 'resultStates', resultEnergy, 'solverStats']) #============= Page structure =============# modelBlocks = [inputView, resultView] #================ Methods ================# def __init__(self): self.SteamPlant() def compute(self): if (self.cycleTranscritical and (self.boiler.computeMethod == 'dT' or self.boiler.computeMethod == 'Q')): raise ValueError('In transcritical cycle, boiler super-heating cannot be used as input') if (self.cycleSupercritical and (self.condenser.computeMethod == 'dT' or self.condenser.computeMethod == 'Q')): raise ValueError('In supercritical cycle condenser sub-cooling cannot be used as input') # Connect components to points self.initCompute(self.fluidName) self.connectPorts(self.condenser.outlet, self.pump.inlet) self.connectPorts(self.pump.outlet, self.boiler.inlet) self.connectPorts(self.boiler.outlet, self.turbine.inlet) self.connectPorts(self.turbine.outlet, self.condenser.inlet) # Initial guess for fl in self.flows: fl.mDot = self.mDot self.condenser.outlet.state.update_pq(self.pLow, 0) # Cycle iterations self.solver.run() # Results self.postProcess() def computeCycle(self): self.pump.compute(self.pHigh) self.boiler.compute() self.turbine.compute(self.pLow) self.condenser.compute() def postProcess(self): super(RankineCycle, self).postProcess(self.TAmbient) # Flows self.pumpPower = self.mDot * self.pump.w self.boilerHeat = self.mDot * self.boiler.qIn self.turbinePower = - self.mDot * self.turbine.w self.condenserHeat = - self.mDot * self.condenser.qIn # Efficiencies self.eta = (self.turbinePower - self.pumpPower) / self.boilerHeat self.etaCarnot = 1 - self.condenser.outlet.state.T / self.boiler.outlet.state.T self.etaSecondLaw = self.eta / self.etaCarnot def SteamPlant(self): self.fluidName = 'Water' self.mDot = 25. self.pHighMethod = 'P' self.pHigh = (35, 'bar') self.pLowMethod = 'P' self.pLow = (0.5, 'bar') self.boiler.computeMethod = 'T' self.boiler.TOutlet = (400, 'degC') self.turbine.eta = 0.85 self.condenser.computeMethod = 'Q' self.condenser.qOutlet = 0 def ORC1(self): self.fluidName = 'R134a' self.mDot = (10, 'kg/min') self.pHighMethod = 'T' self.TEvaporation = (85, 'degC') self.pLowMethod = 'T' self.TCondensation = (40, 'degC') self.boiler.computeMethod = 'Q' self.boiler.qOutlet = 1 self.turbine.eta = 0.8 self.condenser.computeMethod = 'Q' self.condenser.qOutlet = 0
class CylindricalBlockHeatExchanger(NumericalModel): label = "Cylindrical heat exchanger" figure = F.ModelFigure( src="ThermoFluids/img/ModuleImages/HeatExchangerDesign.png", show=False) description = F.ModelDescription( """Model of heat exchanger made out of a solid cylindrical block, with central channels for one of the fluids and a spiraling channel around the block for the other fluid """) async = True progressOptions = {'suffix': '%', 'fractionOutput': False} #================ Inputs ================# #---------------- Fields ----------------# # Fields: geometry blockGeom = F.SubModelGroup(BlockGeometry, 'FG', 'Geometry') blockProps = F.SubModelGroup(BlockProperties, 'FG', 'Properties') blockSG = F.SuperGroup([blockGeom, blockProps], label='Block') # Fields: internal channels primaryChannelsGeom = F.SubModelGroup(ChannelGroupGeometry, 'FG', label='Primary channels') secondaryChannelsGeom = F.SubModelGroup(ChannelGroupGeometry, 'FG', label='Secondary channels') primaryFlowIn = F.SubModelGroup(FluidFlowInput, 'FG', label='Primary flow inlet') secondaryFlowIn = F.SubModelGroup(FluidFlowInput, 'FG', label='Secondary flow inlet') internalChannelSG = F.SuperGroup([ primaryChannelsGeom, secondaryChannelsGeom, primaryFlowIn, secondaryFlowIn ], label='Channels') # Fields: external channel externalChannelGeom = F.SubModelGroup(ExternalChannelGeometry, 'FG', label='Geometry') externalFlowIn = F.SubModelGroup(IncompressibleSolutionFlowInput, 'incomSolFG', label='Inlet flow') externalChannelSG = F.SuperGroup([externalChannelGeom, externalFlowIn], label='External channel') # Fields: settings fvSolverSettings = F.SubModelGroup(FiniteVolumeSolverSettings, 'FG', label='Finite volume solver') sectionResultsSettings = F.SubModelGroup(SectionResultsSettings, 'FG', label='Section results plots') settingsSG = F.SuperGroup([fvSolverSettings, sectionResultsSettings], label='Settings') #--------------- Model view ---------------# inputView = F.ModelView(ioType="input", superGroups=[ blockSG, internalChannelSG, externalChannelSG, settingsSG ], autoFetch=True) #================ Results ================# meshView = F.MPLPlot(label='Cross-section mesh') crossSectionProfileView = F.MPLPlot(label='Cross-section profile') longitudinalProfileView = F.MPLPlot(label='Longitudinal profile') primaryChannelProfileView = F.MPLPlot(label='Primary channel profile') secondaryChannelProfileView = F.MPLPlot(label='Secondary channel profile') geometryVG = F.ViewGroup([ meshView, crossSectionProfileView, longitudinalProfileView, primaryChannelProfileView, secondaryChannelProfileView ], label='Geometry/Mesh') geometrySG = F.SuperGroup([geometryVG], label='Geometry/mesh') primaryFlowOut = F.SubModelGroup(FluidFlowOutput, 'FG', label='Primary flow outlet') secondaryFlowOut = F.SubModelGroup(FluidFlowOutput, 'FG', label='Secondary flow outlet') externalFlowOut = F.SubModelGroup(FluidFlowOutput, 'FG', label='External flow outlet') QDotChannels = F.SubModelGroup(HeatFlowChannels, 'FG', label='Heat flow') resultSG = F.SuperGroup( [primaryFlowOut, secondaryFlowOut, externalFlowOut, QDotChannels], label='Results') resultTable = F.TableView(( ('xStart', F.Quantity('Length', default=(1, 'm'))), ('xEnd', F.Quantity('Length', default=(1, 'm'))), ('TPrimFluid', F.Quantity('Temperature', default=(1, 'degC'))), ('TPrimWall', F.Quantity('Temperature', default=(1, 'degC'))), ('RePrim', F.Quantity()), ('hConvPrim', F.Quantity('HeatTransferCoefficient', default=(1, 'W/m**2-K'))), ('QDotPrim', F.Quantity('HeatFlowRate', default=(1, 'W'))), ('TSecFluid', F.Quantity('Temperature', default=(1, 'degC'))), ('TSecWall', F.Quantity('Temperature', default=(1, 'degC'))), ('ReSec', F.Quantity()), ('hConvSec', F.Quantity('HeatTransferCoefficient', default=(1, 'W/m**2-K'))), ('QDotSec', F.Quantity('HeatFlowRate', default=(1, 'W'))), ('TExtFluid', F.Quantity('Temperature', default=(1, 'degC'))), ('TExtWall', F.Quantity('Temperature', default=(1, 'degC'))), ('ReExt', F.Quantity()), ('hConvExt', F.Quantity('HeatTransferCoefficient', default=(1, 'W/m**2-K'))), ('QDotExt', F.Quantity('HeatFlowRate', default=(1, 'W'))), ), label='Detailed results') resultTPlot = F.PlotView(( ('x', F.Quantity('Length', default=(1, 'm'))), ('TPrimFluid', F.Quantity('Temperature', default=(1, 'degC'))), ('TPrimWall', F.Quantity('Temperature', default=(1, 'degC'))), ('TSecFluid', F.Quantity('Temperature', default=(1, 'degC'))), ('TSecWall', F.Quantity('Temperature', default=(1, 'degC'))), ('TExtFluid', F.Quantity('Temperature', default=(1, 'degC'))), ('TExtWall', F.Quantity('Temperature', default=(1, 'degC'))), ), label='Temperature plots') resultQPlot = F.PlotView(( ('x', F.Quantity('Length', default=(1, 'm'))), ('QDotPrim', F.Quantity('HeatFlowRate', default=(1, 'W'))), ('QDotSec', F.Quantity('HeatFlowRate', default=(1, 'W'))), ('QDotExt', F.Quantity('HeatFlowRate', default=(1, 'W'))), ), label='Heat flow plots') detailedResultVG = F.ViewGroup([resultTable, resultTPlot, resultQPlot], label='Detailed results') detailedResultSG = F.SuperGroup([detailedResultVG], label='Detailed results') sectionPlot1 = F.MPLPlot(label='Section 1') sectionPlot2 = F.MPLPlot(label='Section 2') sectionPlot3 = F.MPLPlot(label='Section 3') sectionPlot4 = F.MPLPlot(label='Section 4') sectionPlot5 = F.MPLPlot(label='Section 5') sectionResultsVG = F.ViewGroup( [sectionPlot1, sectionPlot2, sectionPlot3, sectionPlot4, sectionPlot5], label='Section temperatures') sectionResultsSG = F.SuperGroup([sectionResultsVG], label='Section results') resultView = F.ModelView( ioType="output", superGroups=[geometrySG, resultSG, detailedResultSG, sectionResultsSG]) ############# Page structure ######## modelBlocks = [inputView, resultView] ############# Methods ############### def __init__Internal(self): self.blockGeom.diameter = (58.0, 'mm') self.blockGeom.length = (0.8, 'm') self.blockProps.divisionStep = (0.1, 'm') self.blockProps.material = 'Aluminium6061' self.primaryChannelsGeom.number = 3 self.primaryChannelsGeom.radialPosition = (7, 'mm') self.primaryChannelsGeom.startingAngle = (0, 'deg') self.primaryChannelsGeom.meshFineness = 4 self.primaryChannelsGeom.channelName = "PrimaryChannel" self.primaryChannelsGeom.externalDiameter = (7.5, 'mm') self.primaryChannelsGeom.sections[0] = (0.002, 0.2) self.primaryChannelsGeom.sections[1] = (0.004, 0.1) self.primaryChannelsGeom.sections[2] = (0.006, 0.1) self.primaryChannelsGeom.sections[3] = (0.0065, 0.1) self.primaryChannelsGeom.sections[4] = (0.007, 0.3) self.secondaryChannelsGeom.number = 3 self.secondaryChannelsGeom.radialPosition = (17.5, 'mm') self.secondaryChannelsGeom.startingAngle = (60, 'deg') self.secondaryChannelsGeom.meshFineness = 4 self.secondaryChannelsGeom.channelName = "SecondaryChannel" self.secondaryChannelsGeom.externalDiameter = (7.5, 'mm') self.secondaryChannelsGeom.sections[0] = (0.002, 0.2) self.secondaryChannelsGeom.sections[1] = (0.004, 0.1) self.secondaryChannelsGeom.sections[2] = (0.006, 0.1) self.secondaryChannelsGeom.sections[3] = (0.0065, 0.1) self.secondaryChannelsGeom.sections[4] = (0.007, 0.3) self.primaryFlowIn.fluidName = 'ParaHydrogen' self.primaryFlowIn.flowRateChoice = 'm' self.primaryFlowIn.mDot = (1, 'kg/h') self.primaryFlowIn.T = (100, 'K') self.primaryFlowIn.p = (1, 'bar') self.secondaryFlowIn.fluidName = 'ParaHydrogen' self.secondaryFlowIn.flowRateChoice = 'm' self.secondaryFlowIn.mDot = (1, 'kg/h') self.secondaryFlowIn.T = (100, 'K') self.secondaryFlowIn.p = (1, 'bar') self.externalFlowIn.solName = 'MEG' self.externalFlowIn.solMassFraction = (50, '%') self.externalFlowIn.flowRateChoice = 'V' self.externalFlowIn.VDot = (3, 'm**3/h') self.externalFlowIn.T = (80, 'degC') self.externalFlowIn.p = (1, 'bar') self.externalChannelGeom.widthAxial = (30, 'mm') self.externalChannelGeom.heightRadial = (12, 'mm') self.externalChannelGeom.coilPitch = (32, 'mm') self.externalChannelGeom.meshFineness = 4 self.sectionResultsSettings.setTRange = False self.sectionResultsSettings.Tmin = (100, 'K') self.sectionResultsSettings.Tmax = (380, 'K') def __init__(self): self.blockGeom.diameter = (60.0, 'mm') self.blockGeom.length = (1.0, 'm') self.blockProps.divisionStep = (0.1, 'm') self.blockProps.material = 'Aluminium6061' self.primaryChannelsGeom.number = 4 self.primaryChannelsGeom.radialPosition = (10, 'mm') self.primaryChannelsGeom.startingAngle = (0, 'deg') self.primaryChannelsGeom.meshFineness = 3 self.primaryChannelsGeom.channelName = "PrimaryChannel" self.primaryChannelsGeom.externalDiameter = (10, 'mm') self.primaryChannelsGeom.sections[0] = (0.000, 0.2) self.primaryChannelsGeom.sections[1] = (0.002, 0.2) self.primaryChannelsGeom.sections[2] = (0.004, 0.2) self.primaryChannelsGeom.sections[3] = (0.006, 0.2) self.primaryChannelsGeom.sections[4] = (0.008, 0.2) self.secondaryChannelsGeom.number = 6 self.secondaryChannelsGeom.radialPosition = (22.5, 'mm') self.secondaryChannelsGeom.startingAngle = (60, 'deg') self.secondaryChannelsGeom.meshFineness = 3 self.secondaryChannelsGeom.channelName = "SecondaryChannel" self.secondaryChannelsGeom.externalDiameter = (7, 'mm') self.secondaryChannelsGeom.sections[0] = (0.002, 0.2) self.secondaryChannelsGeom.sections[1] = (0.003, 0.2) self.secondaryChannelsGeom.sections[2] = (0.004, 0.2) self.secondaryChannelsGeom.sections[3] = (0.005, 0.2) self.secondaryChannelsGeom.sections[4] = (0.006, 0.2) self.primaryFlowIn.fluidName = 'Ammonia' self.primaryFlowIn.flowRateChoice = 'm' self.primaryFlowIn.mDot = (50, 'kg/h') self.primaryFlowIn.T = (400, 'K') self.primaryFlowIn.p = (20, 'bar') self.secondaryFlowIn.fluidName = 'Ammonia' self.secondaryFlowIn.flowRateChoice = 'm' self.secondaryFlowIn.mDot = (50, 'kg/h') self.secondaryFlowIn.T = (400, 'K') self.secondaryFlowIn.p = (20, 'bar') self.externalFlowIn.solName = 'MPG' self.externalFlowIn.solMassFraction = (50, '%') self.externalFlowIn.flowRateChoice = 'V' self.externalFlowIn.VDot = (0.5, 'm**3/h') self.externalFlowIn.T = (20, 'degC') self.externalFlowIn.p = (1, 'bar') self.externalChannelGeom.widthAxial = (40, 'mm') self.externalChannelGeom.heightRadial = (10, 'mm') self.externalChannelGeom.coilPitch = (43, 'mm') self.externalChannelGeom.meshFineness = 3 self.sectionResultsSettings.setTRange = False self.sectionResultsSettings.Tmin = (200, 'K') self.sectionResultsSettings.Tmax = (350, 'K') def computeAsync(self): # PreComputation self.externalChannelGeom.compute(self.blockGeom.diameter) self.primaryChannelsGeom.compute() self.secondaryChannelsGeom.compute() self.primaryFlowIn.compute() self.secondaryFlowIn.compute() self.externalFlowIn.compute() # Validation self.validateInputs() # Create the mesh mesher = HeatExchangerMesher() mesher.create(self) # Create channel calculator objects solver = HeatExchangerSolver(self, mesher) solver.createChannelCalculators(self) # Produce geometry/mesh drawings self.drawGeometry(mesher, solver) solver.solve(self) # Produce results self.postProcess(mesher, solver) def drawGeometry(self, mesher, solver): # Draw the mesh vertexCoords = mesher.mesh.vertexCoords vertexIDs = mesher.mesh._orderedCellVertexIDs triPlotMesh = tri.Triangulation(vertexCoords[0], vertexCoords[1], np.transpose(vertexIDs)) self.meshView.triplot(triPlotMesh) self.meshView.set_aspect('equal') self.meshView.set_title('%d elements' % len(mesher.mesh.cellCenters[0])) # Draw the channels profiles ticks = ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x * 1e3)) solver.primChannelCalc.plotGeometry(self.primaryChannelProfileView) self.primaryChannelProfileView.set_xlabel('[m]') self.primaryChannelProfileView.set_ylabel('[mm]') self.primaryChannelProfileView.yaxis.set_major_formatter(ticks) solver.secChannelCalc.plotGeometry(self.secondaryChannelProfileView) self.secondaryChannelProfileView.set_xlabel('[m]') self.secondaryChannelProfileView.set_ylabel('[mm]') self.secondaryChannelProfileView.yaxis.set_major_formatter(ticks) #Draw the cross-section profile crossSectionProfile = HeatExchangerCrossSectionProfile() crossSectionProfile.addBlock(self.blockGeom) crossSectionProfile.addChannels(self.primaryChannelsGeom) crossSectionProfile.addChannels(self.secondaryChannelsGeom) crossSectionProfile.plotGeometry(self.crossSectionProfileView) self.crossSectionProfileView.set_xlabel('[mm]') self.crossSectionProfileView.set_ylabel('[mm]') self.crossSectionProfileView.xaxis.set_major_formatter(ticks) self.crossSectionProfileView.yaxis.set_major_formatter(ticks) #Draw the longitudinal profile longitudinalProfile = HeatExchangerLongitudinalProfile() longitudinalProfile.addBlock(self.blockGeom) longitudinalProfile.addExternalChannel(self.externalChannelGeom) longitudinalProfile.plotGeometry(self.longitudinalProfileView) self.longitudinalProfileView.set_xlabel('[mm]') self.longitudinalProfileView.set_ylabel('[mm]') self.longitudinalProfileView.xaxis.set_major_formatter(ticks) self.longitudinalProfileView.yaxis.set_major_formatter(ticks) def postProcess(self, mesher, solver): # Get the value for the outlet conditions self.primaryFlowOut.compute(fState=solver.primChannelStateOut, mDot=self.primaryFlowIn.mDot) self.secondaryFlowOut.compute(fState=solver.secChannelStateOut, mDot=self.secondaryFlowIn.mDot) self.externalFlowOut.compute(fState=solver.extChannelStateOut, mDot=self.externalFlowIn.mDot) self.QDotChannels.QDotPrimaryChannels = self.primaryFlowIn.mDot * \ abs(self.primaryFlowOut.fState.h - self.primaryFlowIn.fState.h) self.QDotChannels.QDotSecondaryChannels = self.secondaryFlowIn.mDot * \ abs(self.secondaryFlowOut.fState.h - self.secondaryFlowIn.fState.h) self.QDotChannels.QDotExternalChannel = self.externalFlowIn.mDot * \ abs(self.externalFlowOut.fState.h - self.externalFlowIn.fState.h) # Fill the table with values self.resultTable.resize(solver.numSectionSteps) self.resultTPlot.resize(solver.numSectionSteps) self.resultQPlot.resize(solver.numSectionSteps) for i in range(solver.numSectionSteps): self.resultTable[i] = ( solver.primChannelCalc.sections[i].xStart, solver.primChannelCalc.sections[i].xEnd, solver.primChannelCalc.sections[i].fState.T, solver.primChannelCalc.sections[i].TWall, solver.primChannelCalc.sections[i].Re, solver.primChannelCalc.sections[i].hConv, abs(solver.primChannelCalc.sections[i].QDotWall), solver.secChannelCalc.sections[i].fState.T, solver.secChannelCalc.sections[i].TWall, solver.secChannelCalc.sections[i].Re, solver.secChannelCalc.sections[i].hConv, abs(solver.secChannelCalc.sections[i].QDotWall), solver.extChannelCalc.sections[i].fState.T, solver.extChannelCalc.sections[i].TWall, solver.extChannelCalc.sections[i].Re, solver.extChannelCalc.sections[i].hConv, abs(solver.extChannelCalc.sections[i].QDotWall), ) self.resultTPlot[i] = ( (solver.primChannelCalc.sections[i].xStart + solver.primChannelCalc.sections[i].xEnd) / 2, solver.primChannelCalc.sections[i].fState.T, solver.primChannelCalc.sections[i].TWall, solver.secChannelCalc.sections[i].fState.T, solver.secChannelCalc.sections[i].TWall, solver.extChannelCalc.sections[i].fState.T, solver.extChannelCalc.sections[i].TWall, ) self.resultQPlot[i] = ( (solver.primChannelCalc.sections[i].xStart + solver.primChannelCalc.sections[i].xEnd) / 2, abs(solver.primChannelCalc.sections[i].QDotWall), abs(solver.secChannelCalc.sections[i].QDotWall), abs(solver.extChannelCalc.sections[i].QDotWall), ) def validateInputs(self): self.validateChannels(self.primaryChannelsGeom, "primary") self.validateChannels(self.secondaryChannelsGeom, "secondary") if self.externalChannelGeom.widthAxial > self.externalChannelGeom.coilPitch: raise ValueError( 'The coil pitch of the external channel is less than the axial width.' ) def validateChannels(self, channelsGeom, channelsName): if self.blockGeom.diameter <= channelsGeom.externalDiameter: raise ValueError( 'The block diameter is less than the external diameter of the {0} channels.' .format(channelsName)) if self.blockGeom.diameter / 2. <= ( channelsGeom.radialPosition + channelsGeom.externalDiameter / 2.): raise ValueError( 'The radial position of the {0} channels is too big (the channels are outside of the block).' .format(channelsName)) if self.blockGeom.length != channelsGeom.length: raise ValueError( 'The length of the block is different from the length of the {0} channels.' .format(channelsName)) i = 0 for section in channelsGeom.sections: if (section['length'] < self.blockProps.divisionStep): raise ValueError( 'The axial division step of the block is greater than the {0}-th section of the {1} channels' .format(i, channelsName)) if self.isDividedExactly(section['length'], self.blockProps.divisionStep) == False: raise ValueError( 'The axial division step of the block does not divide the {0}-th section of the {1} channels in equal parts.' .format(i, channelsName)) i += 1 def isDividedExactly(self, a, b): bigE = 1e6 return int(a * bigE) % int(b * bigE) == 0