Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
    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')
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
 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')
Ejemplo n.º 5
0
    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)
Ejemplo n.º 6
0
def apply_incompressibleWorkRelation(state_in: StatePure, state_out: StatePure):
    """Applies the steady flow **reversible** work relation for incompressible states. (h2 - h1 = mu * (P2 - P1))"""
    endStates = [state_in, state_out]
    # h2 - h1 = mu * (P2 - P1)
    # mu * P2 - mu * P1 - h2 + h1 = 0

    # Check if both end states are incompressible
    assert all(state.x <= 0 for state in endStates)
    # Incompressible -> mu constant
    states_with_mu = [state for state in endStates if state.hasDefined('mu')]
    if len(states_with_mu) > 0:
        sampleState_with_mu = states_with_mu[0]
        for state in [state for state in endStates if state is not sampleState_with_mu]:
            state.set_or_verify({'mu': sampleState_with_mu.mu})

    workRelation = LinearEquation(LHS=[ ( (state_out, 'mu'), (state_out, 'P') ), (-1, (state_in, 'mu'), (state_in, 'P')), (-1, (state_out, 'h')), (1, (state_in, 'h')) ], RHS=0)
    if workRelation.isSolvable():
        workRelation.solve_and_set()
        return True
    else:
        return workRelation
Ejemplo n.º 7
0
    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')
Ejemplo n.º 8
0
    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')
Ejemplo n.º 9
0
    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)
Ejemplo n.º 10
0
    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