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 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 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 ChemostatDDE2_ESA(NumericalModel): label = "DDE Chemostat (Example 2 - ESA)" description = F.ModelDescription( "Chemostat model with delay differential equations (DDE) and extremum seeking algorithm (ESA) - Example 2", show=True) figure = F.ModelFigure( src="BioReactors/img/ModuleImages/SimpleChemostat.png", show=False) #1. ############ Inputs ############### #1.1 Fields - Input values # Parameters k1 = F.Quantity( default=0, minValue=0, maxValue=1e6, label='k<sub>1</sub>', description= 'yield coefficient related to substrate-1 consumption from bacteria-1') k2 = F.Quantity( default=0, minValue=0, maxValue=1e6, label='k<sub>2</sub>', description= 'yield coefficient related to substrate-2 production from bacteria-1') k3 = F.Quantity( default=0, minValue=0, maxValue=1e6, label='k<sub>3</sub>', description= 'yield coefficient related to substrate-2 consumption from bacteria-2') k4 = F.Quantity( default=0, minValue=0, maxValue=1e6, label='k<sub>4</sub>', description= 'yield coefficient of methane (biogas) on bacteria-2 and substrate-2') s1_in = F.Quantity('Bio_MassConcentration', default=(0, 'g/L'), label='s<sub>1</sub><sup>in</sub>', description='input substrate-1 concentration') s2_in = F.Quantity('Bio_MassConcentration', default=(0, 'g/L'), label='s<sub>2</sub><sup>in</sub>', description='input substrate-2 concentration') a = F.Quantity( 'Fraction', default=(0, '-'), label='α', description= 'proportion of organisms that are affected by the dilution rate D') D = F.Quantity('Bio_TimeRate', default=(0, '1/day'), minValue=(0, '1/day'), label='D', description='dilution rate') parametersFG = F.FieldGroup([s1_in, s2_in, k1, k2, k3, k4, a, D], label='Parameters') # Parameters - specific growth rates m1 = F.Quantity('Bio_TimeRate', default=(0, '1/day'), minValue=(0, '1/day'), label='m<sub>1</sub>', description='maximum specific growth rate of bacteria-1') m2 = F.Quantity('Bio_TimeRate', default=(0, '1/day'), minValue=(0, '1/day'), label='m<sub>2</sub>', description='maximum specific growth rate of bacteria-2') k_s1 = F.Quantity('Bio_MassConcentration', default=(0, 'g/L'), label='k<sub>s1</sub>', description='half saturation constant of bacteria-1') k_s2 = F.Quantity('Bio_MassConcentration', default=(0, 'g/L'), label='k<sub>s2</sub>', description='half saturation constant of bacteria-2') k_I = F.Quantity('Dimensionless', default=(0, '-'), label='k<sub>I</sub>', description='constant in unit: [ sqrt( g/L ) ]') parametersMuFG = F.FieldGroup([m1, m2, k_s1, k_s2, k_I], label='Parameters - specific growth rates') # Parameters - time delays tau1 = F.Quantity( 'Bio_Time', default=(0, 'day'), minValue=(0, 'day'), label='τ<sub>1</sub>', description= 'time delay in conversion of the substrate-1 to viable biomass for bacteria-1' ) tau2 = F.Quantity( 'Bio_Time', default=(0, 'day'), minValue=(0, 'day'), label='τ<sub>2</sub>', description= 'time delay in conversion of the substrate-2 to viable biomass for bacteria-2' ) parametersTauFG = F.FieldGroup([tau1, tau2], label='Parameters - time delay') # historical initial conditions s1_hist_vals = F.Quantity( 'Bio_MassConcentration', default=(0, 'g/L'), label='s<sub>1</sub><sup>[-τ<sub>1</sub>, 0]</sup>', description='historical initial condition for substrate-1 concentration' ) x1_hist_vals = F.Quantity( 'Bio_MassConcentration', default=(0, 'g/L'), label='x<sub>1</sub><sup>[-τ<sub>1</sub>, 0]</sup>', description='historical initial condition for bacteria-1 concentration' ) s2_hist_vals = F.Quantity( 'Bio_MassConcentration', default=(0, 'g/L'), label='s<sub>2</sub><sup>[-τ<sub>2</sub>, 0]</sup>', description='historical initial condition for substrate-2 concentration' ) x2_hist_vals = F.Quantity( 'Bio_MassConcentration', default=(0, 'g/L'), label='x<sub>2</sub><sup>[-τ<sub>2</sub>, 0]</sup>', description='historical initial condition for bacteria-2 concentration' ) parametersHistFG = F.FieldGroup( [s1_hist_vals, x1_hist_vals, s2_hist_vals, x2_hist_vals], label='Historical initial conditions') inputValuesSG = F.SuperGroup( [parametersMuFG, parametersFG, parametersTauFG, parametersHistFG], label="Input values") #1.2 Fields - Settings ESA_enable = F.Boolean( default=False, label='enable', description= 'find the the maximum methane flow rate (i.e. run extremum seeking algorithm)' ) ESA_DMin = F.Quantity('Bio_TimeRate', default=(0, '1/day'), minValue=(0.0, '1/day'), label='D<sub>min</sub>', description='minimum dilution rate', show='self.ESA_enable') ESA_DMax = F.Quantity('Bio_TimeRate', default=(0, '1/day'), minValue=(0.0, '1/day'), label='D<sub>max</sub>', description='maximum dilution rate', show='self.ESA_enable') ESA_eps = F.Quantity('Bio_TimeRate', default=(0, '1/day'), minValue=(1e-9, '1/day'), maxValue=(1.0, '1/day'), label='ε*', description='D max tolerance', show='self.ESA_enable') ESA_h = F.Quantity('Bio_TimeRate', default=(0, '1/day'), minValue=(1e-8, '1/day'), maxValue=(1.0, '1/day'), label='h*', description='seeking step of D max', show='self.ESA_enable') ESA_eps_z = F.Quantity('Bio_MassConcentration', default=(0, 'g/L'), minValue=(1e-9, 'g/L'), maxValue=(1.0, 'g/L'), label='ε<sub>z</sub>*', description='equilibrium point tolerance', show='self.ESA_enable') ESA_FG = F.FieldGroup( [ESA_enable, ESA_DMin, ESA_DMax, ESA_eps, ESA_h, ESA_eps_z], label='Extremum Seeking Algorithm (ESA)') DsQs_DMin = F.Quantity('Bio_TimeRate', default=(0, '1/day'), minValue=(0, '1/day'), label='D<sub>min</sub>', description='minimum dilution rate') DsQs_DMax = F.Quantity('Bio_TimeRate', default=(0, '1/day'), minValue=(0, '1/day'), label='D<sub>max</sub>', description='maximum dilution rate') DsQs_Step = F.Quantity('Bio_TimeRate', default=(0, '1/day'), minValue=(1e-4, '1/day'), maxValue=(1.0, '1/day'), label='D<sub>step</sub>', description='step of dilution rate') DsQs_FG = F.FieldGroup([DsQs_DMin, DsQs_DMax, DsQs_Step], label='Chart (Ds, Qs)') tFinal = F.Quantity('Bio_Time', default=(100, 'day'), minValue=(0, 'day'), maxValue=(5000, 'day'), label='simulation time') tPrint = F.Quantity('Bio_Time', default=(0.1, 'day'), minValue=(1e-5, 'day'), maxValue=(100, 'day'), label='print interval*') mainSimStep = F.Quantity('Bio_Time', default=(10., 'day'), minValue=(0.25, 'day'), maxValue=(1e3, 'day'), label='main simulation step*') absTol = F.Quantity('Bio_Time', default=(1e-12, 'day'), minValue=(1e-16, 'day'), maxValue=(1e-5, 'day'), label='absolute tolerance*') relTol = F.Quantity('Bio_Time', default=(1e-12, 'day'), minValue=(1e-16, 'day'), maxValue=(1e-3, 'day'), label='relative tolerance*') solverFG = F.FieldGroup([tFinal, tPrint, mainSimStep, absTol, relTol], label='Solver') settingsSG = F.SuperGroup([solverFG, DsQs_FG, ESA_FG], label='Settings') #1.4 Model view inputView = F.ModelView(ioType="input", superGroups=[inputValuesSG, settingsSG], autoFetch=True) #2. ############ Results ############### # 2.1. Results dataSeries = ( ('time', F.Quantity('Bio_Time', default=(1, 'day'))), ('s1', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))), ('x1', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))), ('s2', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))), ('x2', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))), ('D', F.Quantity('Bio_TimeRate', default=(1, '1/day'))), ('Q', F.Quantity('Bio_MassConcentrationFlowRate', default=(1, 'g/L/day'))), ) plot = F.PlotView( dataSeries, label='Plot', options={ 'ylabel': None, 'title': 'Chemostat (DDE)' }, ) table = F.TableView(dataSeries, label='Table', options={ 'title': 'Title', 'formats': [ '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000' ] }) chartS1S2 = F.MPLPlot(label='Chart (s<sub>1</sub>, s<sub>2</sub>)') chartX1X2 = F.MPLPlot(label='Chart (x<sub>1</sub>, x<sub>2</sub>)') chartS1X1 = F.MPLPlot(label='Chart (s<sub>1</sub>, x<sub>1</sub>)') chartS2X2 = F.MPLPlot(label='Chart (s<sub>2</sub>, x<sub>2</sub>)') chartS2X2 = F.MPLPlot(label='Chart (s<sub>2</sub>, x<sub>2</sub>)') chartDQ = F.MPLPlot(label='Chart (D, Q)') chartDsQs = F.MPLPlot(label='Chart (Ds, Qs)') resultsVG = F.ViewGroup([ plot, table, chartS1S2, chartX1X2, chartS1X1, chartS2X2, chartDQ, chartDsQs ], label='Results') resultsSG = F.SuperGroup([resultsVG], label="Results") # 2.2 Equilibrium point s1_eqpnt = F.Quantity( 'Bio_MassConcentration', default=(0., 'g/L'), label='s<sub>1</sub><sup>*</sup>', description='substrate-1 concentration at the equilibrium point') x1_eqpnt = F.Quantity( 'Bio_MassConcentration', default=(0., 'g/L'), label='x<sub>1</sub><sup>*</sup>', description='bacteria-1 concentration at the equilibrium point') s2_eqpnt = F.Quantity( 'Bio_MassConcentration', default=(0., 'g/L'), label='s<sub>2</sub><sup>*</sup>', description='substrate-2 concentration at the equilibrium point') x2_eqpnt = F.Quantity( 'Bio_MassConcentration', default=(0., 'g/L'), label='x<sub>2</sub><sup>*</sup>', description='bacteria-1 concentration at the equilibrium point') equilibriumPointFG = F.FieldGroup([s1_eqpnt, x1_eqpnt, s2_eqpnt, x2_eqpnt], label='Equilibrium point') equilibriumPointSG = F.SuperGroup([equilibriumPointFG], label='Equilibrium point') # 2.3 ESA Results resESA_QMaxIsFound = F.Boolean(label='Q<sub>max</sub> is found') resESA_DMax = F.Quantity('Bio_TimeRate', default=(0, '1/day'), label='D<sub>max</sub>', description='maximum dilution rate') resESA_QMax = F.Quantity('Bio_MassConcentrationFlowRate', default=(0, 'g/L/day'), label='Q<sub>max</sub>', description='maximum methane (biogas) flow rate') resESA_FG = F.FieldGroup([resESA_QMaxIsFound, resESA_DMax, resESA_QMax], label='Results (ESA)') resESA_SG = F.SuperGroup([resESA_FG], label='Results (ESA)') #2.1 Model view resultView = F.ModelView( ioType="output", superGroups=[resultsSG, resESA_SG, equilibriumPointSG]) ############# Page structure ######## modelBlocks = [inputView, resultView] def __init__(self): self.k1 = (10.53, '-') self.k2 = (28.6, '-') self.k3 = (1074, '-') self.k4 = (100, '-') self.s1_in = (7.5, 'g/L') self.s2_in = (75, 'g/L') self.a = (0.5, '-') self.m1 = (1.2, '1/day') self.m2 = (0.74, '1/day') self.k_s1 = (7.1, 'g/L') self.k_s2 = (9.28, 'g/L') self.k_I = (16, '-') self.D = (0.25, '1/day') self.tau1 = (2, 'day') self.tau2 = (7, 'day') self.s1_hist_vals = (2, 'g/L') self.x1_hist_vals = (0.1, 'g/L') self.s2_hist_vals = (10, 'g/L') self.x2_hist_vals = (0.05, 'g/L') self.DsQs_DMin = (0.0, '1/day') self.DsQs_DMax = (1.0, '1/day') self.DsQs_Step = (0.01, '1/day') self.tFinal = (2000, 'day') self.tPrint = (1.0, 'day') self.mainSimStep = (10., 'day') self.absTol = (1e-12, 'day') self.relTol = (1e-12, 'day') self.ESA_enable = True self.ESA_DMin = (0.01, '1/day') self.ESA_DMax = (0.33, '1/day') self.ESA_eps = (0.01, '1/day') self.ESA_h = (0.025, '1/day') self.ESA_eps_z = (0.01, 'g/L') def compute(self): chemostatDDE = DM.ChemostatDDE2_ESA(self) if self.ESA_enable: chemostatDDE.runESA(self) else: chemostatDDE.run(self) resESA = chemostatDDE.getResultsESA() self.resESA_QMaxIsFound = resESA['QMaxIsFound'] self.resESA_DMax = (resESA['DMax'], '1/day') self.resESA_QMax = (resESA['QMax'], 'kg/m**3/day') res = chemostatDDE.getResults() results = np.array([ res['t'], res['s1'], res['x1'], res['s2'], res['x2'], res['D'], res['Q'] ]).transpose() self.plot = results self.table = results chemostatDDE.plotS1S2(self.chartS1S2) chemostatDDE.plotX1X2(self.chartX1X2) chemostatDDE.plotS1X1(self.chartS1X1) chemostatDDE.plotS2X2(self.chartS2X2) chemostatDDE.plotDQ(self.chartDQ) chemostatDDE.plotDsQs(self.chartDsQs) self.s1_eqpnt = (chemostatDDE.equilibriumPoint[0], 'kg/m**3') self.x1_eqpnt = (chemostatDDE.equilibriumPoint[1], 'kg/m**3') self.s2_eqpnt = (chemostatDDE.equilibriumPoint[2], 'kg/m**3') self.x2_eqpnt = (chemostatDDE.equilibriumPoint[3], 'kg/m**3')
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 ChemostatDDE1(NumericalModel): label = "DDE Chemostat (Example 1)" description = F.ModelDescription( "Chemostat model with delay differential equations (DDE) - Example 1", show=True) figure = F.ModelFigure( src="BioReactors/img/ModuleImages/SimpleChemostat.png", show=False) #1. ############ Inputs ############### #1.1 Fields - Input values # Parameters k1 = F.Quantity( default=10.53, minValue=0, maxValue=1e6, label='k<sub>1</sub>', description= 'yield coefficient related to substrate-1 consumption from bacteria-1') k2 = F.Quantity( default=28.6, minValue=0, maxValue=1e6, label='k<sub>2</sub>', description= 'yield coefficient related to substrate-2 production from bacteria-1') k3 = F.Quantity( default=1074., minValue=0, maxValue=1e6, label='k<sub>3</sub>', description= 'yield coefficient related to substrate-2 consumption from bacteria-2') s1_in = F.Quantity('Bio_MassConcentration', default=(7.5, 'g/L'), label='s<sub>1</sub><sup>in</sub>', description='input substrate-1 concentration') s2_in = F.Quantity('Bio_MassConcentration', default=(75., 'g/L'), label='s<sub>2</sub><sup>in</sub>', description='input substrate-2 concentration') a = F.Quantity( 'Fraction', default=(0.5, '-'), label='α', description= 'proportion of organisms that are affected by the dilution rate D') D = F.Quantity('Bio_TimeRate', default=(0.89, '1/day'), minValue=(0, '1/day'), label='D', description='dilution rate') parametersFG = F.FieldGroup([s1_in, s2_in, k1, k2, k3, a, D], label='Parameters') # Parameters - specific growth rates m1 = F.Quantity('Bio_TimeRate', default=(1.20, '1/day'), minValue=(0, '1/day'), label='m<sub>1</sub>', description='maximum specific growth rate of bacteria-1') m2 = F.Quantity('Bio_TimeRate', default=(0.74, '1/day'), minValue=(0, '1/day'), label='m<sub>2</sub>', description='maximum specific growth rate of bacteria-2') k_s1 = F.Quantity('Bio_MassConcentration', default=(7.1, 'g/L'), label='k<sub>s1</sub>', description='half saturation constant of bacteria-1') k_s2 = F.Quantity('Bio_MassConcentration', default=(9.28, 'g/L'), label='k<sub>s2</sub>', description='half saturation constant of bacteria-2') k_I = F.Quantity('Dimensionless', default=(16., '-'), label='k<sub>I</sub>', description='constant in unit: [ sqrt( g/L ) ]') parametersMuFG = F.FieldGroup([m1, m2, k_s1, k_s2, k_I], label='Parameters - specific growth rates') # Parameters - time delays tau1 = F.Quantity( 'Bio_Time', default=(10.1, 'day'), minValue=(0, 'day'), label='τ<sub>1</sub>', description= 'time delay in conversion of the substrate-1 to viable biomass for bacteria-1' ) tau2 = F.Quantity( 'Bio_Time', default=(5.7, 'day'), minValue=(0, 'day'), label='τ<sub>2</sub>', description= 'time delay in conversion of the substrate-2 to viable biomass for bacteria-2' ) parametersTauFG = F.FieldGroup([tau1, tau2], label='Parameters - time delay') # historical initial conditions s1_hist_vals = F.Quantity( 'Bio_MassConcentration', default=(3., 'g/L'), label='s<sub>1</sub><sup>[-τ<sub>1</sub>, 0]</sup>', description='historical initial condition for substrate-1 concentration' ) x1_hist_vals = F.Quantity( 'Bio_MassConcentration', default=(0.5, 'g/L'), label='x<sub>1</sub><sup>[-τ<sub>1</sub>, 0]</sup>', description='historical initial condition for bacteria-1 concentration' ) s2_hist_vals = F.Quantity( 'Bio_MassConcentration', default=(15., 'g/L'), label='s<sub>2</sub><sup>[-τ<sub>2</sub>, 0]</sup>', description='historical initial condition for substrate-2 concentration' ) x2_hist_vals = F.Quantity( 'Bio_MassConcentration', default=(0.1, 'g/L'), label='x<sub>2</sub><sup>[-τ<sub>2</sub>, 0]</sup>', description='historical initial condition for bacteria-2 concentration' ) parametersHistFG = F.FieldGroup( [s1_hist_vals, x1_hist_vals, s2_hist_vals, x2_hist_vals], label='Historical initial conditions') inputValuesSG = F.SuperGroup( [parametersMuFG, parametersFG, parametersTauFG, parametersHistFG], label="Input values") #1.2 Fields - Settings tFinal = F.Quantity('Bio_Time', default=(100, 'day'), minValue=(0, 'day'), maxValue=(1000, 'day'), label='simulation time') tPrint = F.Quantity('Bio_Time', default=(0.1, 'day'), minValue=(1e-5, 'day'), maxValue=(100, 'day'), label='print interval') absTol = F.Quantity('Bio_Time', default=(1e-12, 'day'), minValue=(1e-16, 'day'), maxValue=(1e-5, 'day'), label='absolute tolerance') relTol = F.Quantity('Bio_Time', default=(1e-12, 'day'), minValue=(1e-16, 'day'), maxValue=(1e-3, 'day'), label='relative tolerance') solverFG = F.FieldGroup([tFinal, tPrint, absTol, relTol], label='Solver') settingsSG = F.SuperGroup([solverFG], label='Settings') #1.4 Model view inputView = F.ModelView(ioType="input", superGroups=[inputValuesSG, settingsSG], autoFetch=True) #2. ############ Results ############### dataSeries = ( ('time', F.Quantity('Bio_Time', default=(1, 'day'))), ('s1', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))), ('x1', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))), ('s2', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))), ('x2', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))), ) plot = F.PlotView( dataSeries, label='Plot', options={ 'ylabel': None, 'title': 'Chemostat (DDE)' }, ) table = F.TableView(dataSeries, label='Table', options={ 'title': 'Title', 'formats': ['0.0000', '0.0000', '0.0000', '0.0000', '0.0000'] }) chartS1S2 = F.MPLPlot(label='Chart (s<sub>1</sub>, s<sub>2</sub>)') chartX1X2 = F.MPLPlot(label='Chart (x<sub>1</sub>, x<sub>2</sub>)') chartS1X1 = F.MPLPlot(label='Chart (s<sub>1</sub>, x<sub>1</sub>)') chartS2X2 = F.MPLPlot(label='Chart (s<sub>2</sub>, x<sub>2</sub>)') resultsVG = F.ViewGroup( [plot, table, chartS1S2, chartX1X2, chartS1X1, chartS2X2], label='Results') resultsSG = F.SuperGroup([resultsVG], label="Results") # 2.2 Equilibrium point s1_eqpnt = F.Quantity( 'Bio_MassConcentration', default=(0., 'g/L'), label='s<sub>1</sub><sup>*</sup>', description='substrate-1 concentration at the equilibrium point') x1_eqpnt = F.Quantity( 'Bio_MassConcentration', default=(0., 'g/L'), label='x<sub>1</sub><sup>*</sup>', description='bacteria-1 concentration at the equilibrium point') s2_eqpnt = F.Quantity( 'Bio_MassConcentration', default=(0., 'g/L'), label='s<sub>2</sub><sup>*</sup>', description='substrate-2 concentration at the equilibrium point') x2_eqpnt = F.Quantity( 'Bio_MassConcentration', default=(0., 'g/L'), label='x<sub>2</sub><sup>*</sup>', description='bacteria-1 concentration at the equilibrium point') equilibriumPointFG = F.FieldGroup([s1_eqpnt, x1_eqpnt, s2_eqpnt, x2_eqpnt], label='Equilibrium point') equilibriumPointSG = F.SuperGroup([equilibriumPointFG], label='Equilibrium point') #2.1 Model view resultView = F.ModelView(ioType="output", superGroups=[resultsSG, equilibriumPointSG]) ############# Page structure ######## modelBlocks = [inputView, resultView] def __init__(self): self.k1 = 10.53 self.k2 = 28.6 self.k3 = 1074 self.s1_in = 7.5 self.s2_in = 75 self.a = 0.5 self.m1 = 1.2 self.m2 = 0.74 self.k_s1 = 7.1 self.k_s2 = 9.28 self.k_I = 16 self.D = 0.85 self.tau1 = 2 self.tau2 = 7 self.s1_hist_vals = 2 self.x1_hist_vals = 0.1 self.s2_hist_vals = 10 self.x2_hist_vals = 0.05 def compute(self): chemostatDDE = DM.ChemostatDDE1(self) chemostatDDE.run(self) res = chemostatDDE.getResults() results = np.array( [res['t'], res['s1'], res['x1'], res['s2'], res['x2']]).transpose() self.plot = results self.table = results chemostatDDE.plotS1S2(self.chartS1S2) chemostatDDE.plotX1X2(self.chartX1X2) chemostatDDE.plotS1X1(self.chartS1X1) chemostatDDE.plotS2X2(self.chartS2X2) self.s1_eqpnt = (chemostatDDE.equilibriumPoint[0], 'kg/m**3') self.x1_eqpnt = (chemostatDDE.equilibriumPoint[1], 'kg/m**3') self.s2_eqpnt = (chemostatDDE.equilibriumPoint[2], 'kg/m**3') self.x2_eqpnt = (chemostatDDE.equilibriumPoint[3], 'kg/m**3')
class BiochemicalReactions(NumericalModel): label = "Biochemical reactions" description = F.ModelDescription( "Solver for elementary biochemical reactions", show=True) figure = F.ModelFigure( src="BioReactors/img/ModuleImages/BiochemicalReactions.png", show=False) async = True progressOptions = {'suffix': 's', 'fractionOutput': True} #1. ############ Inputs ############### #1.1 Fields - Input values reactions = F.RecordArray( ( ('reactionEquation', F.String('E + S <-> ES', label='Equation', inputBoxWidth=160)), ('rateConstnats', F.String('0.0, 0.0', label='Rate constants', inputBoxWidth=100)), ('reactionName', F.String('enzyme binding to a substrate', label='Name', inputBoxWidth=400, showTooltip=True)), ), toggle=False, label='Reactions', numRows=2, description='Biochemical reactions', ) reactionsFG = F.FieldGroup([reactions], hideContainer=True, label="Reactions") reactionsSG = F.SuperGroup([reactionsFG], label="Reactions") species = F.RecordArray( ( ('speciesVariable', F.String('E', label='Variable', inputBoxWidth=70)), ('initialValue', F.Quantity('Bio_MolarConcentration', default=(1, 'M'), minValue=(0, 'M'), label='Initial value', inputBoxWidth=75)), ('speciesName', F.String('enzyme', label='Name', inputBoxWidth=270)), ), toggle=False, label='species', numRows=4, description='Species of the reactions', ) speciesFG = F.FieldGroup([species], hideContainer=True, label="Species") speciesSG = F.SuperGroup([speciesFG], label="Species") #1.2 Fields - Settings tFinal = F.Quantity('Time', default=(0.0, 's'), minValue=(0, 's'), maxValue=(1000, 's'), label='simulation time') tPrint = F.Quantity('Time', default=(0.0, 's'), minValue=(1e-3, 's'), maxValue=(100, 's'), label='print interval') solverFG = F.FieldGroup([tFinal, tPrint], label='Solver') settingsSG = F.SuperGroup([solverFG], label='Settings') #1.4 Model view exampleAction = A.ServerAction( "loadEg", label="Examples", options=( ('exampleMMK', 'Michaelis-Menten kinetics'), ('exampleMMKI', 'Michaelis-Menten kinetics with inhibition'), )) inputView = F.ModelView( ioType="input", superGroups=[reactionsSG, speciesSG, settingsSG], autoFetch=True, actionBar=A.ActionBar([exampleAction]), ) #2. ############ Results ############### storage = F.HdfStorage(hdfFile=DM.dataStorageFilePath, hdfGroup=DM.dataStorageDatasetPath) dataSeries = ( ('time', F.Quantity('Time', default=(1, 's'))), ('E', F.Quantity('Bio_MolarConcentration', default=(1, 'M'))), ) plot = F.PlotView(dataSeries, label='Plot', useHdfStorage=True, storage='storage') table = F.TableView( dataSeries, label='Table', options={'formats': ['0.000', '0.000', '0.000', '0.000']}, useHdfStorage=True, storage='storage') storageVG = F.ViewGroup([storage], show="false") resultsVG = F.ViewGroup([plot, table], label='Results') resultsSG = F.SuperGroup([resultsVG, storageVG], label='Results') # 2.2 ODEs plot odesPlot = F.MPLPlot(label='ODEs') odesVG = F.ViewGroup([odesPlot], label='Ordinary differential equations') odesSG = F.SuperGroup([odesVG], label='ODEs') # 2.3 Results plot chartPlot = F.MPLPlot(label='Chart') chartPlotVG = F.ViewGroup([chartPlot], label='Chart') chartPlotSG = F.SuperGroup([chartPlotVG], label='Chart') #2.1 Model view resultView = F.ModelView(ioType="output", superGroups=[resultsSG, odesSG, chartPlotSG], keepDefaultDefs=True) ############# Page structure ######## modelBlocks = [inputView, resultView] def __init__(self): self.exampleMMK() def exampleMMK(self): self.species[0] = ('E', 4.0, 'enzyme') self.species[1] = ('S', 8.0, 'substrate') self.species[2] = ('ES', 0.0, 'complex') self.species[3] = ('P', 0.0, 'product') self.reactions[0] = ( 'E + S <=> ES', '2.0, 1.0', 'an enzyme binding to a substrate form an enzyme-substrate complex (reversible process)' ) self.reactions[1] = ( 'ES -> E + P', '1.5', 'an enzyme-substrate complex decomposition to a product and the enzyme' ) self.tFinal = 20.0 self.tPrint = 0.01 def exampleMMKI(self): self.species.resize(6) self.species[0] = ('E', 4.0, 'enzyme') self.species[1] = ('S', 8.0, 'substrate') self.species[2] = ('ES', 0.0, 'enzyme-substrate complex') self.species[3] = ('P', 0.0, 'product') self.species[4] = ('I', 5.0, 'inhibitor') self.species[5] = ('EI', 0.0, 'inactive enzyme-inhibitor complex') self.reactions.resize(3) self.reactions[0] = ( 'E + S <=> ES', '2.0, 1.0', 'an enzyme binding to a substrate form an enzyme-substrate complex (reversible process)' ) self.reactions[1] = ( 'ES -> E + P', '1.5', 'an enzyme-substrate complex decomposition to a product and an enzyme' ) self.reactions[2] = ( 'E + I <=> EI', '2.0, 0.5', 'an inhibitor binding to the active site of an enzyme form an inactive enzyme-inhibitor complex (reversible processes)' ) self.tFinal = 20.0 self.tPrint = 0.01 def computeAsync(self): # Redefine some fields self.redefineFileds() # Create the model model = DM.BiochemicalReactions(self) # Tun simulation model.prepareSimulation() model.run(self) # Show results self.storage = model.resultStorage.simulationName # Plot results model.plotODEsTxt(self.odesPlot) model.plotHDFResults(self.chartPlot) def redefineFileds(self): # Create tuples for species speciesTuples = (('time', F.Quantity('Time', default=(1, 's'))), ) datasetColumns = ['t'] for itSpecies in self.species: X = itSpecies[0] #species variable speciesTuple = (('%s' % X, F.Quantity('Bio_MolarConcentration', default=(1, 'M'))), ) speciesTuples += speciesTuple datasetColumns.append('%s' % X) # Redefine Fields redefinedStorage = F.HdfStorage( hdfFile='BioReactors_SimulationResults.h5', hdfGroup='/BiochemicalReactions', datasetColumns=datasetColumns) self.redefineField('storage', 'storageVG', redefinedStorage) redefinedPlot = F.PlotView(speciesTuples, label='Plot', options={ 'ylabel': 'concentration', 'title': '' }, useHdfStorage=True, storage='storage') self.redefineField('plot', 'resultsVG', redefinedPlot) redefinedTable = F.TableView( speciesTuples, label='Table', options={'formats': ['0.000', '0.000', '0.000', '0.000']}, useHdfStorage=True, storage='storage') self.redefineField('table', 'resultsVG', redefinedTable)
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
class ChemostatSimple(NumericalModel): label = "Simple Chemostat" description = F.ModelDescription( "Simple chemostat model with ordinary differential equations (ODE)", show=True) figure = F.ModelFigure( src="BioReactors/img/ModuleImages/SimpleChemostat.png", show=False) async = True progressOptions = {'suffix': 'day', 'fractionOutput': True} #1. ############ Inputs ############### #1.1 Fields - Input values S_in = F.Quantity('Bio_MassConcentration', default=(5, 'g/L'), minValue=(0, 'g/L'), label='S<sub>in</sub>', description='input substrate concentration') X_in = F.Quantity('Bio_MassConcentration', default=(0.0, 'g/L'), minValue=(0, 'g/L'), label='X<sub>in</sub>', description='input microorganisms concentration') m = F.Quantity( 'Bio_TimeRate', default=(3, '1/day'), minValue=(0, '1/day'), label='m', description='maximum specific growth rate of the microorganisms') K = F.Quantity('Bio_MassConcentration', default=(3.7, 'g/L'), minValue=(0, 'g/L'), label='K', description='half saturation constant') gamma = F.Quantity(default=0.6, minValue=0, maxValue=1.0, label='γ', description='yield coefficient of microorganisms') D_vals = F.RecordArray(( ('time', F.Quantity('Bio_Time', default=(20, 'day'), minValue=(0, 'day'), label='Duration')), ('D', F.Quantity('Bio_TimeRate', default=(1, '1/day'), minValue=(0, '1/day'), label='D')), ), label='D', description='dilution (or washout) rate') parametersFG = F.FieldGroup([S_in, X_in, m, K, gamma, D_vals], label="Parameters") S0 = F.Quantity('Bio_MassConcentration', default=(0, 'g/L'), minValue=0, label='S<sub>0</sub>', description='initial substrate concentration') X0 = F.Quantity('Bio_MassConcentration', default=(0.5, 'g/L'), minValue=0, label='X<sub>0</sub>', description='initial microorganisms concentration') initialValuesFG = F.FieldGroup([S0, X0], label="Initial values") inputValuesSG = F.SuperGroup([parametersFG, initialValuesFG], label="Input values") #1.2 Fields - Settings tFinal = F.Quantity('Bio_Time', default=(100, 'day'), minValue=(0, 'day'), maxValue=(1000, 'day'), label='simulation time') tPrint = F.Quantity('Bio_Time', default=(0.1, 'day'), minValue=(1e-5, 'day'), maxValue=(100, 'day'), label='print interval') solverFG = F.FieldGroup([tFinal, tPrint], label='Solver') settingsSG = F.SuperGroup([solverFG], label='Settings') #1.4 Model view inputView = F.ModelView(ioType="input", superGroups=[inputValuesSG, settingsSG], autoFetch=True) #2. ############ Results ############### storage = F.HdfStorage(hdfFile=DM.dataStorageFilePath, hdfGroup=DM.dataStorageDatasetPath) dataSeries = (('time', F.Quantity('Bio_Time', default=(1, 'day'))), ('S', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))), ('X', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))), ('D', F.Quantity('Bio_TimeRate', default=(1, '1/day')))) plot = F.PlotView(dataSeries, label='Plot', options={'ylabel': None}, useHdfStorage=True, storage='storage') table = F.TableView(dataSeries, label='Table', options={ 'title': 'Simple Chemostat', 'formats': ['0.000', '0.000', '0.000', '0.000'] }, useHdfStorage=True, storage='storage') resultsVG = F.ViewGroup([plot, table], label='Results') storageVG = F.ViewGroup([storage], show="false") resultsSG = F.SuperGroup([resultsVG, storageVG]) #2.1 Model view resultView = F.ModelView(ioType="output", superGroups=[resultsSG]) ############# Page structure ######## modelBlocks = [inputView, resultView] def computeAsync(self): # Create the model chemostat = DM.ChemostatSimple(self) # Run simulation chemostat.prepareSimulation() chemostat.run(self) # Show results self.storage = chemostat.resultStorage.simulationName