def hello_world(): body = request.get_json() units = body['units'] set_units(units) selected_species = pm.get(body['species']) chart_data = build_chart_data(selected_species) if 'pressure' in body: pressure_float = float(body['pressure']) return jsonify({ "values": build_saturation_response_from_pressure(selected_species, pressure_float), "chart_data": chart_data }) if 'temp' in body: temp_float = float(body['temp']) return jsonify({ "values": build_saturation_response_from_temp(selected_species, temp_float), "chart_data": chart_data })
def plot_cycle(cycle): id_df = cycle()[0] re_df = cycle()[2] index = id_df.columns.values T_id = id_df[index[0]] s_id = id_df[index[3]] T_re = re_df[index[0]] s_re = re_df[index[3]] p_id = id_df[index[1]] p_re = re_df[index[1]] H2O = pm.get('mp.H2O') T_id_last = np.linspace(T_id[-1], T_id[0],150) p_id_last = p_id[-1] * np.ones(len(T_id_last)) S_id_last = H2O.s(T=T_id_last,p=p_id_last) T_re_last = np.linspace(T_re[-1], T_re[0],150) p_re_last = p_re[-1] * np.ones(len(T_re_last)) S_re_last = H2O.s(T=T_re_last,p=p_re_last) water_curve() plt.plot(S_id_last,T_id_last,'-g') plt.plot(s_id, T_id, '-og', label='Ideal') plt.plot(S_re_last,T_re_last,'-k') plt.plot(s_re, T_re, '-ok', label='Real') plt.legend() plt.title(cycle()[-1]) plt.xlabel('Entropy s [kJ/(kg K)]') plt.ylabel('Temperature T [K]') return plt.show()
def __init__(self, T3t=k.T3t, T0=k.T0, Pa=k.Pa, delta=0.001): self.delta = delta self.data = self.create_data_array() self.T3t, self.T0, self.Pa = T3t, T0, Pa self.P3t = (np.asscalar(self.air.d(T=self.T3t)) / 1000) self.air = pm.get('ig.air') self.cp0 = np.asscalar(self.air.cp(T=self.T0, p=self.Pa))
def __init__(self, delta=0.01): self.M0_at_SF_max = 2.34 self.SF_max = 817.5201699820385 self.air = pm.get('ig.air') self.delta = 0.01 # self.data = Problem1.empty_data_array(delta) self.Ts, self.gs = self.throat_convergence() # self.Te, self.ge, self.Me = self.exit_convergence() # self.A = self.area_ratio() self.check()
def __init__(self, T3t=k.T3t, T0=k.T0, Pa=k.Pa, delta=0.01, gamma=k.GAMMA, verbose=True): self.delta = delta self.data = Problem1.empty_data_array(delta) self.P1 = Problem1(verbose=False) self.SF_P1, self.M0_P1 = self.P1.SF, self.P1.M0 self.M0, self.SF = 0, 0 self.air = pm.get('ig.air') self.T3t = T3t self.T0 = T0 self.Pa = Pa self.M4 = 2 self.gamma = gamma self.verbose = verbose self.ideal_nozzle()
def __init__(self, delta=0.01): self.data = Problem2.empty_data_array(delta) self.SF_ideal, self.M0_ideal = Problem1.return_values() # self.M0_at_SF_max = 2.34 # self.SF_max = 817.5201699820385 self.air = pm.get('ig.air') self.delta = 0.01 self.g = self.gamma(k.T0) self.Ts, self.gs = self.throat_convergence() self.Ps = self.pressure_from_temperature() self.Te = self.temperature_from_pressure() self.Me_id = self.Me_ideal() self.A = self.area_ratio() self.execute()
def __init__(self, fluid, unit_system="kSI_K", verbose=False): self.verbose = verbose self.unit_system = unit_system self.fluid = fluid if fluid.lower() == "air": fluid = "air" self._pm = pm.get(f"ig.{fluid}") self._pm.config = pm.config # legacy definitions/aliases self.Cp = self.c_p = self.cp self.Cv = self.c_v = self.cv self.mw = self.mm self.e = self.u self.gamma = self.k
def water_curve(): #From the pyromat documentation we get the water curve: # Get the critical and triple point properties H2O = pm.get('mp.H2O') Tt,pt = H2O.triple() Tc,pc = H2O.critical() # Explore the temperatures between Tt and Tc in 5K increments T = np.linspace(Tt,Tc,1000) s0 = np.zeros(len(T)) s1 = np.zeros(len(T)) #fig = plt.figure() for i in range(len(T)): s0[i]=H2O.s(T[i],x=0) s1[i]=H2O.s(T[i],x=1) return plt.plot(s0,T,'cyan',s1,T,'red',ls='--')
def plot_t_s(): # initialize library air = pyro.get('ig.air') pyro.config['unit_pressure'] = 'bar' pyro.config['unit_temperature'] = 'K' pyro.config['unit_matter'] = 'kg' pyro.config['unit_energy'] = 'kJ' # isobaric p1 = P0 t1 = T0 s1 = air.s(t1, p1) p2 = p1 * MAX_PRESSURE_RATIO t2 = air.T_s(s=s1, p=p2) s2 = s1 # wc = air.h(t2,p2) - air.h(T1,p1) t3 = T_MAX p4 = p1 p3 = p2 qh = air.h(t3, p3) - air.h(t2, p2) s3 = air.s(t3, p3) s4 = s3 t4 = air.T_s(s=s4, p=p4) wt = air.h(t3, p3) - air.h(t4, p4) temperature_axis_array = np.linspace(t2, t3) plt.plot([s1, s2], [t1, t2], 'blue', linewidth=1.5) plt.plot(air.s(T=temperature_axis_array, p=p2), temperature_axis_array, 'green', linewidth=1.5) plt.plot([s3, s3], [t3, t4], 'black', linewidth=1.5) temperature_axis_array = np.linspace(t1, t4) plt.plot(air.s(T=temperature_axis_array, p=p1), temperature_axis_array, 'red', linewidth=1.5) plt.legend([ 'Adiabatic Compression', 'Isobaric Combustion', 'Adiabatic Compression', 'Isobaric Cooling' ]) plt.xlabel('Entropy, s (kJ/kg/K)') plt.ylabel('Temperature, T (K)') plt.grid('on') plt.title('Brayton Cycle T-S Graphic') plt.show()
def get_substance(self, idstr): """Wrapper function for pm.get() that registers appropriate error messages substance = get_substance(idstr) The idstr is the ID string used by get(). """ substance = None try: substance = pm.get(idstr) except pm.utility.PMParamError: self.mh.error('Substance not found: ' + str(idstr)) except: self.mh.error( 'There was an unexpected error retrieving substance: ' + str(idstr)) self.mh.message(repr(sys.exc_info()[1])) return substance
def __init__(self, T3t=k.T3t, T0=k.T0, Pa=k.Pa, delta=0.01, gamma=k.GAMMA, verbose=True): self.delta = delta self.data = Problem1.empty_data_array(delta) self.T3t = T3t # [K] self.T0 = T0 # [K] self.Pa = Pa # [K] self.gamma = gamma self.air = pm.get('ig.air') self.verbose = verbose self.SF = 0 self.M0 = 0 self.execute()
def __init__(self, T3t=k.T3t, T0=k.T0, Pa=k.Pa, delta=0.01, gamma=k.GAMMA, verbose=True): self.delta = delta self.data = Problem1.empty_data_array(delta) self.P1 = Problem1(verbose=False) self.SF_P1, self.M0_P1 = self.P1.SF, self.P1.M0 self.air = pm.get('ig.air') self.T3t = T3t self.T0 = T0 self.Pa = Pa self.gamma = gamma self.verbose = verbose self.M0_star = self.equation_A() #self.optimize() self.M4 = 2.53 self.A = self.equation_B(self.M4) #self.nozzle_area_ratio = self.equation_B() self.execute()
def runargtest(outfile, species, args, reference, error_threshold=.001): """Test properties for a species object given flexible arguments runargtest(outfile, species, args, reference) outfile An open writable file to which to stream the results summary text. species The pyromat object to test or the string id of the object to get args A dictionary containing keyword arguments to pass to the methods identified by reference. reference A dictionary with keys that must correspond to property methods belonging to the species. The corresponding value to each key are the expected results from the Tp test. Examples are in test.py. """ if isinstance(species, str): test = pyro.get(species) elif issubclass(type(species), pyro.reg.__basedata__): test = species else: raise Exception( "Species needs to be a PYroMat ID string or a PYroMat object\n") sys.stdout.write("Testing " + str(test) + "..") outfile.write("Validation of {:s}\n".format(str(test))) outfile.write(" Error failure threshold {:%}\n".format(error_threshold)) error = False for argname in args: N = np.array(args[argname]).size break if N > 1: linefmt = "{:>15s} = " + "{:<15.5e}" * N + "\n" errfmt = "{:>15s} = " + "{:<15.5%}" * N + "\n" for argname in args: outfile.write(linefmt.format(argname, *args[argname])) for thisparam in reference: # Evaluate the parameter under test val = getattr(test, thisparam)(**args) val_ref = reference[thisparam] val_error = np.abs((val - val_ref) / val_ref) outfile.write(linefmt.format(thisparam, *val)) outfile.write(linefmt.format('ref', *val_ref)) outfile.write(errfmt.format('error', *val_error)) if np.any(val_error > error_threshold): error = True outfile.write(" [FAILED]\n") else: outfile.write(" [passed]\n") else: linefmt = "{:>15s} = {:<15.5e}\n" errfmt = "{:>15s} = {:<15.5%}\n" for argname in args: outfile.write(linefmt.format(argname, float(args[argname]))) for thisparam in reference: # Evaluate the parameter under test val = getattr(test, thisparam)(**args) val_ref = reference[thisparam] val_error = np.abs((val - val_ref) / val_ref) outfile.write(linefmt.format(thisparam, float(val))) outfile.write(linefmt.format('ref', float(val_ref))) outfile.write(errfmt.format('error', float(val_error))) if np.any(val_error > error_threshold): error = True outfile.write(" [FAILED]\n") else: outfile.write(" [passed]\n") outfile.write("\n") if error: sys.stdout.write("[FAILED]\n") else: sys.stdout.write("[passed]\n") return error
if runargtest(writeto, 'ig.O2', args, reference): failures.append('ig.O2') # Test the mixture class T = [320., 1000., 1000.] p = [1., 1., 5.] h = [446.5, 1173., 1173.] s = [3.956, 5.158, 4.696] cp = [1.007, 1.141, 1.142] writeto.write( "Air properties were referenced against the CRC Handbook for" + " Chemistry and\nPhysics 97th Edition ``Thermophysical Properties of" + " Air''\nby Eric W. Lemon.\n" + "Enthalpy and Entropy values were adjusted to match a 1 bar and 300K.\n" ) air = pyro.get('ig.air') h = air.h(T=T[0], p=1.) - h[0] + np.array(h) s = air.s(T=T[0], p=1.) - s[0] + np.array(s) reference = {'cp': cp, 's': s, 'h': h} args = {'T': T, 'p': p} error = runargtest(writeto, air, args, reference, error_threshold=.005) # Now the inverse args = {'h': h, 'p': p} reference = {'T_h': T} error = runargtest(writeto, air, args, reference) or error args = {'s': s, 'p': p} reference = {'T_s': T} error = runargtest(writeto, air, args, reference) or error if error:
# Reference values from IF-97 reference = { 'cp': np.array([.261609445e1, .272724317e1, .288569882e1]), 'h': np.array([.521976855e4, .516723514e4, .657122604e4]), 's': np.array([.965408875e1, .772970133e1, .853640623e1]), 'e': np.array([.452749310e4, .447495124e4, .563707038e4]), 'd': 1. / np.array([.138455090e1, .230761299e-1, .311385219e-1]) } writeto.write("Steam validation values for region 5 found at \n" + "http://www.iapws.org/relguide/IF97-Rev.pdf\n" + " Table 42 pg 40\n") runargtest(writeto, 'steam', args, reference) test = pyro.get('steam') T = np.array([300., 500., 600.]) args = {'T': T} reference = {'ps': np.array([.353658941e-1, .263889776e2, .123443146e3])} writeto.write( "Steam validation values for saturation (region 4) found at \n" + "http://www.iapws.org/relguide/IF97-Rev.pdf\n" + " Table 35 pg 34 and Table 36 pg 36\n") runargtest(writeto, 'steam', args, reference) p = np.array([1., 10., 100.]) args = {'p': p} reference = {'Ts': np.array([.372755919e3, .453035632e3, .584149488e3])} runargtest(writeto, 'steam', args, reference)
def update(self): """Update the cycle states and processes """ if self._test(require=('fluid', 'p1', 'p2', 'eta12', 'eta23', 'eta34')): raise PMCycleError( 'There was a problem with the Rankine Cycle configuration') fluid = self.param['fluid'] # Parse the subsance input if isinstance(fluid, str): fluid = pm.get(fluid) if not isinstance(fluid, pm.reg.registry['mp1']): raise PMCycleError('RankineCycle requires substances of class mp1') # Vectorize the parameters p1, p2, eta12, eta23, eta34 = self._prepparam('p1', 'p2', 'eta12', 'eta23', 'eta34') # Get the critical and triple points Tc, pc = fluid.critical() Tt, pt = fluid.triple() # Test the inputs # pt < p1 < pc if (p1 <= pt).any(): raise PMCycleError( 'RankineCycle requires p1 to be greater than the triple-point pressure' ) elif (p1 >= pc).any(): raise PMCycleError( 'Rankine Cycle requires p1 to be less than the critical-point pressure' ) # p1 < p2 < pc if (p2 <= p1).any(): raise PMCycleError( 'RankineCycle requires p2 to be greater than p1.') elif (p2 >= pc).any(): raise PMCycleError( 'RankineCycle requires p1 to be less than the critical-point pressure' ) if (eta12<0).any() or (eta12>1).any() or (eta23<0).any() or \ (eta23>1).any() or (eta34<0).any() or (eta34>1).any(): raise PMCycleError( 'RankineCycle efficiencies must be between 0 and 1.') # Update the lastparam values self._writelast() # Calculate state 1 self.p[0] = p1 self.T[0] = fluid.Ts(p=p1) self.s[0], _ = fluid.ss(T=self.T[0]) self.h[0], _ = fluid.hs(T=self.T[0]) self.d[0], _ = fluid.ds(T=self.T[0]) self.x[0] = 0. # Calculate state 2 self.p[1] = p2 # Start with the isentropic performance T2s = fluid.T_s(s=self.s[0], p=p2) h2s = fluid.h(T=T2s, p=p2) # Adjust the enthalpy rise by the inverse of pump efficiency self.h[1] = self.h[0] + (h2s - self.h[0]) / eta12 self.T[1], self.x[1] = fluid.T_h(h=self.h[1], p=p2, quality=True) self.d[1] = fluid.d(T=self.T[1], p=p2, x=self.x[1]) # Now that we have density, we can use it directly to solve for entropy self.s[1] = fluid.s(T=self.T[1], d=self.d[1]) # Calculate state 3 self.p[2] = p2 self.T[2] = fluid.Ts(p=p2) _, self.s[2] = fluid.ss(T=self.T[2]) _, self.h[2] = fluid.hs(T=self.T[2]) _, self.d[2] = fluid.ds(T=self.T[2]) self.x[2] = 1. # Calculate state 4 self.p[3] = p1 T4s, x4s = fluid.T_s(self.s[2], p=p1, quality=True) h4s = fluid.h(T=T4s, x=x4s) # Adjust enthlpy fall by the turbine efficiency self.h[3] = self.h[2] + (h4s - self.h[2]) * eta34 self.T[3], self.x[3] = fluid.T_h(h=self.h[3], p=p1, quality=True) self.d[3] = fluid.d(T=self.T[3], p=p1, x=self.x[3]) # Now that we have density, we can use it directly to solve for entropy self.s[3] = fluid.s(T=self.T[3], d=self.d[3]) self.w = [self.h[0] - self.h[1], 0., self.h[2] - self.h[3], 0.] # Adjust the required boiler heat by the efficiency self.q = [ 0., (self.h[2] - self.h[1]) / eta23, 0., self.h[0] - self.h[3] ] self.meta['qH'] = self.q[1] self.meta['qL'] = self.q[3] self.meta['wnet'] = self.w[2] + self.w[0] self.meta['eta'] = self.meta['wnet'] / self.meta['qH']
sys.stdout = save_stdout species, type, plin, Tlin, vlin, hlin, slin, up, uT, uE, uM, uV = pmcgi.argparse( [('id'), ('type', str, 'Ts'), ('plin', str, 'false'), ('Tlin', str, 'false'), ('vlin', str, 'false'), ('hlin', str, 'false'), ('slin', str, 'false'), ('up'), ('uT'), ('uE'), ('uM'), ('uV')]) # Apply the units pm.config['unit_temperature'] = uT pm.config['unit_pressure'] = up pm.config['unit_matter'] = uM pm.config['unit_energy'] = uE pm.config['unit_volume'] = uV this = pm.get(species) # Calculate the states F = pm.get(species) if plin == 'true': plines = None else: plines = [] if Tlin == 'true': Tlines = None else: Tlines = [] if vlin == 'true':
# # Brayton cycle demo # C.R. Martin (c) 2016-2018 # GPL v3.0 # Enjoy! # import pyromat as pyro import numpy as np import matplotlib.pyplot as plt air = pyro.get('ig.air') # Force the unit system into kJ,kg,bar,K pyro.config['unit_energy'] = 'kJ' pyro.config['unit_matter'] = 'kg' pyro.config['unit_pressure'] = 'bar' pyro.config['unit_temperature'] = 'K' # Let's design a gas turbine with a 100kW power output Wnet = 100. # There are three processes separating four states in a brayton cycle. # # (1) ---|Compressor|---> (2) ---|Combustor|---> (3) ---|Turbine|---> (4) # #(1) The inlet is ambient temperature and pressure. In our example, we will # use 1.013bar and 300K for p1 and T1 p1 = 1.013 T1 = 300. #|Compressor| is ideally an isentropic process designed to compress the # incoming air to a certain pressure ratio, pr. Let's use pr=12.
from fluid_properties import * import matplotlib.pyplot as plt import numpy as np import math import pyromat as pm from pyXSteam.XSteam import XSteam from CoolProp.HumidAirProp import HAPropsSI def wall_element_calc(case, y_plus, V_inf, L_characteristic, epsilon, fluid_name, T1, p1): # get working_fluid properties pm.config['unit_pressure'] = 'Pa' pm.config['unit_temperature'] = 'C' working_fluid = pm.get('ig.' + fluid_name) rho = working_fluid.d(T=T1, p=p1) mu = working_fluid.mu = thermophysical_properties('mu', fluid_name, T1) Re = (rho * V_inf * L_characteristic) / mu if case == "pipe_flow": cf = colebrook_calc(epsilon, L_characteristic, Re) / 4 tau_w = cf * 0.5 * rho * V_inf**2 u_star = math.sqrt(tau_w / rho) y = (y_plus * mu) / (rho * u_star) return y y = wall_element_calc('pipe_flow', 30, 30, 0.001, 0, 'air', 15, 101325) print(y)
def tsplot(self, ax=None, fig=None, slabels=True, satstyle=None, procstyle=None, statestyle=None): """ """ # If the properties are empty, abort if not self.T: raise PMCycleError( 'The plot cannot be generated for a cycle that has not yet been updated. Run update().' ) if isinstance(self.T, np.ndarray) and self.T.size > 1: raise PMCycleError( 'Plotting arrays of cycle data is not supported.') uE = self.lastparam['unit_energy'] uM = self.lastparam['unit_matter'] uT = self.lastparam['unit_temperature'] ax = self._initplot('s (%s/%s%s)' % (uE, uM, uT), 'T (%s)' % (uT), ax=ax, fig=fig) if satstyle is None: satstyle = {'lw': 2, 'c': 'k', 'ls': 'solid', 'marker': 'None'} if procstyle is None: procstyle = {'lw': 2, 'c': 'r', 'ls': 'solid', 'marker': 'None'} if statestyle is None: statestyle = { 'ls': 'None', 'marker': 'o', 'mec': 'k', 'mew': 1, 'mfc': 'r', 'ms': 6 } boxstyle = {'fc': 'w', 'ec': 'k', 'boxstyle': 'square,pad=.25'} # Get the working fluid object if isinstance(self.lastparam['fluid'], str): fluid = pm.get(self.lastparam['fluid']) else: fluid = self.lastparam['fluid'] # Plot the dome Tc, pc = fluid.critical() Tt, pt = fluid.triple() T = np.linspace(Tt, 0.99999 * Tc, 100) sL, sV = fluid.ss(T=T) ax.plot(sL, T, **satstyle) ax.plot(sV, T, **satstyle) # Isentropic pump is a straight vertical line ax.plot([self.s[0], self.s[1]], [self.T[0], self.T[1]], **procstyle) # Isobaric boiler s = np.linspace(self.s[1], self.s[2], 51) T = fluid.T_s(s, p=self.p[1]) ax.plot(s, T, **procstyle) # Isobaric super-heater s = np.linspace(self.s[2], self.s[3], 21) T = fluid.T_s(s, p=self.p[1]) ax.plot(s, T, **procstyle) # Isentropic turbine is a straight vertical line ax.plot([self.s[3], self.s[4]], [self.T[3], self.T[4]], **procstyle) # Isobaric condensation s = np.linspace(self.s[4], self.s[0], 51) T = fluid.T_s(s, p=self.p[0]) ax.plot(s, T, **procstyle) # deterine offsets in T and s space ds = ax.get_xlim() ds = .02 * (ds[1] - ds[0]) dT = ax.get_ylim() dT = .02 * (dT[1] - dT[0]) # Deal with the state labels ax.plot(self.s[0], self.T[0], **statestyle) tt = ax.text(self.s[0] - ds, self.T[0] - dT, '1', ha='right', va='top') tt.set_bbox(boxstyle) ax.plot(self.s[1], self.T[1], **statestyle) tt = ax.text(self.s[1] - ds, self.T[1] + dT, '2', ha='right', va='bottom') tt.set_bbox(boxstyle) ax.plot(self.s[2], self.T[2], **statestyle) tt = ax.text(self.s[2] - ds, self.T[2] + dT, '3', ha='right', va='bottom') tt.set_bbox(boxstyle) ax.plot(self.s[3], self.T[3], **statestyle) tt = ax.text(self.s[3] + ds, self.T[3] + dT, '4', ha='left', va='bottom') tt.set_bbox(boxstyle) ax.plot(self.s[4], self.T[4], **statestyle) tt = ax.text(self.s[4] + ds, self.T[4] - dT, '5', ha='left', va='top') tt.set_bbox(boxstyle) return ax
def update(self): """Update the cycle states and processes """ if self._test(require=('fluid', 'p1', 'p2', 'T4', 'q34', 'eta12', 'eta23', 'eta34', 'eta45')): raise PMCycleError( 'There was a problem with the Rankine Cycle configuration') # Determine the super-heat solution mode mode = 0 if self.param['q34'] is not None: mode = 2 elif self.param['T4'] is not None: mode = 1 fluid = self.param['fluid'] # Parse the subsance input if isinstance(fluid, str): fluid = pm.get(fluid) if not isinstance(fluid, pm.reg.registry['mp1']): raise PMCycleError('RankineCycle requires substances of class mp1') p1, p2, T4, q34, eta12, eta23, eta34, eta45 = self._prepparam( 'p1', 'p2', 'T4', 'q34', 'eta12', 'eta23', 'eta34', 'eta45') # Get the critical and triple points Tc, pc = fluid.critical() Tt, pt = fluid.triple() # Test the inputs # pt < p1 < pc if (p1 <= pt).any(): raise PMCycleError( 'RankineSHCycle requires p1 to be greater than the triple-point pressure' ) elif (p1 >= pc).any(): raise PMCycleError( 'RankineSHCycle requires p1 to be less than the critical-point pressure' ) # p1 < p2 < pc if (p2 <= p1).any(): raise PMCycleError( 'RankineSHCycle requires p2 to be greater than p1.') elif (p2 >= pc).any(): raise PMCycleError( 'RankineSHCycle requires p1 to be less than the critical-point pressure' ) if (eta12<0).any() or (eta12>1).any() or (eta23<0).any() or \ (eta23>1).any() or (eta34<0).any() or (eta34>1).any() or\ (eta45<0).any() or (eta45>1).any(): raise PMCycleError( 'RankineSHCycle efficiencies must be between 0 and 1.') # Update the lastparam values self._writelast() # Calculate state 1 self.p[0] = p1 self.T[0] = fluid.Ts(p=p1) self.s[0], _ = fluid.ss(T=self.T[0]) self.h[0], _ = fluid.hs(T=self.T[0]) self.d[0], _ = fluid.ds(T=self.T[0]) self.x[0] = 0. # Calculate state 2 self.p[1] = p2 # Start with the isentropic performance T2s = fluid.T_s(s=self.s[0], p=p2) h2s = fluid.h(T=T2s, p=p2) # Adjust the enthalpy rise by the inverse of pump efficiency self.h[1] = self.h[0] + (h2s - self.h[0]) / eta12 self.T[1], self.x[1] = fluid.T_h(h=self.h[1], p=p2, quality=True) self.d[1] = fluid.d(T=self.T[1], p=p2, x=self.x[1]) # Now that we have density, we can use it directly to solve for entropy self.s[1] = fluid.s(T=self.T[1], d=self.d[1]) # Calculate state 3 self.p[2] = p2 self.T[2] = fluid.Ts(p=p2) _, self.s[2] = fluid.ss(T=self.T[2]) _, self.h[2] = fluid.hs(T=self.T[2]) _, self.d[2] = fluid.ds(T=self.T[2]) self.x[2] = 1. # Calculate states 4 by case # If the target for state5 is a saturated vapor if mode == 0: # force s4 to be equal to the sV at p1 self.p[3] = p2 _, self.s[3] = fluid.ss(p=p1) self.T[3], self.x[3] = fluid.T_s(s=self.s[3], p=p2, quality=True) self.d[3] = fluid.d(T=self.T[3], p=p2, x=self.x[3]) self.h[3] = fluid.h(T=self.T[3], d=self.d[3]) # If state 4 is temperature limited elif mode == 1: # Test the temperature if (T4 < self.T[2]).any(): raise PMCycleError( 'RankineSHCycle requires T4 to be greater than T3.') self.p[3] = p2 self.T[3] = T4 self.h[3], self.s[3], self.d[3] = fluid.hsd(T=T4, p=p2) self.x[3] = -1. # If state 4 is determined by heat addition else: self.p[3] = p2 self.h[3] = self.h[2] + q34 self.T[3], self.x[3] = fluid.T_h(h=self.h[3], p=p2, quality=True) self.d[3] = fluid.d(T=self.T[3], p=p2, x=self.x[3]) self.s[3] = fluid.s(T=self.T[3], d=self.d[3]) # Calculate state 5 self.p[4] = p1 T5s, x5s = fluid.T_s(self.s[3], p=p1, quality=True) h5s = fluid.h(T=T5s, p=p1, x=x5s) # Adjust enthlpy fall by the turbine efficiency self.h[4] = self.h[3] + (h5s - self.h[3]) * eta45 self.T[4], self.x[4] = fluid.T_h(h=self.h[4], p=p1, quality=True) self.d[4] = fluid.d(T=self.T[4], p=p1, x=self.x[4]) # Now that we have density, we can use it directly to solve for entropy self.s[4] = fluid.s(T=self.T[4], d=self.d[4]) self.w = [self.h[0] - self.h[1], 0., 0., self.h[3] - self.h[4], 0.] self.q = [ 0., self.h[2] - self.h[1], self.h[3] - self.h[2], 0., self.h[0] - self.h[4] ] self.meta['qH'] = self.q[1] + self.q[2] self.meta['qL'] = self.q[3] self.meta['wnet'] = self.w[3] + self.w[0] self.meta['eta'] = self.meta['wnet'] / self.meta['qH']
import pyromat as pm import numpy as np import matplotlib.pylab as pylab #Tester for MP1. Not exhaustive, as other bugs have also been identified. #get steam mp1obj = pm.get('mp.H2O') #Public methods #Tlim mp1obj.Tlim() mp1obj.Tlim(p=1) #Plim mp1obj.plim() mp1obj.plim(T=1000) #critical mp1obj.critical() mp1obj.critical(density=True) #triple mp1obj.triple() #ps mp1obj.ps(T=373) #should be around 1 bar mp1obj.ps(T=[373, 373]) mp1obj.ps(T=np.asarray(373)) mp1obj.ps(T=np.asarray([373, 373])) #print(mp1obj.ps(T=373))
#color = 'b' # Blue # This is a True/False flag to deactivate the plot text show_text = True # This is a True/False flag to allow over-plotting of previous results clear_plots = False # Liquid water reservoir is at ambient pressure p1 = 1.013 # Operating pressure of the boiler p2 = 18.3 # 18.3 bar is roughly 250 psig #p2 = 11.4 # 11.4 bar is roughly 150 psig # How much work do we need? Wnet = 100. # Let's make a 100kW engine # Get the steam data steam = pyro.get('mp.H2O') # Assume the reservoir is a saturated liquid, as it would likely # be coming out of the condenser. In reality some supercooling # is likely, but we will neglect it here. # States (5) and (1) will straddle the dome T1 = steam.Ts(p1) p5 = p1 # Since it is spanning the done, (5) and (1) share T and p T5 = T1 h1, h5 = steam.hs(p=p1, T=T1) # these are faster with T and p s1, s5 = steam.ss(p=p1, T=T1) d1, d5 = steam.ds(p=p1, T=T1) # Isentropic compression of liquid water # It is common to assume that T1=T2 since liquid is very close # to incompressible. To compare results, remove the comment
# In this code, we generate a surface plot that spans the saturation # curve from triple point to critical point. We'll add two red lines # to show where the liquid- and gas-phase saturation properties are in # each plot. import pyromat as pyro import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D import numpy as np # Start with a blank slate plt.close('all') # Get the steam object S = pyro.get('mp.H2O') # Get the critical and triple point properties Tt,pt = S.triple() Tc,pc = S.critical() # Explore the temperatures between Tt and Tc in 5K increments Ts = np.arange(Tt,Tc,5.) # Now, obtain the saturation properties # Note that when a saturation property is called with the "tp" flag True # it also returns the saturation temperature and pressure. # This is more efficient than separate calls to Ts and ps. Ts,ps,dL,dV = S.ds(T=Ts,tp=True) # It is faster to explicitly pass both Ts and ps to the saturation methods. # This saves them a redundant call to Ts or ps since both are required anyway.
from fluid_properties import * import matplotlib.pyplot as plt import numpy as np import math import pyromat as pm from pyXSteam.XSteam import XSteam from CoolProp.HumidAirProp import HAPropsSI ## Configure Pyromat pm.config['unit_pressure'] = 'Pa' pm.config['unit_temperature'] = 'C' # Live example working_fluid = pm.get('ig.air') cp = working_fluid.cp(25, 101325) steamTable = XSteam(XSteam.UNIT_SYSTEM_MKS) # m/kg/sec/°C/bar/W ''' cp=steamTable.Cp_pt(25,1) '''
def update(self): """Update the cycle states and processes """ if self._test(require=('fluid', 'p1', 'p2', 'T1', 'T3', 'q23', 'eta12', 'eta23', 'eta34')): raise PMCycleError( 'There was a problem with the Brayton Cycle configuration') # Detect the solution mode if self.param['T3'] is not None: mode = 0 elif self.param['q23'] is not None: mode = 1 else: raise PMCycleError( 'BraytonCycle requires T3 or q23 to be specified.') fluid = self.param['fluid'] # Parse the subsance input if isinstance(fluid, str): fluid = pm.get(fluid) p1, p2, T1, T3, q23, eta12, eta23, eta34 = self._prepparam( 'p1', 'p2', 'T1', 'T3', 'q23', 'eta12', 'eta23', 'eta34') # p1 < p2 < pc if (p2 <= p1).any(): raise PMCycleError( 'BraytonCycle requires p2 to be greater than p1.') if (T3 <= T1).any(): raise PMCycleError('BraytonCycle requires T3 to be less than T1.') self._writelast() # Calculate state 1 self.p[0] = p1 self.T[0] = T1 self.s[0] = fluid.s(T=T1, p=p1) self.h[0] = fluid.h(T=T1, p=p1) self.d[0] = fluid.d(T=T1, p=p1) self.x[0] = -1 # Calculate state 2 self.p[1] = p2 Ts2 = fluid.T_s(s=self.s[0], p=p2) hs2 = fluid.h(T=Ts2, p=p2) # Modify the work by the compressor efficiency self.h[1] = self.h[0] + (hs2 - self.h[0]) / eta12 self.T[1] = fluid.T_h(h=self.h[1], p=p2) self.s[1] = fluid.s(T=self.T[1], p=p2) self.d[1] = fluid.d(T=self.T[1], p=p2) self.x[1] = -1 if mode == 0: # Calculate state 3 self.p[2] = p2 self.T[2] = T3 self.s[2] = fluid.s(T=T3, p=p2) self.h[2] = fluid.h(T=T3, p=p2) self.d[2] = fluid.d(T=T3, p=p2) self.x[2] = -1 else: self.p[2] = p2 self.h[2] = self.h[1] + q23 * eta23 self.T[2] = fluid.T_h(h=self.h[2], p=p2) self.s[2] = fluid.s(T=self.T[2], p=p2) self.d[2] = fluid.d(T=self.d[2], p=p2) self.x[2] = -1 # Calculate state 4 self.p[3] = p1 T4s = fluid.T_s(s=self.s[2], p=p1) h4s = fluid.h(T=T4s, p=p1) self.h[3] = self.h[2] + (h4s - self.h[2]) * eta34 self.T[3] = fluid.T_h(h=self.h[3], p=p1) self.s[3] = fluid.s(T=self.T[3], p=p1) self.d[3] = fluid.d(T=self.T[3], p=p1) self.x[3] = -1 self.w = [self.h[0] - self.h[1], 0., self.h[2] - self.h[3], 0.] self.q = [0., self.h[2] - self.h[1], 0., self.h[0] - self.h[3]] self.meta['qH'] = self.q[1] self.meta['qL'] = self.q[3] self.meta['wnet'] = self.w[2] + self.w[0] self.meta['eta'] = self.meta['wnet'] / self.meta['qH']
import matplotlib.pyplot as plt import numpy as np uT = 'K' up = 'kPa' uM = 'kg' uE = 'kJ' uV = 'm3' pm.config['unit_temperature'] = uT pm.config['unit_pressure'] = up pm.config['unit_matter'] = uM pm.config['unit_energy'] = uE pm.config['unit_volume'] = uV F = pm.get('mp.H2O') f = plt.figure() ax = f.add_subplot(111) def P_h(T, h): P_h = pmsolve.solve1n('p', f=F.T_h, param_init=100) return P_h(T, h=h) def P_d(T, d): P_d = pmsolve.solve1n('p', f=F.ds, param_init=1) return P_d(d, T=T) def Psat_d(d):
import pyromat as pm import numpy as np pm.config["unit_pressure"] = "kPa" pm.config["def_p"] = 100 mp_water = pm.get("mp.H2O") # <-- for multi-phase water properties p1 = 10 # <-- given p2 = 400 # <-- given v1 = 1/mp_water.ds(p=p1)[0] w_pump1 = v1*(p2-p1) h2 = h1+w_pump1 print(f"Work required by pump 1: {round(float(w_pump1),1)} kJ/kg") Work required by pump 1: 0.4 kJ/kg p5 = 4000 # <-- given T5 = 400+273.15 # K <-- given h5 = mp_water.h(p=p5, T=T5) s5 = mp_water.s(p=p5, T=T5) s6 =s5 p6 = 400 # <-- given T6, x6 = mp_water.T_s(s=s6, p=p6, quality=True) h6 = mp_water.h(x=x6, p=p6) print(f"Quality of bled steam: {round(float(x6),4)}") Quality of bled steam: 0.9757
#Set up the species spinner lu.setspeciesselect(P, species, speciesline, 56) # Apply the units within pyromat pm.config['unit_temperature'] = uT pm.config['unit_pressure'] = up pm.config['unit_matter'] = uM pm.config['unit_energy'] = uE pm.config['unit_volume'] = uV # # # # # # # # # # # # # # Calculate the states # # # # # # # # # # # # # # #Get the substance object F = pm.get(species) #Set blank labels for each of the properties. #These will be reset by each of the methods for re-entry into the input boxes. Tval = pval = '' #check for cases where too few or too many properties are specified (zero specified results in defaults) ins = np.array([p1, T1 ]) #test array for counting how many properties were specified if (sum(ins >= 0) == 0): p1 = 101.325 elif (sum(ins >= 0) != 1): lu.perror(P, 'Must specify pressure or temperature, but not both.', errline) #Determine which two properties were chosen, calculate the state
import pyromat as pm pm.config["unit_pressure"] = "kPa" pm.config["def_p"] = 100 mp_water = pm.get("mp.H2O") #saturated liquid, thus x = 0 p1 = 10 # <--given T1 = mp_water.Ts(p=p1)[0] s1 = mp_water.ss(p=p1)[0] p2 = 2000 # <--given and converted to kPa s2= s1 v = 1/mp_water.ds(p=p1)[0] w_p = v*(p2-p1) print(f"Work required by pump: {round(float(w_p),1)} kJ/kg") h1 = mp_water.hs(p=p1)[0] h2 = h1+w_p T2 = mp_water.T_h(p=p2,h=h2) print(f"h2 = {round(float(h2),1)} kJ/kg") h2 = 193.8 kJ/kg # steam leaves the boiler as saturated vapor, thus x = 1 p3 = p2 T3 = mp_water.Ts(p=p3) h3 = mp_water.hs(p=p3)[1] s3dash = mp_water.ss(p=p3)[0]