def solve_heatExchanger(self, device: HeatExchanger): """Constructs the heat balance equation over the flows entering and exiting the heat exchanger. If equation is solvable as is (i.e. has 1 unknown), calculates the missing property and sets its value in the relevant object.""" # m1h11 + m2h21 + m3h31 = m1h12 + m2h22 + m3h32 # m1(h1i - h1o) + m2(h2i - h2o) + m3(h3i - h3o) = 0 # heatBalance = LinearEquation([ [ ( (state_in, 'flow.massFF'), state_in.h - state_out.h) for state_in, state_out in device.lines], 0 ]) heatBalance_LHS = [] for state_in, state_out in device.lines: heatBalance_LHS.append( ((state_in.flow, 'massFF'), (state_in, 'h'))) heatBalance_LHS.append( ((-1), (state_out.flow, 'massFF'), (state_out, 'h'))) heatBalance = LinearEquation(LHS=heatBalance_LHS, RHS=0) if heatBalance.isSolvable( ): # if solvable by itself, there is only one unknown solution = heatBalance.solve() unknownAddress = list(solution.keys())[0] setattr_fromAddress(object=unknownAddress[0], attributeName=unknownAddress[1], value=solution[unknownAddress]) self._updatedUnknowns.add(unknownAddress) else: self._equations.append(heatBalance) heatBalance.source = device
def get_sHeatSupplied(self, returnExpression: bool = False): """Returns the value of the total specific heat supplied by the HeatDevices of the flow. If returnExpression, returns the expression that gives the value when added in a LinearEquation.""" self._sHeatSupplied = float('nan') expression_LHS = [(-1, (self, '_sHeatSupplied'))] for device in set( device for device in self.heatDevices if isinstance(device, ( Boiler, ReheatBoiler, Combustor, GasReheater))): # if not isinstance(device, HeatExchanger) expression_LHS += device.get_sHeatSuppliedExpression( forFlow=self, constant_c=self.constant_c) expression = LinearEquation(LHS=expression_LHS, RHS=0) expression.source = 'Flows.get_sHeatSupplied' if expression.isSolvable(): assert expression.get_unknowns()[0][0] == (self, '_sHeatSupplied') # Linear equation is solvable if only there is only one unknown. If there is only one unknown, it must be the self._net_sWorkExtracted since we know it is unknown for sure result = list(expression.solve().values())[0] return result else: if returnExpression: return expression.isolate([(self, '_sHeatSupplied')]) else: return float('nan')
def _add_pressureRatioRelation(self, device: WorkDevice): """Adds a linear equation describing the relation between end state pressures and the pressure ratio parameter of the work device. Works only with work devices that have one state_in and one states_out.""" state_out = device.states_out[0] if isinstance(device, (Compressor, Pump)): # Compression pressureRatioRelation_LHS = [((device, 'pressureRatio'), (device.state_in, 'P')), (-1, (state_out, 'P'))] else: assert isinstance(device, Turbine) # Expansion pressureRatioRelation_LHS = [((device, 'pressureRatio'), (state_out, 'P')), (-1, (device.state_in, 'P'))] pressureRatioRelation = LinearEquation(LHS=pressureRatioRelation_LHS, RHS=0) device._pressureRatioRelationSetup = True if pressureRatioRelation.isSolvable(): solution = pressureRatioRelation.solve() unknownAddress = list(solution.keys())[0] setattr_fromAddress(object=unknownAddress[0], attributeName=unknownAddress[1], value=solution[unknownAddress]) self._updatedUnknowns.add(unknownAddress) else: pressureRatioRelation.source = device self._equations.append(pressureRatioRelation)
def get_sHeatSupplied(self, returnExpression: bool = False): self._sHeatSupplied = float('nan') expression_LHS = [ (-1, (self, '_sHeatSupplied')) ] for device in set(device for device in self.heatDevices if isinstance(device, Boiler) or isinstance(device, ReheatBoiler)): # if not isinstance(device, HeatExchanger) expression_LHS += device.get_sHeatSuppliedExpression(forFlow=self) # expression_LHS += [ (1, (device.state_out, 'h')), (-1, (device.state_in, 'h')) ] expression = LinearEquation(LHS=expression_LHS, RHS=0) expression.source = 'Flows.get_sHeatSupplied' if expression.isSolvable(): assert expression.get_unknowns()[0][0] == (self, '_sHeatSupplied') # Linear equation is solvable if only there is only one unknown. If there is only one unknown, it must be the self._net_sWorkExtracted since we know it is unknown for sure result = list(expression.solve().values())[0] return result else: if returnExpression: return expression.isolate( [(self, '_sHeatSupplied')] ) else: return float('nan')
def _add_turbineMassBalance(self, device: Turbine): """Creates a mass balance equation for flows entering/exiting a turbine.""" massBalance_LHS = [] massBalance_LHS.append((1, (device.state_in.flow, 'massFF'))) for state_out in device.states_out: massBalance_LHS.append((-1, (state_out.flow, 'massFF'))) massBalance = LinearEquation(LHS=massBalance_LHS, RHS=0) massBalance.source = device if massBalance.isSolvable(): solution = massBalance.solve() unknownAddress = list(solution.keys())[0] setattr_fromAddress(object=unknownAddress[0], attributeName=unknownAddress[1], value=solution[unknownAddress]) self._updatedUnknowns.add(unknownAddress) else: self._equations.append(massBalance)
def get_net_sWorkExtracted(self, returnExpression: bool = False): """Returns the value of the total specific work extracted by the WorkDevices of the flow. If returnExpression, returns the expression that gives the value when added in a LinearEquation.""" self._net_sWorkExtracted = float('nan') expression_LHS = [(-1, (self, '_net_sWorkExtracted'))] for device in self.workDevices: stateBefore, stateAfter = self.get_surroundingItems( device, includeNone=True ) # returned list will have None values if there is no item in the spot before / after if stateBefore is None and isinstance(device, Turbine): stateBefore = device.state_in # A turbine (commonly in steam cycles) may have multiple flows coming out of it. The state_in may not belong to the same flow as the state_out. if stateBefore is not None and stateAfter is not None: if not self.constant_c: expression_LHS += [ (1, (stateBefore, 'h')), (-1, (stateAfter, 'h')) ] # effectively adds (h_in - h_out) to the equation else: # constant c analysis expression_LHS += [ (1, (self.workingFluid.cp), (stateBefore, 'T')), (-1, (self.workingFluid.cp), (stateAfter, 'T')) ] else: continue expression = LinearEquation( LHS=expression_LHS, RHS=0 ) # -1 * self._net_sWorkExtracted + state_in.h - state_out.h = 0 expression.source = 'Flows.get_net_sWorkExtracted' if expression.isSolvable(): assert expression.get_unknowns()[0][0] == (self, '_net_sWorkExtracted') # Linear equation is solvable if only there is only one unknown. If there is only one unknown, it must be the self._net_sWorkExtracted since we know it is unknown for sure result = list(expression.solve().values())[0] return result else: if returnExpression: return expression.isolate([(self, '_net_sWorkExtracted')]) else: return float('nan')
def get_net_sWorkExtracted(self, returnExpression: bool = False): self._net_sWorkExtracted = float('nan') expression_LHS = [ (-1, (self, '_net_sWorkExtracted')) ] for device in self.workDevices: stateBefore, stateAfter = self.get_surroundingItems(device, includeNone=True) # returned list will have None values if there is no item in the spot before / after if stateBefore is None and isinstance(device, Turbine): stateBefore = device.state_in if stateBefore is not None and stateAfter is not None: expression_LHS += [ (1, (stateBefore, 'h')) , (-1, (stateAfter, 'h')) ] # effectively adds (h_in - h_out) to the equation else: continue expression = LinearEquation(LHS=expression_LHS, RHS=0) # -1 * self._net_sWorkExtracted + state_in.h - state_out.h = 0 expression.source = 'Flows.get_net_sWorkExtracted' if expression.isSolvable(): assert expression.get_unknowns()[0][0] == (self, '_net_sWorkExtracted') # Linear equation is solvable if only there is only one unknown. If there is only one unknown, it must be the self._net_sWorkExtracted since we know it is unknown for sure result = list(expression.solve().values())[0] return result else: if returnExpression: return expression.isolate( [(self, '_net_sWorkExtracted')] ) else: return float('nan')
def _solveDevice(self, device: Device): endStates = device.endStates if isinstance(device, WorkDevice): # Apply isentropic efficiency relations to determine outlet state self.solve_workDevice(device) # if not self._initialSolutionComplete: # the below processes do not need to be done in each flow solution iteration, but only for the initial one if isinstance(device, HeatDevice): # Setting end state pressures to be the same if device._infer_constant_pressure: device.infer_constant_pressure() if isinstance( device, ReheatBoiler): # reheat boilers can have multiple lines. # Setting up fixed exit temperature if inferring exit temperature from one exit state if device._infer_fixed_exitT: device.infer_fixed_exitT() elif isinstance(device, Intercooler): if device.coolTo == 'ideal': # Cool to the temperature of the compressor inlet state assert isinstance( (compressorBefore := self.get_itemRelative( device, -2)), Compressor ) # before intercooler, there should be compressor exit state, and then a compressor device.state_out.set_or_verify( {'T': compressorBefore.state_in.T}) else: # Cool to specified temperature assert isNumeric(device.coolTo) device.state_out.set_or_verify({'T': device.coolTo}) elif isinstance(device, GasReheater): if device.heatTo == 'ideal': # Heat to the temperature of the turbine inlet state assert isinstance( (turbineBefore := self.get_itemRelative(device, -2)), Turbine) device.state_out.set_or_verify( {'T': turbineBefore.state_in.T}) elif device.heatTo == 'heatSupplied': if not self._initialSolutionComplete: assert isNumeric(device.sHeatSupplied) if not self.constant_c: sHeatSuppliedRelation = LinearEquation( LHS=[(1, (device.state_out, 'h')), (-1, (device.state_in, 'h'))], RHS=device.sHeatSupplied) else: sHeatSuppliedRelation = LinearEquation( LHS=[(1, self.workingFluid.cp, (device.state_out, 'T')), (-1, self.workingFluid.cp, (device.state_in, 'T'))], RHS=device.sHeatSupplied) self._equations.append(sHeatSuppliedRelation) if sHeatSuppliedRelation.isSolvable(): solution = sHeatSuppliedRelation.solve() unknownAddress = list(solution.keys())[0] setattr_fromAddress( object=unknownAddress[0], attributeName=unknownAddress[1], value=solution[unknownAddress]) self._updatedUnknowns.add(unknownAddress) else: sHeatSuppliedRelation.source = device self._equations.append(sHeatSuppliedRelation) else: # Heat to specified temperature assert isNumeric(device.heatTo) device.state_out.set_or_verify({'T': device.heatTo}) elif isinstance(device, HeatExchanger): # Setting end state pressures along the same line if pressures is assumed constant along each line if device._infer_constant_linePressures: device.infer_constant_linePressures() # Setting temperature of exit states equal for all lines # TODO - not the ideal place - inter-flow operation should ideally be in cycle scope if device._infer_common_exitTemperatures: device.infer_common_exitTemperatures() elif isinstance(device, MixingChamber): # Setting pressures of all in / out flows to the same value if device._infer_common_mixingPressure: device.infer_common_mixingPressure() elif isinstance(device, Trap): if device._infer_constant_enthalpy: device.infer_constant_enthalpy() self._defineStates_ifDefinable(endStates)
def solve_regenerator(self, device: Regenerator): if all( isNumeric(line[0].T) for line in device.lines ): # Need inlet temperatures of both lines to determine in which direction heat will flow warmLine, coldLine = device.lines if device.lines[1][0].T > device.lines[0][ 0].T: # state_in of device.lines[1] coldLine, warmLine = device.lines warm_in, warm_out = warmLine cold_in, cold_out = coldLine assert warm_in.flow.constant_c == cold_in.flow.constant_c, 'solve_regenerator: Flows of the warm and cold lines have different constant_c settings! Not allowed.' constant_c = warm_in.flow.constant_c if device.counterFlow_commonColdTemperature: warm_out.set_or_verify({'T': cold_in.T}) heatBalance_LHS = [] # warm_mFF*(warm_in.h - warm_out.h)*effectiveness = cold_mFF*(cold_out.h - cold_in.h) # warm_mFF*(warm_in.h - warm_out.h)*effectiveness + cold_mFF*(cold_in.h - cold_out.h) = 0 if constant_c: assert isNumeric(warm_in.flow.workingFluid.cp) heatBalance_LHS.append( ((device.effectiveness), (warm_in.flow, 'massFF'), (warm_in.flow.workingFluid.cp), (warm_in, 'T'))) heatBalance_LHS.append( ((device.effectiveness), (-1), (warm_out.flow, 'massFF'), (warm_out.flow.workingFluid.cp), (warm_out, 'T'))) heatBalance_LHS.append( ((cold_in.flow, 'massFF'), (cold_in.flow.workingFluid.cp), (cold_in, 'T'))) heatBalance_LHS.append( ((-1), (cold_out.flow, 'massFF'), (cold_out.flow.workingFluid.cp), (cold_out, 'T'))) else: heatBalance_LHS.append( ((device.effectiveness), (warm_in.flow, 'massFF'), (warm_in, 'h'))) heatBalance_LHS.append( ((device.effectiveness), (-1), (warm_out.flow, 'massFF'), (warm_out, 'h'))) heatBalance_LHS.append( ((cold_in.flow, 'massFF'), (cold_in, 'h'))) heatBalance_LHS.append( ((-1), (cold_out.flow, 'massFF'), (cold_out, 'h'))) heatBalance = LinearEquation(LHS=heatBalance_LHS, RHS=0) if heatBalance.isSolvable( ): # if solvable by itself, there is only one unknown solution = heatBalance.solve() unknownAddress = list(solution.keys())[0] setattr_fromAddress(object=unknownAddress[0], attributeName=unknownAddress[1], value=solution[unknownAddress]) self._updatedUnknowns.add(unknownAddress) else: self._equations.append(heatBalance) heatBalance.source = device return True else: return False