def test_air_01(self): # From MECH2201 - A10 Q2 s1 = fullyDefine_StateIGas(StateIGas(P=97, T=70), air) self.CompareResults(s1, {'mu': 1.015}, 3) s2 = fullyDefine_StateIGas(StateIGas(T=(1136.86 - 273), mu=0.0507), air) self.CompareResults(s2, {'P': 6435}, 3) s3 = fullyDefine_StateIGas(StateIGas(P=6435, T=(2046.35 - 273.15)), air) self.CompareResults(s3, {'mu': 0.09126}, 3)
def test_air_01(self): # MECH2201 - A10 - Q1 s1 = StateIGas(P=100, T=30, mu=0.45/0.517) s2 = StateIGas(P=1100) apply_isentropicIGasProcess(constant_c=False, fluid=air, state_in=s1, state_out=s2) self.CompareResults(s1, {'P_r': 1.4356}, 3) self.CompareResults(s1, {'mu_r': 606.08}, 3) self.CompareResults(s2, {'P_r': 15.9712}, 3) self.CompareResults(s2, {'T': 594.96-273}, 3) self.CompareResults(s2, {'mu_r': 108.22}, 3) self.CompareResults(s2, {'u': 430.94}, 3)
def apply_IGasLaw(state: StateIGas, R: float): """Uses Ideal Gas Law to find missing properties, if possible. If all variables in the Ideal Gas Law are already defined, checks consistency""" # P mu = R T IGasLaw_allProperties = ['P', 'mu', 'T'] IGasLaw_availableProperties = [propertyName for propertyName in IGasLaw_allProperties if isNumeric(getattr(state, propertyName))] IGasLaw_missingProperties = [propertyName for propertyName in IGasLaw_allProperties if propertyName not in IGasLaw_availableProperties] if number_ofMissingProperties := len(IGasLaw_missingProperties) == 1: missingProperty = IGasLaw_missingProperties[0] if missingProperty == 'P': state.P = (R * to_Kelvin(state.T) / state.mu) elif missingProperty == 'mu': state.mu = (R * to_Kelvin(state.T) / state.P) elif missingProperty == 'T': state.T = to_deg_C(state.P * state.mu / R)
def interpolate_inIGasTable(mpDF: DataFrame, interpolate_by: str, interpolate_at: float) -> StateIGas: """Method to interpolate in the mpDF of an ideal gas, which should give T-dependent properties such as h, u, P_r, mu_r, s0.""" queryPropt, queryValue = interpolate_by, interpolate_at exactMatch = mpDF.query('{0} == {1}'.format(queryPropt, queryValue)) if exactMatch.empty: states_ordered_byPropt = mpDF.sort_values(queryPropt) proptVal_below, proptVal_above = get_surroundingValues(states_ordered_byPropt[queryPropt].to_list(), queryValue) state_below = StateIGas().init_fromDFRow( mpDF.query('{0} == {1}'.format(queryPropt, proptVal_below)) ) state_above = StateIGas().init_fromDFRow( mpDF.query('{0} == {1}'.format(queryPropt, proptVal_above)) ) state_atProptVal = interpolate_betweenPureStates(state_below, state_above, interpolate_at={queryPropt: queryValue}) assert all([state_atProptVal.hasDefined(property) for property in StateIGas._properties_Tdependent]) return state_atProptVal
def define_StateIGas(state: StateIGas, fluid: 'IdealGas'): """Tries to fill in the properties of an ideal gas state by applying the ideal gas law and looking up state on the provided mpDF.""" if len(available_TDependentProperties := [propertyName for propertyName in fluid.mpDF.mp.availableProperties if propertyName in state._properties_all and isNumeric(getattr(state, propertyName))]) >= 1: refPropt_name = available_TDependentProperties[0] # Try finding exact state on mpDF state_at_refPropt = fluid.mpDF.cq.cQuery({refPropt_name: getattr(state, refPropt_name)}) try: if state_at_refPropt.empty: # Interpolate in mpDF refPropt_valueBelow, refPropt_valueAbove = get_surroundingValues(fluid.mpDF[refPropt_name], value=getattr(state, refPropt_name)) state_at_refPropt_valueBelow = StateIGas().init_fromDFRow(fluid.mpDF.cq.cQuery({refPropt_name: refPropt_valueBelow})) state_at_refPropt_valueAbove = StateIGas().init_fromDFRow(fluid.mpDF.cq.cQuery({refPropt_name: refPropt_valueAbove})) state_at_refPropt = interpolate_betweenPureStates(state_at_refPropt_valueBelow, state_at_refPropt_valueAbove, interpolate_at={refPropt_name: getattr(state, refPropt_name)}) state.copy_fromState(state_at_refPropt) except NeedsExtrapolationError: pass
def test_air_02(self): # MECH2201 - A10 - Q2 s1 = StateIGas(P=97, T=70) fullyDefine_StateIGas(s1, air) self.CompareResults(s1, {'mu': 1.015}, 3) s2 = StateIGas(mu=s1.mu/20) # Tests finding T of state using ratio of mu1/mu2 apply_isentropicIGasProcess(constant_c=True, fluid=air, state_in=s1, state_out=s2) self.CompareResults(s2, {'T': 1136.86-273, 'P': 6435}, 3) s3 = StateIGas(P=6435, T=2046.35-273) fullyDefine_StateIGas(s3, air) self.CompareResults(s3, {'mu': 0.09126}, 3) s4 = StateIGas(mu=20*s2.mu) apply_isentropicIGasProcess(constant_c=True, fluid=air, state_in=s3, state_out=s4) self.CompareResults(s4, {'T': 781.05-273}, 3)
def fullyDefine_StateIGas(state: StateIGas, fluid: 'IdealGas') -> StateIGas: """Tries to fill in the properties of an ideal gas state by applying the ideal gas law and looking up state on the provided mpDF.""" apply_IGasLaw(state, fluid.R) # Do an ideal gas table look-up after applying ideal gas law above. The process above may first determine T and only then this table look-up can be done. # if len(available_TDependentProperties := [propertyName for propertyName in state.get_asList_definedPropertiesNames() if propertyName in fluid.mpDF.mp.availableProperties]) >= 1: if len(available_TDependentProperties := [propertyName for propertyName in StateIGas._properties_Tdependent if state.hasDefined(propertyName)]) >= 1: # Get tabulated T-dependent properties refPropt_name = available_TDependentProperties[0] state_at_refPropt = fluid.mpDF.cq.cQuery({refPropt_name: getattr(state, refPropt_name)}) # Try finding exact state on mpDF if state_at_refPropt.empty: try: # Interpolate in mpDF - ideal gas properties table interpolatedState = interpolate_inIGasTable(mpDF=fluid.mpDF, interpolate_by=refPropt_name, interpolate_at=getattr(state, refPropt_name)) state.copy_fromState(interpolatedState) except NeedsExtrapolationError: print('fullyDefine_StateIGas: Extrapolation needed to find state at {0}={1}'.format(refPropt_name, getattr(state, refPropt_name))) pass else: state.init_fromDFRow(state_at_refPropt) # Found exact match, initialize from DFRow
def _get_state_in_from_state_out(self, device: WorkDevice, state_out: StateIGas, percentDifference: float = 0.1, iteration_T_steps: float = 2): """Finds state_in to the WorkDevice by iterating and trying to match the isentropic efficiency.""" # TODO: Can add the process for constant c, non ideal gases state_in_guess = StateIGas(P=device.state_in.P) apply_isentropicIGasProcess(constant_c=self.constant_c, state_in=state_in_guess, state_out=state_out, fluid=self.workingFluid) state_in_guess.clearFields(keepFields=[ 'P', 'T' ]) # Iteration guess state has only T and P defined. compression = state_out.P > device.state_in.P iteration = 1 while True: print( '_get_state_in_from_state_out: Solution iteration #{0} - state_in temperature guess: {1}' .format(iteration, state_in_guess.T)) self.workingFluid.define(state_in_guess) state_out_ideal_guess = StateIGas(P=state_out.P) apply_isentropicIGasProcess(self.constant_c, fluid=self.workingFluid, state_in=state_in_guess, state_out=state_out_ideal_guess) if compression: eta_isentropic_guess = (state_out_ideal_guess.h - state_in_guess.h) / (state_out.h - state_in_guess.h) else: # expansion eta_isentropic_guess = (state_in_guess.h - state_out.h) / ( state_in_guess.h - state_out_ideal_guess.h) if isWithin(eta_isentropic_guess, percentDifference, '%', device.eta_isentropic): print( '_get_state_in_from_state_out: Solution satisfactory at iteration {0} - eta_isentropic Prescribed/Calculated: {1}/{2}\n\tstate_in: {3}' .format(iteration, device.eta_isentropic, eta_isentropic_guess, state_in_guess)) break else: # Reset iteration guess state at each iteration to have T and P only. state_in_guess = StateIGas( T=(state_in_guess.T - iteration_T_steps), P=device.state_in.P ) # reduce temperature by K/°C and try again to see if this is the right state_in that gives the prescribed isentropic efficiency iteration += 1 return state_in_guess
def solve_workDevice(self, device: WorkDevice): """Determines outlet state based on available inlet state using isentropic efficiency.""" # Find the state_out out of the device IN THIS FLOW - work devices may have multiple states_out (e.g. turbines with many extractions for reheat, regeneration). occurrences_ofDevice = [ index for index, item in enumerate(self.items) if item is device ] states_afterDevice: List[StatePure] = [ self.items[index + 1] for index in occurrences_ofDevice if index + 1 < len(self.items) ] # state_afterDevice is a StatePure for sure after the check in _check_itemsConsistency # PRESSURE RATIO RELATION SETUP if not device._pressureRatioRelationSetup and len( states_afterDevice) == 1: self._add_pressureRatioRelation(device) # ISENTROPIC PROCESS RELATIONS if device.eta_isentropic == 1: self._set_endStateEntropiesEqual(device) if isinstance(self.workingFluid, IdealGas): for state_out in device.states_out: # Can determine additional properties based on ideal gas isentropic process relations apply_isentropicIGasProcess(constant_c=self.constant_c, state_in=device.state_in, state_out=state_out, fluid=self.workingFluid) self._defineStates_ifDefinable( device.endStates ) # if any states became definable with the above process # ISENTROPIC EFFICIENCY RELATIONS for state_out in states_afterDevice: if not isinstance(self.workingFluid, IdealGas) and ( device.state_in.hasDefined('h') and state_out.hasDefined('P') ): # Used to check if state_in also hadNumeric 's' # going to overwrite state_out - TODO: Need to copy in the first time, then verify in subseqs state_out.copy_fromState( apply_isentropicEfficiency( constant_c=self.constant_c, state_in=device.state_in, state_out_ideal=state_out, eta_isentropic=device.eta_isentropic, fluid=self.workingFluid)) elif isinstance(self.workingFluid, IdealGas): # FIND state_out FROM state_in if not state_out.hasDefined( 'T'): # common case: find state_out based on state_in state_out_ideal = StateIGas().copy_fromState(state_out) apply_isentropicIGasProcess(constant_c=self.constant_c, fluid=self.workingFluid, state_in=device.state_in, state_out=state_out_ideal) state_out_actual = apply_isentropicEfficiency( constant_c=self.constant_c, state_in=device.state_in, state_out_ideal=state_out_ideal, eta_isentropic=device.eta_isentropic, fluid=self.workingFluid) if state_out_actual is not None: state_out.copy_fromState(state_out_actual) # FIND state_in FROM state_out else: if device.state_in.hasDefined( 'P') and state_out.hasDefined( 'P') and not device.state_in.hasDefined('T'): print( '\tReverse Isentropic Efficiency Calculation:\n\tstate_out: {0}\n\tstate_in: {1}' .format(state_out, device.state_in)) device.state_in.copy_fromState( self._get_state_in_from_state_out( device, state_out))