public double output(IF97Parameters key, double T, double p) { switch (key) { case IF97Parameters.T: return(T); case IF97Parameters.p: return(p); case IF97Parameters.d: return(rhomass(T, p)); case IF97Parameters.h: return(hmass(T, p)); case IF97Parameters.s: return(smass(T, p)); case IF97Parameters.u: return(umass(T, p)); case IF97Parameters.cp: return(cpmass(T, p)); case IF97Parameters.cv: return(cvmass(T, p)); case IF97Parameters.w: return(speed_sound(T, p)); case IF97Parameters.mu: return(visc(T, rhomass(T, p))); // Viscosity is a function of rho. case IF97Parameters.tc: return(tcond(T, p, rhomass(T, p))); // Conductivity needs p and rho. case IF97Parameters.drhodp: return(drhodp(T, p)); // For verification testing. } throw new ArgumentOutOfRangeException("Unable to match input parameters"); }
static double RegionOutputBackward(double p, double X, IF97Parameters inkey) { // Note that this routine returns only temperature (IF97_T). All other values should be // calculated from this temperature and the known pressure using forward equations. // Setup Backward Regions for output // Make sure input and output keys are valid for Backward formulas if ((inkey != IF97Parameters.h) && (inkey != IF97Parameters.s)) throw new ArgumentException("Backward Formulas take variable inputs of Enthalpy or Entropy only."); // Get Saturation Parameters IF97REGIONS region = RegionDetermination_pX(p, X, inkey); switch (region) { case IF97REGIONS.REGION_1: if (inkey == IF97Parameters.h) return B1H.T_pX(p, X); else return B1S.T_pX(p, X); case IF97REGIONS.REGION_2: if (inkey == IF97Parameters.h) { if (p <= 4.0) return B2aH.T_pX(p, X); else if (X >= BackwardsRegion.H2b2c_p(p)) return B2bH.T_pX(p, X); else return B2cH.T_pX(p, X); } else { if (p <= 4.0) return B2aS.T_pX(p, X); else if (X >= Constants.S2bc) return B2bS.T_pX(p, X); else return B2cS.T_pX(p, X); }; case IF97REGIONS.REGION_3: if (inkey == IF97Parameters.h) { if (X <= BackwardsRegion.H3ab_p(p)) return B3aH.T_pX(p, X); else return B3bH.T_pX(p, X); } else { if (X <= Constants.Scrit) return B3aS.T_pX(p, X); else return B3bS.T_pX(p, X); }; case IF97REGIONS.REGION_4: return Tsat97(p); default: throw new ArgumentOutOfRangeException("Unable to match region"); } } // Region Output backward
static IF97REGIONS RegionDetermination_pX(double p, double X, IF97Parameters inkey) { // Setup needed Region Equations for region determination // Saturation Region Limit Variables double Tsat = 0; double Xliq = 0; double Xvap = 0; // Check overall boundary limits if ((p < Constants.Pmin) || (p > Constants.Pmax)) throw new ArgumentOutOfRangeException("Pressure out of range"); double Xmin = R1.output(inkey, Constants.Tmin, p); double Xmax = R2.output(inkey, Constants.Tmax, p); if ((X < Xmin) || (X > (Xmax + 1.0E-10))) { if (inkey == IF97Parameters.h) { throw new ArgumentOutOfRangeException("Enthalpy out of range"); } else { throw new ArgumentOutOfRangeException("Entropy out of range"); } } // Check saturation Dome first if (p <= Constants.Pcrit) { Tsat = Region4.Tsat97(p); Xliq = R1.output(inkey, Tsat, p); Xvap = R2.output(inkey, Tsat, p); if ((Xliq <= X) && (X <= Xvap)) { // Within Saturation Dome return IF97REGIONS.REGION_4; // Region 4 } } // End Check saturation Dome // Check values below 16.529 MPa if (p <= Constants.P23min) { // p <= P23min (saturation dome) if (X <= Xliq) return IF97REGIONS.REGION_1; else if (X >= Xvap) return IF97REGIONS.REGION_2; else return IF97REGIONS.REGION_4; } // Check values above 16.529 MPa else if (X <= R1.output(inkey, Constants.T23min, p)) return IF97REGIONS.REGION_1; else if (X >= R2.output(inkey, Region23_p(p), p)) return IF97REGIONS.REGION_2; else return IF97REGIONS.REGION_3; } // Region Output backward
static double Q_pX(double p, double X, IF97Parameters inkey) { double Xliq, Xvap; if ((p < Constants.Pmin) || (p > Constants.Pmax)) { throw new ArgumentOutOfRangeException("Pressure out of range"); } else if (p < Constants.Ptrip) { return 0; //Liquid, at all temperatures } else if (p > Constants.Pcrit) { double t; switch (inkey) { case IF97Parameters.h: case IF97Parameters.s: t = RegionOutputBackward(p, X, inkey); break; case IF97Parameters.u: case IF97Parameters.d: default: // There are no reverse functions for t(p,U) or t(p,rho) throw new ArgumentException("Quality cannot be determined for these inputs."); } if (t < Constants.Tcrit) return 1.0; // Vapor, at all pressures above critical point else // Supercritical Region (p>Pcrit) && (t>Tcrit) throw new ArgumentException("Quality not defined in supercritical region."); } else { switch (inkey) { case IF97Parameters.h: case IF97Parameters.s: case IF97Parameters.u: Xliq = RegionOutput(inkey, Tsat97(p), p, SatState.LIQUID); Xvap = RegionOutput(inkey, Tsat97(p), p, SatState.VAPOR); return Math.Min(1.0, Math.Max(0.0, (X - Xliq) / (Xvap - Xliq))); case IF97Parameters.d: Xliq = 1.0 / RegionOutput(IF97Parameters.d, Tsat97(p), p, SatState.LIQUID); Xvap = 1.0 / RegionOutput(IF97Parameters.d, Tsat97(p), p, SatState.VAPOR); X = 1.0 / X; return Math.Min(1.0, Math.Max(0.0, (X - Xliq) / (Xvap - Xliq))); default: throw new ArgumentException("Quality cannot be determined for these inputs."); } } // If all else fails, which it shouldn't... throw new ArgumentException("Quality cannot be determined for these inputs."); }
static double BackwardOutputHS(IF97Parameters outkey, double h, double s) { // Note that this routine returns only temperature (IF97_T). All other values should be // calculated from this temperature and the known pressure using forward equations. // Setup Backward Regions for output // double Pval = 0, Tval = 0; // Make sure output keys are valid for Backward_HS formulas if ((outkey != IF97Parameters.p) && (outkey != IF97Parameters.T)) throw new ArgumentException("Backward HS Formulas output Temperature or Pressure only."); // Get Saturation Parameters IF97BACKREGIONS region = RegionDetermination_HS(h, s); switch (region) { case IF97BACKREGIONS.BACK_1: Pval = B1HS.p_hs(h, s); break; case IF97BACKREGIONS.BACK_2A: Pval = B2aHS.p_hs(h, s); break; case IF97BACKREGIONS.BACK_2B: Pval = B2bHS.p_hs(h, s); break; case IF97BACKREGIONS.BACK_2C: Pval = B2cHS.p_hs(h, s); break; case IF97BACKREGIONS.BACK_3A: Pval = B3aHS.p_hs(h, s); break; case IF97BACKREGIONS.BACK_3B: Pval = B3bHS.p_hs(h, s); break; case IF97BACKREGIONS.BACK_4: if (s >= Constants.SgT23) // T(h,s) only defined over part of the 2-phase region Tval = B4HS.t_hs(h, s); else throw new ArgumentOutOfRangeException("Entropy out of range"); break; default: throw new ArgumentOutOfRangeException("Unable to match region"); } if (outkey == IF97Parameters.p) // Returning Pressure (IF97_P) if (region == IF97BACKREGIONS.BACK_4) // return psat97(Tval); // Not REGION 4, already have pressure else // return Pval; // REGION 4, Calculate Psat from Tsat // else // ELSE Returning Temperature if (region == IF97BACKREGIONS.BACK_4) // return Tval; // REGION 4, already have Temperature else // return RegionOutputBackward(Pval, h, IF97Parameters.h); // Not REGION 4 Calc from Backward T(p,h) } // Region Output backward
} // SatSubRegionAdjust public static double output(IF97Parameters key, double T, double p, SatState State) { double rho; char region = BackwardsRegion3.RegionDetermination(T, p); // if this is a saturated vapor or liquid function, make sure we're on // the correct side of the saturation curve and adjust region before // calculating density. region = SatSubRegionAdjust(State, p, region); rho = 1 / BackwardsRegion3.Region3_v_TP(region, T, p); #if REGION3_ITERATE // Use previous rho value from algebraic equations // as an initial guess to solve rhomass iteratively // with Newton-Raphson rho = rhomass(T, p, rho); #endif switch (key) // return all properties using the new rho value { case IF97Parameters.d: return(rho); case IF97Parameters.h: return(hmass(T, rho)); case IF97Parameters.s: return(smass(T, rho)); case IF97Parameters.u: return(umass(T, rho)); case IF97Parameters.cp: return(cpmass(T, rho)); case IF97Parameters.cv: return(cvmass(T, rho)); case IF97Parameters.w: return(speed_sound(T, rho)); case IF97Parameters.mu: return(visc(T, rho)); case IF97Parameters.tc: return(tcond(T, p, rho)); case IF97Parameters.drhodp: return(drhodp(T, rho)); default: throw new ArgumentException("Bad key to output"); // JPH: changed this to invalid_argument exception } }
} // Region Output backward static int BackwardRegion(double p, double X, IF97Parameters inkey) { // This routine is for testing purposes only. It returns the // Region as an integer based on the backward evaluation of either // (p,h) or (p,s) // Make sure input and output keys are valid for Backward formulas if ((inkey != IF97Parameters.h) && (inkey != IF97Parameters.s)) throw new ArgumentException("Backward Formulas take variable inputs of Enthalpy or Entropy only."); IF97REGIONS region = RegionDetermination_pX(p, X, inkey); switch (region) { case IF97REGIONS.REGION_1: return 1; case IF97REGIONS.REGION_2: return 2; case IF97REGIONS.REGION_3: return 3; case IF97REGIONS.REGION_4: return 4; default: return 0; } }
} // Region Output backward static double rho_pX(double p, double X, IF97Parameters inkey) { // NOTE: This implementation works, and with the 2016 Supplementary Release // for v(p,T) for Region 3 implemented, it is no longer iterative. // However, the 2014 Supplementary Release for v(p,h) and v(p,s) are // more direct and may be slightly faster, since only one algebraic // equation is needed instead of two in Region 3. double T = RegionOutputBackward(p, X, inkey); if (RegionDetermination_pX(p, X, inkey) == IF97REGIONS.REGION_4) { // If in saturation dome double Tsat = Tsat97(p); double Xliq = R1.output(inkey, Tsat, p); double Xvap = R2.output(inkey, Tsat, p); double vliq = 1.0 / R1.output(IF97Parameters.d, Tsat, p); double vvap = 1.0 / R2.output(IF97Parameters.d, Tsat, p); return 1.0 / (vliq + (X - Xliq) * (vvap - vliq) / (Xvap - Xliq)); // Return Mixture Density } else { // else return RegionOutput(IF97Parameters.d, T, p, SatState.NONE); } }
static double X_pQ(IF97Parameters inkey, double p, double Q) { double Xliq, Xvap; if ((p < Constants.Ptrip) || (p > Constants.Pcrit)) throw new ArgumentOutOfRangeException("Pressure out of range"); if ((Q < 0.0) || (Q > 1.0)) throw new ArgumentOutOfRangeException("Quality out of range"); switch (inkey) { case IF97Parameters.h: case IF97Parameters.s: case IF97Parameters.u: Xliq = RegionOutput(inkey, Tsat97(p), p, SatState.LIQUID); Xvap = RegionOutput(inkey, Tsat97(p), p, SatState.VAPOR); return Q * Xvap + (1 - Q) * Xliq; case IF97Parameters.d: Xliq = 1.0 / RegionOutput(IF97Parameters.d, Tsat97(p), p, SatState.LIQUID); Xvap = 1.0 / RegionOutput(IF97Parameters.d, Tsat97(p), p, SatState.VAPOR); return 1.0 / (Q * Xvap + (1 - Q) * Xliq); default: throw new ArgumentException("Mixture property undefined"); } }
public static double RegionOutput(IF97Parameters outkey, double T, double p, SatState State) { IF97REGIONS region = RegionDetermination_TP(T, p); switch (region) { case IF97REGIONS.REGION_1: if (State == SatState.VAPOR) return R2.output(outkey, T, p); // On saturation curve and need the Vapor phase else return R1.output(outkey, T, p); // otherwise, use Liquid Region 1 case IF97REGIONS.REGION_2: if (State == SatState.LIQUID) return R1.output(outkey, T, p); // On saturation curve and need the Liquid phase else return R2.output(outkey, T, p); // otherwise, use Vapor Region 2 case IF97REGIONS.REGION_3: return Region3.output(outkey, T, p, State); case IF97REGIONS.REGION_4: if (State == SatState.VAPOR) { return R2.output(outkey, T, p); } else if (State == SatState.LIQUID) { return R1.output(outkey, T, p); } else { throw new ArgumentOutOfRangeException("Cannot use Region 4 with T and p as inputs"); } case IF97REGIONS.REGION_5: return R5.output(outkey, T, p); } throw new ArgumentOutOfRangeException("Unable to match region"); }