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 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 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 SolverSettings(NumericalModel): tFinal = F.Quantity('Bio_Time', default=(0.0, 'day'), minValue=(0, 'day'), maxValue=(1000, 'day'), label='simulation time') tPrint = F.Quantity('Bio_Time', default=(0.0, 'day'), minValue=(1e-5, 'day'), maxValue=(100, 'day'), label='print interval') absTol = F.Quantity('Bio_Time', default=(0.0, 'day'), minValue=(1e-16, 'day'), maxValue=(1e-5, 'day'), label='absolute tolerance') relTol = F.Quantity('Bio_Time', default=(0.0, 'day'), minValue=(1e-16, 'day'), maxValue=(1e-3, 'day'), label='relative tolerance') FG = F.FieldGroup([tFinal, tPrint, absTol, relTol], label='Solver') SG = F.SuperGroup([FG], label='Settings') modelBlocks = []
class CycleIterator(NumericalModel): hTolerance = F.Quantity('SpecificEnthalpy', default=1.0, label='enthalpy tolerance') maxNumIter = F.Quantity(default=500, label='max iterations', description='maximum number of iterations') convSettings = F.FieldGroup([hTolerance, maxNumIter], label='Convergence settings') solverSettings = F.SuperGroup([convSettings], label='Solver') modelBlocks = [] def run(self): self.converged = False self.ncp = len(self.cycle.fp) self.old_h = np.zeros((self.ncp)) self.old_T = np.zeros((self.ncp)) self.old_p = np.zeros((self.ncp)) self.hHistory = [] self.change_h = np.zeros((self.ncp)) self.change_hHistory = [] i = 0 self.cycle.computeCycle() while (i < self.maxNumIter): self.saveOldValues() self.cycle.computeCycle() self.checkConvergence() if (self.converged): break i += 1 if (not self.converged): raise E.ConvergenceError( 'Solution did not converge, delta_h = {:e}'.format( self.change_hHistory[-1])) def saveOldValues(self): for i in range(self.ncp): self.old_h[i] = self.cycle.fp[i].h self.old_T[i] = self.cycle.fp[i].T self.old_p[i] = self.cycle.fp[i].p #self.hHistory.append(self.old_h) def computeChange(self): for i in range(self.ncp): self.change_h[i] = self.cycle.fp[i].h - self.old_h[i] change = np.sqrt(np.sum(self.change_h**2)) self.hHistory.append([x for x in self.change_h]) self.change_hHistory.append(change) return change def checkConvergence(self): self.converged = self.computeChange() < self.hTolerance return self.converged def printValues(self): print[fp.T for fp in self.cycle.fp] print[fl.mDot for fl in self.cycle.flows]
class Controller(NumericalModel): initialState = F.Choices( OrderedDict(( (TC.FUELING, 'fueling'), (TC.EXTRACTION, 'extraction'), )), label='start with', description='start the simulation with fueling or extraction') tWaitBeforeExtraction = F.Quantity( 'Time', default=(0., 's'), minValue=(0., 's'), maxValue=(1.e6, 's'), label='τ (extraction)', description='waiting time before each extraction') tWaitBeforeFueling = F.Quantity( 'Time', default=(0., 's'), minValue=(0., 's'), maxValue=(1.e6, 's'), label='τ (fueling)', description='waiting time before each fueling') pMin = F.Quantity( 'Pressure', default=(0., 'bar'), label='minimum pressure</sub>', description='minimum pressure in the gas storage for starting fueling') pMax = F.Quantity( 'Pressure', default=(0., 'bar'), label='maximum pressure', description='maximum pressure in the gas storage for stopping fueling') mDotExtr = F.Quantity('MassFlowRate', default=(0., 'kg/h'), label='extraction mass flow rate', description='extraction mass flow rate') nCompressor = F.Quantity('AngularVelocity', default=(0., 'rev/s'), minValue=(0, 'rev/s'), maxValue=(1e4, 'rev/s'), label='compressor speed', description='compressor speed') FG = F.FieldGroup([ initialState, pMin, pMax, mDotExtr, nCompressor, tWaitBeforeExtraction, tWaitBeforeFueling ], label='Parameters') SG = F.SuperGroup([FG], label="Parameters") modelBlocks = []
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 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 Tank(NumericalModel): wallArea = F.Quantity('Area', default=(0., 'm**2'), label='wall area', description='wall area') volume = F.Quantity('Volume', default=(0., 'L'), maxValue=(1e6, 'L'), label='volume', description='volume') TInit = F.Quantity('Temperature', default=(0., 'degC'), label='initial temperature', description='initial temperature') pInit = F.Quantity('Pressure', default=(0., 'bar'), label='initial pressure', description='initial pressure') linerMaterial = F.ObjectReference(Solids, default='StainlessSteel304', label='liner material', description='liner material') linerThickness = F.Quantity('Length', default=(0., 'm'), minValue=(0, 'm'), label='liner thickness', description='liner thickness') compositeMaterial = F.ObjectReference(Solids, default='StainlessSteel304', label='composite material', description='composite material') compositeThickness = F.Quantity('Length', default=(0., 'm'), minValue=(0, 'm'), label='composite thickness', description='composite thickness') hConvExternal = F.Quantity( 'HeatTransferCoefficient', default=(0., 'W/m**2-K'), label='h<sub>conv</sub> (external)</sub>', description='external convection coefficient to ambient') hConvInternalWaiting = F.Quantity( 'HeatTransferCoefficient', default=(0., 'W/m**2-K'), label='h<sub>conv</sub> (internal,waiting)</sub>', description='internal convection coefficient during waiting time') hConvInternalExtraction = F.Quantity( 'HeatTransferCoefficient', default=(0., 'W/m**2-K'), label='h<sub>conv</sub> (internal, extraction)</sub>', description='internal convection coefficient during extraction') hConvInternalFueling = F.Quantity( 'HeatTransferCoefficient', default=(0., 'W/m**2-K'), label='h<sub>conv</sub> (internal, fueling)', description='internal convection coefficient during refueling') initialValuesFG = F.FieldGroup([wallArea, volume, TInit, pInit], label='Initial values') convectionFG = F.FieldGroup([ hConvExternal, hConvInternalWaiting, hConvInternalExtraction, hConvInternalFueling ], label='Convection') linerFG = F.FieldGroup([linerMaterial, linerThickness], label='Liner') compositeFG = F.FieldGroup([compositeMaterial, compositeThickness], label='Composite') SG = F.SuperGroup([initialValuesFG, convectionFG, linerFG, compositeFG], label="Parameters") modelBlocks = []
class CycleDiagram(NumericalModel): #================ Inputs ================# enable = F.Boolean(label='create process diagram', default=True) isotherms = F.Boolean(label='isotherms') temperatureUnit = F.Choices(OrderedDict((('K', 'K'), ('degC', 'degC'))), default='K', label="temperature unit", show="self.isotherms == true") isochores = F.Boolean(label='isochores') isentrops = F.Boolean(label='isentrops') qIsolines = F.Boolean(label='vapor quality isolines') diagramInputs = F.FieldGroup( [enable, isotherms, temperatureUnit, isochores, isentrops, qIsolines], label='Diagram') defaultMaxP = F.Boolean(label='default max pressure') defaultMaxT = F.Boolean(label='default max temperature') maxPressure = F.Quantity('Pressure', default=(1, 'bar'), label='max pressure', show="self.defaultMaxP == false") maxTemperature = F.Quantity('Temperature', default=(300, 'K'), label='max temperature', show="self.defaultMaxT == false") boundaryInputs = F.FieldGroup( [defaultMaxP, defaultMaxT, maxPressure, maxTemperature], label='Value Limits') inputs = F.SuperGroup([diagramInputs, boundaryInputs], label='Diagram settings') modelBlocks = [] def draw(self, fluid, fluidPoints, cycleLines): # Create diagram object diagram = PHDiagram(fluid.name, temperatureUnit=self.temperatureUnit) # Set limits pMax, TMax = None, None if not self.defaultMaxP: pMax = self.maxPressure if not self.defaultMaxT: TMax = self.maxTemperature diagram.setLimits(pMax=pMax, TMax=TMax) fig = diagram.draw(isotherms=self.isotherms, isochores=self.isochores, isentrops=self.isentrops, qIsolines=self.qIsolines) ax = fig.get_axes()[0] # Draw points i = 1 for fp in fluidPoints: ax.semilogy(fp.h / 1e3, fp.p / 1e5, 'ko') ax.annotate('{}'.format(i), xy=(fp.h / 1e3, fp.p / 1e5), xytext=(3, 2), textcoords='offset points', size='x-large') i += 1 # Draw lines for (fp1, fp2) in cycleLines: ax.semilogy([fp1.h / 1e3, fp2.h / 1e3], [fp1.p / 1e5, fp2.p / 1e5], 'k', linewidth=2) # Export diagram to file fHandle, resourcePath = diagram.export(fig) os.close(fHandle) return resourcePath
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 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 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 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 CH4Bioreactor(NumericalModel): # Stoichiometric parameter values Y_ac = F.Quantity(default=0.0, minValue=0.0, label='Y<sub>ac</sub>', description='yield of acetate degraders on acetate') stoichiometricParametersFG = F.FieldGroup( [ Y_ac, ], label='Stoichiometric parameters (R-CH4)') # Biochemical parameter values k_m_ac = F.Quantity( 'Bio_TimeRate', default=(0.0, '1/day'), minValue=(0, '1/day'), label='k<sub>m,ac</sub>', description='specific Monod maximum uptake rate of acetate degraders') K_S_ac = F.Quantity( 'Bio_MassConcentration', default=(0.0, 'g/L'), minValue=(0, 'g/L'), label='K<sub>S,ac</sub>', description='Monod half saturation constant of acetate degraders') biochemicalParametersFG = F.FieldGroup( [ k_m_ac, K_S_ac, ], label='Biochemical parameters (R-CH4)') # Physiochemical parameters Y_ch4_ac = F.Quantity( default=0.0, minValue=0.0, label='Y<sub>ch4,ac</sub>', description='yield coefficient of methane on acetate') physiochemicalParametersFG = F.FieldGroup( [Y_ch4_ac], label='Physiochemical parameters (R-CH4)') # Physical parameters V_liq_RCH4_del_V_liq_RH2 = F.Quantity( default=(0.0, '-'), label='V<sub>liq,RCH4</sub>/V<sub>liq,RH2</sub>', description= 'fraction between the volume of the liquid part of the H2 and CH4 bioreactors' ) physicalParametersFG = F.FieldGroup([V_liq_RCH4_del_V_liq_RH2], label='Physical parameters (R-CH4)') # Input concentrations of components X_ac_in = F.Quantity( 'Bio_MassConcentration', default=(0.0, 'g/L'), minValue=(0, 'g/L'), label='X<sub>ac</sub><sup> (in)</sup>', description='input concentration of acetate degraders') inputConcentrationsFG = F.FieldGroup([ X_ac_in, ], label='Input concentrations (R-CH4)') # Initial concentrations of components S_ac_0 = F.Quantity('Bio_CODConcentration', default=(0.0, 'gCOD/L'), minValue=(0, 'gCOD/L'), label='S<sub>ac</sub><sup> (0)</sup>', description='initial concentration of acetate') X_ac_0 = F.Quantity( 'Bio_MassConcentration', default=(0.0, 'g/L'), minValue=(0, 'g/L'), label='X<sub>ac</sub><sup> (0)</sup>', description='initial concentration of acetate degraders') initialConcentrationsFG = F.FieldGroup( [ S_ac_0, X_ac_0, ], label='Initial concentrations (R-CH4)') #1.2 Super groups - parameters & concentrations parametersSG = F.SuperGroup([ stoichiometricParametersFG, biochemicalParametersFG, physiochemicalParametersFG, physicalParametersFG, ], label="Parameters (R-CH4)") concentrationsSG = F.SuperGroup([ initialConcentrationsFG, inputConcentrationsFG, ], label="Concentrations (R-CH4)") modelBlocks = []
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 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 H2Bioreactor(NumericalModel): # Stoichiometric parameter values f_ch_xc = F.Quantity(default=0.0, minValue=0.0, label='f<sub>ch,xc</sub>', description='yield of carbohydrates on composites') f_pr_xc = F.Quantity(default=0.0, minValue=0.0, label='f<sub>pr,xc</sub>', description='yield of proteins on composites') f_li_xc = F.Quantity(default=0.0, minValue=0.0, label='f<sub>li,xc</sub>', description='yield of lipids on composites') f_su_li = F.Quantity(default=0.0, minValue=0.0, label='f<sub>su,li</sub>', description='yield of sugar on lipids') f_fa_li = F.Quantity(default=0.0, minValue=0.0, label='f<sub>fa,li</sub>', description='yield of LCFA on lipids') f_ac_su = F.Quantity(default=0.0, minValue=0.0, label='f<sub>ac,su</sub>', description='yield of acetate on sugars') f_ac_aa = F.Quantity(default=0.0, minValue=0.0, label='f<sub>ac,aa</sub>', description='yield of acetate on amino acids') Y_suaa = F.Quantity( default=0.0, minValue=0.0, label='Y<sub>su-aa</sub>', description= 'yield of sugar and amino acid degraders on sugars and amino acids') Y_fa = F.Quantity(default=0.0, minValue=0.0, label='Y<sub>fa</sub>', description='yield of LCFA degraders on LCFA') stoichiometricParametersFG = F.FieldGroup( [ f_ch_xc, f_pr_xc, f_li_xc, f_su_li, f_fa_li, f_ac_su, f_ac_aa, Y_suaa, Y_fa ], label='Stoichiometric parameters (R-H2)') # Biochemical parameter values k_dis = F.Quantity( 'Bio_TimeRate', default=(0.0, '1/day'), minValue=(0, '1/day'), label='k<sub>dis</sub>', description='rate coefficient for disintegration of composites') k_hyd_ch = F.Quantity( 'Bio_TimeRate', default=(0.0, '1/day'), minValue=(0, '1/day'), label='k<sub>hyd,ch</sub>', description='rate coefficient for hydrolysis of carbohydrates') k_hyd_pr = F.Quantity( 'Bio_TimeRate', default=(0.0, '1/day'), minValue=(0, '1/day'), label='k<sub>hyd,pr</sub>', description='rate coefficient for hydrolysis of proteins') k_hyd_li = F.Quantity( 'Bio_TimeRate', default=(0.0, '1/day'), minValue=(0, '1/day'), label='k<sub>hyd,li</sub>', description='rate coefficient for hydrolysis of lipids') k_m_suaa = F.Quantity( 'Bio_TimeRate', default=(0.0, '1/day'), minValue=(0, '1/day'), label='k<sub>m,su-aa</sub>', description= 'specific Monod maximum uptake rate of sugar and amino acids degraders' ) K_S_suaa = F.Quantity( 'Bio_MassConcentration', default=(0.0, 'g/L'), minValue=(0, 'g/L'), label='K<sub>S,su-aa</sub>', description= 'Monod half saturation constant of sugar and amino acids degraders') k_m_fa = F.Quantity( 'Bio_TimeRate', default=(0.0, '1/day'), minValue=(0, '1/day'), label='k<sub>m,fa</sub>', description='specific Monod maximum uptake rate of LCFA degraders') K_S_fa = F.Quantity( 'Bio_MassConcentration', default=(0.0, 'g/L'), minValue=(0, 'g/L'), label='K<sub>S,fa</sub>', description='Monod half saturation constant of LCFA degraders') biochemicalParametersFG = F.FieldGroup( [ k_dis, k_hyd_ch, k_hyd_pr, k_hyd_li, k_m_suaa, K_S_suaa, k_m_fa, K_S_fa, ], label='Biochemical parameters (R-H2)') # Physiochemical parameters Y_h2_su = F.Quantity(default=0.0, minValue=0.0, label='Y<sub>h2,su</sub>', description='yield coefficient of hydrogen on sugar') Y_h2_aa = F.Quantity( default=0.0, minValue=0.0, label='Y<sub>h2,aa</sub>', description='yield coefficient of hydrogen on amino acids') Y_h2_fa = F.Quantity(default=0.0, minValue=0.0, label='Y<sub>h2,fa</sub>', description='yield coefficient of hydrogen on LCFA') physiochemicalParametersFG = F.FieldGroup( [Y_h2_su, Y_h2_aa, Y_h2_fa], label='Physiochemical parameters (R-H2)') # Dilution rates D_liq_arr = F.RecordArray( ( ('time', F.Quantity('Bio_Time', default=(10.0, 'day'), minValue=(0, 'day'), label='Duration')), ('D', F.Quantity('Bio_TimeRate', default=(1.0, '1/day'), minValue=(0, '1/day'), label='dilution rate')), ), label='D<sub> liq</sub>', description='liquid dilution (or washout) rate', numRows=4, toggle=True, ) dilutionRatesFG = F.FieldGroup([ D_liq_arr, ], label='Dilution rates (R-H2)') # Input concentrations of components S_su_in = F.Quantity('Bio_CODConcentration', default=(0.0, 'gCOD/L'), minValue=(0, 'gCOD/L'), label='S<sub>su</sub><sup> (in)</sup>', description='input concentration of sugars') S_aa_in = F.Quantity('Bio_CODConcentration', default=(0.0, 'gCOD/L'), minValue=(0, 'gCOD/L'), label='S<sub>aa</sub><sup> (in)</sup>', description='input concentration of amino acids') S_fa_in = F.Quantity( 'Bio_CODConcentration', default=(0.0, 'gCOD/L'), minValue=(0, 'gCOD/L'), label='S<sub>fa</sub><sup> (in)</sup>', description='input concentration of fatty acids (LCFA)') S_ac_in = F.Quantity('Bio_CODConcentration', default=(0.0, 'gCOD/L'), minValue=(0, 'gCOD/L'), label='S<sub>ac</sub><sup> (in)</sup>', description='input concentration of acetate') X_c_in = F.Quantity('Bio_CODConcentration', default=(0.0, 'gCOD/L'), minValue=(0, 'gCOD/L'), label='X<sub>c</sub><sup> (in)</sup>', description='input concentration of composites') X_ch_in = F.Quantity('Bio_CODConcentration', default=(0.0, 'gCOD/L'), minValue=(0, 'gCOD/L'), label='X<sub>ch</sub><sup> (in)</sup>', description='input concentration of carbohydrates') X_pr_in = F.Quantity('Bio_CODConcentration', default=(0.0, 'gCOD/L'), minValue=(0, 'gCOD/L'), label='X<sub>pr</sub><sup> (in)</sup>', description='input concentration of proteins') X_li_in = F.Quantity('Bio_CODConcentration', default=(0.0, 'gCOD/L'), minValue=(0, 'gCOD/L'), label='X<sub>li</sub><sup> (in)</sup>', description='input concentration of lipids') X_suaa_in = F.Quantity( 'Bio_MassConcentration', default=(0.0, 'g/L'), minValue=(0, 'g/L'), label='X<sub>su-aa</sub><sup> (in)</sup>', description='input concentration of sugar and amino acids degraders') X_fa_in = F.Quantity('Bio_MassConcentration', default=(0.0, 'g/L'), minValue=(0, 'g/L'), label='X<sub>fa</sub><sup> (in)</sup>', description='input concentration of LCFA degraders') inputConcentrationsFG = F.FieldGroup([ S_su_in, S_aa_in, S_fa_in, S_ac_in, X_c_in, X_ch_in, X_pr_in, X_li_in, X_suaa_in, X_fa_in, ], label='Input concentrations (R-H2)') # Initial concentrations of components S_su_0 = F.Quantity('Bio_CODConcentration', default=(0.0, 'gCOD/L'), minValue=(0, 'gCOD/L'), label='S<sub>su</sub><sup> (0)</sup>', description='initial concentration of sugars') S_aa_0 = F.Quantity('Bio_CODConcentration', default=(0.0, 'gCOD/L'), minValue=(0, 'gCOD/L'), label='S<sub>aa</sub><sup> (0)</sup>', description='initial concentration of amino acids') S_fa_0 = F.Quantity( 'Bio_CODConcentration', default=(0.0, 'gCOD/L'), minValue=(0, 'gCOD/L'), label='S<sub>fa</sub><sup> (0)</sup>', description='initial concentration of fatty acids (LCFA)') S_ac_0 = F.Quantity('Bio_CODConcentration', default=(0.0, 'gCOD/L'), minValue=(0, 'gCOD/L'), label='S<sub>ac</sub><sup> (0)</sup>', description='initial concentration of acetate') X_c_0 = F.Quantity('Bio_CODConcentration', default=(0.0, 'gCOD/L'), minValue=(0, 'gCOD/L'), label='X<sub>c</sub><sup> (0)</sup>', description='initial concentration of composites') X_ch_0 = F.Quantity('Bio_CODConcentration', default=(0.0, 'gCOD/L'), minValue=(0, 'gCOD/L'), label='X<sub>ch</sub><sup> (0)</sup>', description='initial concentration of carbohydrates') X_pr_0 = F.Quantity('Bio_CODConcentration', default=(0.0, 'gCOD/L'), minValue=(0, 'gCOD/L'), label='X<sub>pr</sub><sup> (0)</sup>', description='initial concentration of proteins') X_li_0 = F.Quantity('Bio_CODConcentration', default=(0.0, 'gCOD/L'), minValue=(0, 'gCOD/L'), label='X<sub>li</sub><sup> (0)</sup>', description='initial concentration of lipids') X_suaa_0 = F.Quantity( 'Bio_MassConcentration', default=(0.0, 'g/L'), minValue=(0, 'g/L'), label='X<sub>su-aa</sub><sup> (0)</sup>', description='initial concentration of sugar and amino acids degraders') X_fa_0 = F.Quantity('Bio_MassConcentration', default=(0.0, 'g/L'), minValue=(0, 'g/L'), label='X<sub>fa</sub><sup> (0)</sup>', description='initial concentration of LCFA degraders') initialConcentrationsFG = F.FieldGroup( [ S_su_0, S_aa_0, S_fa_0, S_ac_0, X_c_0, X_ch_0, X_pr_0, X_li_0, X_suaa_0, X_fa_0, ], label='Initial concentrations (R-H2)') #1.2 Super groups - parameters & concentrations parametersSG = F.SuperGroup([ stoichiometricParametersFG, biochemicalParametersFG, physiochemicalParametersFG, dilutionRatesFG, ], label="Parameters (R-H2)") concentrationsSG = F.SuperGroup([ initialConcentrationsFG, inputConcentrationsFG, ], label="Concentrations (R-H2)") modelBlocks = []
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 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 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