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 FiniteVolumeSolverSettings(NumericalModel): tolerance = F.Quantity(default=1e-6, label='tolerance') maxNumIterations = F.Integer(default=100, label='max number iterations') relaxationFactor = F.Quantity(default=1.0, label='relaxation factor') FG = F.FieldGroup([tolerance, maxNumIterations, relaxationFactor], label='Settings') modelBlocks = []
class TankRes(NumericalModel): compositeMass = F.Quantity('Mass', default=(0., 'kg'), label='composite mass', description='composite mass') linerMass = F.Quantity('Mass', default=(0., 'kg'), label='liner mass', description='liner mass') FG = F.FieldGroup([linerMass, compositeMass], label='Pressure vessel') modelBlocks = []
class BlockGeometry(NumericalModel): diameter = F.Quantity('Length', default=(0., 'mm'), label='diameter', description='block diameter') length = F.Quantity('Length', default=(0., 'm'), label='length', description='block length') FG = F.FieldGroup([diameter, length], label='Block geometry') modelBlocks = []
class FluidSource_TP(CycleComponent): T = F.Quantity('Temperature', label='temperature') p = F.Quantity('Pressure', label='pressure') mDot = F.Quantity('MassFlowRate', label='mass flow rate') FG = F.FieldGroup([T, p, mDot], label='Compressor') modelBlocks = [] #================== Ports =================# outlet = F.Port(P.ThermodynamicPort) #================== Methods =================# def compute(self): self.outlet.flow.mDot = self.mDot self.outlet.state.update_Tp(self.T, self.p)
class IsobaricHeatExchanger(CycleComponent2FlowPorts): etaThermal = F.Quantity(default=1.0, label='thermal efficiency', minValue=0, maxValue=1, show='self.computeMethod == "eta"') TExt = F.Quantity('Temperature', default=(30, 'degC'), label='external temperature', show='self.computeMethod == "eta"') qOutlet = F.Quantity(default=0, minValue=0, maxValue=1, label='outlet vapor quality', show='self.computeMethod == "Q"') TOutlet = F.Quantity('Temperature', default=(40, 'degC'), label='outlet temperature', show='self.computeMethod == "T"') hOutlet = F.Quantity('SpecificEnthalpy', default=(100, 'kJ/kg'), minValue=-1e10, label='outlet enthalpy', show='self.computeMethod == "H"') modelBlocks = [] #================== Methods =================# def compute(self): CycleComponent2FlowPorts.compute(self) pIn = self.inlet.state.p if (self.computeMethod == 'eta'): self.outlet.state.update_Tp(self.TExt, pIn) hOut = ( 1 - self.etaThermal ) * self.inlet.state.h + self.etaThermal * self.outlet.state.h self.outlet.state.update_ph(pIn, hOut) elif (self.computeMethod == 'dT'): self.outlet.state.update('P', pIn, 'dT', self.dTOutlet) elif (self.computeMethod == 'Q'): self.outlet.state.update_pq(pIn, self.qOutlet) elif (self.computeMethod == 'T'): self.outlet.state.update_Tp(self.TOutlet, pIn) elif (self.computeMethod == 'H'): self.outlet.state.update_ph(pIn, self.hOutlet) else: raise ValueError('Unknown compute method {}'.format( self.computeMethod)) self.qIn = self.outlet.state.h - self.inlet.state.h self.delta_h = self.qIn self.w = 0
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 Compressor(NumericalModel): etaS = F.Quantity('Efficiency', default=(0., '-'), label='isentropic efficiency', description='isentropic efficiency') fQ = F.Quantity('Fraction', default=(0., '-'), label='heat loss fraction', description='heat loss fraction to ambient') V = F.Quantity('Volume', default=(0., 'L'), maxValue=(1e6, 'L'), label='displacement volume', description='displacement volume') FG = F.FieldGroup([etaS, fQ, V], label='Parameters') modelBlocks = []
class FluidStateSource(NumericalModel): sourceTypeTxt = F.Choices(OrderedDict(( ('TP', 'temperature, pressure'), ('PQ', 'pressure, vapour quality'), ('TQ', 'temperature, vapour quality'), )), label='state variables', description='choose the initial state variables') @property def sourceType(self): if self.sourceTypeTxt == 'TP': return DM.FluidStateSource.TP if self.sourceTypeTxt == 'PQ': return DM.FluidStateSource.PQ if self.sourceTypeTxt == 'TQ': return DM.FluidStateSource.TQ else: raise ValueError('Unsupported source type of FluidStateSource.') T = F.Quantity( 'Temperature', default=(0., 'degC'), label='temperature', description='temperature', show="self.sourceTypeTxt == 'TP' || self.sourceTypeTxt == 'TQ'") p = F.Quantity( 'Pressure', default=(0., 'bar'), label='pressure', description='pressure', show="self.sourceTypeTxt == 'TP' || self.sourceTypeTxt == 'PQ'") q = F.Quantity( 'VaporQuality', default=(0., '-'), minValue=0, maxValue=1, label='vapour quality', description='vapour quality', show="self.sourceTypeTxt == 'TQ' || self.sourceTypeTxt == 'PQ'") FG = F.FieldGroup([sourceTypeTxt, T, p, q], label='Parameters') modelBlocks = []
class Compressor(CycleComponent2FlowPorts): modelType = F.Choices(OrderedDict(( ('S', 'isentropic'), ('T', 'isothermal'), )), label='compressor model') etaS = F.Quantity('Efficiency', label='isentropic efficiency', show="self.modelType == 'S'") fQ = F.Quantity('Fraction', default=0., label='heat loss factor', show="self.modelType == 'S'") etaT = F.Quantity('Efficiency', label='isosthermal efficiency', show="self.modelType == 'T'") dT = F.Quantity('TemperatureDifference', default=0, label='temperature increase', show="self.modelType == 'T'") FG = F.FieldGroup([modelType, etaS, fQ, etaT, dT], label='Compressor') modelBlocks = [] #================== Methods =================# def compute(self, pOut): CycleComponent2FlowPorts.compute(self) if (pOut < self.inlet.state.p): raise ValueError( 'Outlet pressure must be higher than inlet pressure') if (self.modelType == 'S'): self.outlet.state.update_ps(pOut, self.inlet.state.s) wIdeal = self.outlet.state.h - self.inlet.state.h self.w = wIdeal / self.etaS self.qIn = -self.fQ * self.w self.delta_h = self.w + self.qIn self.outlet.state.update_ph(pOut, self.inlet.state.h + self.delta_h) else: self.outlet.state.update_Tp(self.inlet.state.T + self.dT, pOut) self.qIn = (self.outlet.state.s - self.inlet.state.s) * self.inlet.state.T wIdeal = self.outlet.state.h - self.inlet.state.h - self.qIn self.w = wIdeal / self.etaT self.qIn -= (self.w - wIdeal)
class SectionResultsSettings(NumericalModel): setTRange = F.Boolean( False, label='set temperature range', description= 'set temperature range (Tmin, Tmax) of the section results plots') Tmin = F.Quantity('Temperature', default=(0, 'K'), label='min temperature', description='minimum temperature', show='self.setTRange') Tmax = F.Quantity('Temperature', default=(0, 'K'), label='max temperature', description='maximum temperature', show='self.setTRange') FG = F.FieldGroup([setTRange, Tmin, Tmax], label='Settings') modelBlocks = []
class FlowSplitter(CycleComponent): frac1 = F.Quantity('Fraction', label='fraction to outlet 1') frac2 = F.Quantity('Fraction', label='fraction to outlet 2') FG = F.FieldGroup([frac1, frac2]) modelBlocks = [] #================== Ports =================# inlet = F.Port(P.ThermodynamicPort) outlet1 = F.Port(P.ThermodynamicPort) outlet2 = F.Port(P.ThermodynamicPort) #================== Methods =================# def compute(self): self.outlet1.flow.mDot = self.inlet.flow.mDot * self.frac1 / ( self.frac1 + self.frac2) self.outlet2.flow.mDot = self.inlet.flow.mDot * self.frac2 / ( self.frac1 + self.frac2) self.outlet1.state.update_Trho(self.inlet.state.T, self.inlet.state.rho) self.outlet2.state.update_Trho(self.inlet.state.T, self.inlet.state.rho)
class LiquefactionCycle(ThermodynamicalCycle): abstract = True #================ Inputs ================# fluidName = F.Choices(Fluids, default='R134a', label='liquefied fluid') mDot = F.Quantity('MassFlowRate', default=(1, 'kg/min'), label='inlet flow rate') pIn = F.Quantity('Pressure', default=(1, 'bar'), label='inlet gas pressure') TIn = F.Quantity('Temperature', default=(15, 'degC'), label='inlet gas temperature') pHigh = F.Quantity('Pressure', default=(40, 'bar'), label='compressor high pressure') pLiquid = F.Quantity('Pressure', default=(2, 'bar'), label='liquid pressure') TAmbient = F.Quantity( 'Temperature', default=(15, 'degC'), label='ambient temperature', description='used as reference temperature to calculate exergy') workingFluidGroup = F.FieldGroup( ['fluidName', 'mDot', pIn, TIn, pHigh, pLiquid, TAmbient], label='Cycle parameters') #================ Results ================# liqEnergy = F.Quantity('SpecificEnergy', default=(1, 'kJ/kg'), label='liquefaction energy') minLiqEnergy = F.Quantity( 'SpecificEnergy', default=(1, 'kJ/kg'), label='min. liquefaction energy', description= 'minimum energy required for liquefaction in an ideal carnot cycle; \ equal to the difference in exergies between initial and final state') etaSecondLaw = F.Quantity( 'Efficiency', label='figure of merit (FOM)', description= 'minimum energy required for liquefaction to the actual energy required \ in the cycle; equivalent to second law efficiency') efficiencyFieldGroup = F.FieldGroup( [liqEnergy, minLiqEnergy, etaSecondLaw], label='Efficiency')
class CycleComponent2FlowPorts(CycleComponent): abstract = True 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)') #================== Ports =================# inlet = F.Port(P.ThermodynamicPort) outlet = F.Port(P.ThermodynamicPort) #================== Methods =================# def compute(self): self.outlet.flow.mDot = self.inlet.flow.mDot def postProcess(self): self.WDot = self.w * self.inlet.flow.mDot self.QDotIn = self.qIn * self.inlet.flow.mDot self.deltaHDot = self.delta_h * self.inlet.flow.mDot
class Cooler(NumericalModel): workingState = F.Choices(OrderedDict(( (0, 'no'), (1, 'yes'), )), label='enable cooler', description='enable cooler') epsilon = F.Quantity('Efficiency', default=(0., '-'), label='effectiveness', description='effectiveness', show="self.workingState") TCooler = F.Quantity('Temperature', default=(0., 'degC'), label='coolant temperature', description='coolant temperature', show="self.workingState") FG = F.FieldGroup([workingState, epsilon, TCooler], label='Parameters') modelBlocks = []
class HeatFlowChannels(NumericalModel): QDotPrimaryChannels = F.Quantity( 'HeatFlowRate', default=(1.0, 'kW'), label='Primary channels', description='heat flow rate to the primary channels') QDotSecondaryChannels = F.Quantity( 'HeatFlowRate', default=(1.0, 'kW'), label='Secondary channels', description='heat flow rate to the secondary channels') QDotExternalChannel = F.Quantity( 'HeatFlowRate', default=(1.0, 'kW'), label='External channel', description='heat flow rate from the external channel') FG = F.FieldGroup( [QDotPrimaryChannels, QDotSecondaryChannels, QDotExternalChannel], label='Parameters') modelBlocks = []
class BlockProperties(NumericalModel): material = F.ObjectReference(Solids, default='Aluminium6061', label='material', description='block material') divisionStep = F.Quantity('Length', default=(0., 'm'), minValue=(1, 'mm'), label='division step (axial)', description='axial division step') FG = F.FieldGroup([material, divisionStep], label='Block properties') modelBlocks = []
class ExternalChannelGeometry(NumericalModel): widthAxial = F.Quantity( 'Length', default=(0., 'mm'), label='width (axial)', description='axial width of the spiral rectangular channel') heightRadial = F.Quantity( 'Length', default=(0., 'mm'), label='radial height', description='radial height of the spiral rectangular channel') coilPitch = F.Quantity( 'Length', default=(0., 'mm'), label='coil pitch', description='coil pitch of the spiral rectangular channel') cellSize = F.Quantity( 'Length', default=(0., 'mm'), label='mesh cell size', description='mesh cell size near the outer block circle') meshFineness = F.Integer( default=1, minValue=1, maxValue=10, label='mesh fineness', description='mesh fineness near the outer side of the block') FG = F.FieldGroup([widthAxial, heightRadial, coilPitch, meshFineness], label='Channel geometry') modelBlocks = [] def compute(self, blockDiameter): self.averageCoilDiameter = blockDiameter + self.heightRadial self.cellSize = self.averageCoilDiameter / (self.meshFineness * 10.)
class FluidFlowOutput(NumericalModel): VDot = F.Quantity('VolumetricFlowRate', minValue=(0., 'm**3/h'), default=(1., 'm**3/h'), label='volume flow', description='volume flow rate') mDot = F.Quantity('MassFlowRate', minValue=(0, 'kg/h'), default=(1., 'kg/h'), label='mass flow', description='mass flow rate') T = F.Quantity('Temperature', default=(300., 'K'), label='temperature') p = F.Quantity('Pressure', default=(1., 'bar'), label='pressure') FG = F.FieldGroup([mDot, VDot, T, p], label='Parameters') modelBlocks = [] def compute(self, fState, mDot): self.fState = fState self.T = fState.T self.p = fState.p self.mDot = mDot self.VDot = mDot / fState.rho
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 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 ChannelGroupGeometry(NumericalModel): number = F.Integer(default=0, minValue=0, maxValue=30, label='number', description='number of channels') radialPosition = F.Quantity('Length', default=(0., 'mm'), minValue=(0, 'mm'), label='radial position', description='radial position of the channels') startingAngle = F.Quantity('Angle', default=(0., 'deg'), minValue=(-1e6, 'deg'), label='starting angle', description='starting angle of the first') cellSize = F.Quantity('Length', default=(0., 'mm'), label='mesh cell size', description='mesh cell size near the channels') meshFineness = F.Integer(default=1, minValue=1, maxValue=10, label='mesh fineness', description='mesh fineness near the channels') externalDiameter = F.Quantity( 'Length', default=(0., 'mm'), label='external diameter', description='external diameter of the channels') sections = F.RecordArray(( ('internalDiameter', F.Quantity('Length', default=(0, 'mm'), minValue=(0, 'mm'), label='internal diameter')), ('length', F.Quantity('Length', default=(0.2, 'm'), label='length')), ), label='sections', numRows=5) channelName = F.String() FG = F.FieldGroup([ number, radialPosition, startingAngle, externalDiameter, sections, meshFineness ], label='Parameters') modelBlocks = [] def compute(self): self.cellSize = self.externalDiameter / (self.meshFineness * 2.5) self.length = np.sum(self.sections['length'])
class FluidFlowInput(NumericalModel): fluidName = F.Choices(Fluids, default='ParaHydrogen', label='fluid') flowRateChoice = F.Choices(OrderedDict(( ('V', 'volume'), ('m', 'mass'), )), label='flow rate based on') mDot = F.Quantity('MassFlowRate', minValue=(0, 'kg/h'), default=(1., 'kg/h'), label='mass flow', description='mass flow rate', show='self.flowRateChoice == "m"') VDot = F.Quantity('VolumetricFlowRate', minValue=(0., 'm**3/h'), default=(1., 'm**3/h'), label='volume flow', description='volume flow rate', show='self.flowRateChoice == "V"') T = F.Quantity('Temperature', default=(300., 'K'), label='temperature') p = F.Quantity('Pressure', default=(1., 'bar'), label='pressure') FG = F.FieldGroup([fluidName, flowRateChoice, mDot, VDot, T, p], label='Parameters') modelBlocks = [] def createFluidState(self): return CP.FluidState(self.fluidName) def compute(self): self.fState = self.createFluidState() self.fState.update_Tp(self.T, self.p) if (self.flowRateChoice == 'm'): self.VDot = self.mDot / self.fState.rho else: self.mDot = self.VDot * self.fState.rho
class Turbine(CycleComponent2FlowPorts): eta = F.Quantity(default=1, minValue=0, maxValue=1, label='efficiency') FG = F.FieldGroup([eta], label='Turbine') modelBlocks = [] #================== Methods =================# def compute(self, pOut): if (pOut > self.inlet.state.p): raise ValueError( 'Outlet pressure must be lower than inlet pressure') CycleComponent2FlowPorts.compute(self) self.outlet.state.update_ps(pOut, self.inlet.state.s) wIdeal = self.outlet.state.h - self.inlet.state.h self.w = wIdeal * self.eta self.qIn = 0 self.delta_h = self.w + self.qIn self.outlet.state.update_ph(pOut, self.inlet.state.h + self.delta_h)
class IncompressibleSolutionFlowInput(FluidFlowInput): solName = F.Choices(IncompressibleSolutions, default='MEG', label='fluid (name)', description='fluid (incompressible solutions)') solMassFraction = F.Quantity( 'Fraction', default=(0, '%'), label='fluid (mass fraction)', description='mass fraction of the substance other than water') incomSolFG = F.FieldGroup( [solName, solMassFraction, 'flowRateChoice', 'mDot', 'VDot', 'T', 'p'], label='Parameters') def createFluidState(self): return CP5.FluidStateFactory.createIncompressibleSolution( self.solName, self.solMassFraction)
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 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 Condenser(IsobaricHeatExchanger): computeMethod = F.Choices(OrderedDict(( ('dT', 'sub-cooling'), ('Q', 'vapor quality'), ('eta', 'thermal efficiency'), ('T', 'temperature'), ('H', 'enthalpy'), )), label='compute outlet by') dTOutlet = F.Quantity('TemperatureDifference', default=(-10, 'degC'), minValue=-1e10, maxValue=-1e-3, label='outlet subcooling', show='self.computeMethod == "dT"') FG = F.FieldGroup([ 'computeMethod', 'etaThermal', 'TExt', 'dTOutlet', 'TOutlet', 'qOutlet', 'hOutlet' ], label='Condenser') modelBlocks = []
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