bool is_in_closed_range_around(const T& radius, const T& center, const T& x) { return is_in_closed_range(center - radius, center + radius, x); }
void CoolProp::BicubicBackend::update(CoolProp::input_pairs input_pair, double val1, double val2) { // Clear cached values clear(); // Check the tables and build if necessary check_tables(); // To start, set quality to value that is for single-phase _Q = -1000; bool is_mixture = (this->AS->get_mole_fractions().size() >= 2); if (is_mixture){ // For mixtures, the construction of the coefficients is delayed until this // function so that the set_mole_fractions function can be called build_coeffs(single_phase_logph, coeffs_ph); build_coeffs(single_phase_logpT, coeffs_pT); } // Flush the cached indices (set to large number) cached_single_phase_i = std::numeric_limits<std::size_t>::max(); cached_single_phase_j = std::numeric_limits<std::size_t>::max(); cached_saturation_iL = std::numeric_limits<std::size_t>::max(); cached_saturation_iV = std::numeric_limits<std::size_t>::max(); switch(input_pair){ case HmolarP_INPUTS:{ _hmolar = val1; _p = val2; if (!single_phase_logph.native_inputs_are_in_range(_hmolar, _p)){ // Use the AbstractState instance using_single_phase_table = false; if (get_debug_level() > 5){ std::cout << "inputs are not in range"; } throw ValueError(format("inputs are not in range, hmolar=%Lg, p=%Lg", static_cast<CoolPropDbl>(_hmolar), _p)); } else{ using_single_phase_table = true; // Use the table ! std::size_t iL, iV, iclosest = 0; CoolPropDbl hL = 0, hV = 0; SimpleState closest_state; if ( (is_mixture && PhaseEnvelopeRoutines::is_inside(phase_envelope, iP, _p, iHmolar, _hmolar, iclosest, closest_state)) || (!is_mixture && pure_saturation.is_inside(iP, _p, iHmolar, _hmolar, iL, iV, hL, hV)) ) { using_single_phase_table = false; _Q = (static_cast<double>(_hmolar)-hL)/(hV-hL); if(!is_in_closed_range(0.0,1.0,static_cast<double>(_Q))){ throw ValueError("vapor quality is not in (0,1)"); } else{ cached_saturation_iL = iL; cached_saturation_iV = iV; } } else{ // Find and cache the indices i, j selected_table = SELECTED_PH_TABLE; single_phase_logph.find_native_nearest_good_cell(_hmolar, _p, cached_single_phase_i, cached_single_phase_j); CellCoeffs &cell = coeffs_ph[cached_single_phase_i][cached_single_phase_j]; if (!cell.valid()){ if (cell.has_valid_neighbor()){ // Get new good neighbor cell.get_alternate(cached_single_phase_i, cached_single_phase_j); } else{ if (!cell.valid()){throw ValueError(format("Cell is invalid and has no good neighbors for hmolar = %g, p= %g",val1,val2));} } } } } break; } case HmassP_INPUTS:{ update(HmolarP_INPUTS, val1 * AS->molar_mass(), val2); // H: [J/kg] * [kg/mol] -> [J/mol] return; } case PUmolar_INPUTS: case PSmolar_INPUTS: case DmolarP_INPUTS:{ CoolPropDbl otherval; parameters otherkey; switch(input_pair){ case PUmolar_INPUTS: _p = val1; _umolar = val2; otherval = val2; otherkey = iUmolar; break; case PSmolar_INPUTS: _p = val1; _smolar = val2; otherval = val2; otherkey = iSmolar; break; case DmolarP_INPUTS: _rhomolar = val1; _p = val2; otherval = val1; otherkey = iDmolar; break; default: throw ValueError("Bad (impossible) pair"); } using_single_phase_table = true; // Use the table (or first guess is that it is single-phase)! std::size_t iL, iV; CoolPropDbl zL = 0, zV = 0; if (pure_saturation.is_inside(iP, _p, otherkey, otherval, iL, iV, zL, zV)){ using_single_phase_table = false; if (otherkey == iDmolar){ _Q = (1/otherval - 1/zL)/(1/zV - 1/zL); } else{ _Q = (otherval - zL)/(zV - zL); } if(!is_in_closed_range(0.0, 1.0, static_cast<double>(_Q))){ throw ValueError("vapor quality is not in (0,1)"); } else{ cached_saturation_iL = iL; cached_saturation_iV = iV; } } else{ // Find and cache the indices i, j selected_table = SELECTED_PH_TABLE; single_phase_logph.find_nearest_neighbor(iP, _p, otherkey, otherval, cached_single_phase_i, cached_single_phase_j); CellCoeffs &cell = coeffs_ph[cached_single_phase_i][cached_single_phase_j]; if (!cell.valid()){ if (cell.has_valid_neighbor()){ // Get new good neighbor cell.get_alternate(cached_single_phase_i, cached_single_phase_j); } else{ if (!cell.valid()){throw ValueError(format("Cell is invalid and has no good neighbors for p = %g Pa, T= %g K",val1,val2));} } } // Now find hmolar given P, X for X in Hmolar, Smolar, Umolar invert_single_phase_x(single_phase_logph, coeffs_ph, otherkey, otherval, _p, cached_single_phase_i, cached_single_phase_j); } break; } case DmassP_INPUTS:{ // Call again, but this time with molar units; D: [kg/m^3] / [kg/mol] -> [mol/m^3] update(DmassP_INPUTS, val1 / AS->molar_mass(), val2); return; } case PUmass_INPUTS:{ // Call again, but this time with molar units; U: [J/kg] * [kg/mol] -> [J/mol] update(PUmolar_INPUTS, val1, val2*AS->molar_mass()); return; } case PSmass_INPUTS:{ // Call again, but this time with molar units; S: [J/kg/K] * [kg/mol] -> [J/mol/K] update(PSmolar_INPUTS, val1, val2*AS->molar_mass()); return; } case PT_INPUTS:{ _p = val1; _T = val2; if (!single_phase_logpT.native_inputs_are_in_range(_T, _p)){ // Use the AbstractState instance using_single_phase_table = false; if (get_debug_level() > 5){ std::cout << "inputs are not in range"; } throw ValueError(format("inputs are not in range, p=%Lg, T=%Lg", _p, _T)); } else{ using_single_phase_table = true; // Use the table ! std::size_t iL = 0, iV = 0, iclosest = 0; CoolPropDbl TL = 0, TV = 0; SimpleState closest_state; if ( (is_mixture && PhaseEnvelopeRoutines::is_inside(phase_envelope, iP, _p, iT, _T, iclosest, closest_state)) || (!is_mixture && pure_saturation.is_inside(iP, _p, iT, _T, iL, iV, TL, TV)) ) { using_single_phase_table = false; throw ValueError(format("P,T with TTSE cannot be two-phase for now")); } else{ // Find and cache the indices i, j selected_table = SELECTED_PT_TABLE; single_phase_logpT.find_native_nearest_good_cell(_T, _p, cached_single_phase_i, cached_single_phase_j); CellCoeffs &cell = coeffs_pT[cached_single_phase_i][cached_single_phase_j]; if (!cell.valid()){ if (cell.has_valid_neighbor()){ // Get new good neighbor cell.get_alternate(cached_single_phase_i, cached_single_phase_j); } else{ if (!cell.valid()){throw ValueError(format("Cell is invalid and has no good neighbors for p = %g Pa, T= %g K",val1,val2));} } } // If p < pc, you might be getting a liquid solution when you want a vapor solution or vice versa // if you are very close to the saturation curve, so we figure out what the saturation temperature // is for the given pressure if (_p < this->AS->p_critical()) { double Ts = pure_saturation.evaluate(iT, _p, _Q, iL, iV); double TL = single_phase_logpT.T[cached_single_phase_i][cached_single_phase_j]; double TR = single_phase_logpT.T[cached_single_phase_i+1][cached_single_phase_j]; if (TL < Ts && Ts < TR){ if (_T < Ts){ if (cached_single_phase_i == 0){throw ValueError(format("P, T are near saturation, but cannot move the cell to the left")); } // It's liquid, move the cell to the left cached_single_phase_i--; }else{ if (cached_single_phase_i > single_phase_logpT.Nx-2){ throw ValueError(format("P,T are near saturation, but cannot move the cell to the right")); } // It's vapor, move to the right cached_single_phase_i++; } } } } } break; } case SmolarT_INPUTS: case DmolarT_INPUTS:{ CoolPropDbl otherval; parameters otherkey; switch(input_pair){ case SmolarT_INPUTS: _smolar = val1; _T = val2; otherval = val1; otherkey = iSmolar; break; case DmolarT_INPUTS: _rhomolar = val1; _T = val2; otherval = val1; otherkey = iDmolar; break; default: throw ValueError("Bad (impossible) pair"); } using_single_phase_table = true; // Use the table (or first guess is that it is single-phase)! std::size_t iL, iV; CoolPropDbl zL = 0, zV = 0; if (pure_saturation.is_inside(iT, _T, otherkey, otherval, iL, iV, zL, zV)){ using_single_phase_table = false; if (otherkey == iDmolar){ _Q = (1/otherval - 1/zL)/(1/zV - 1/zL); } else{ _Q = (otherval - zL)/(zV - zL); } if(!is_in_closed_range(0.0, 1.0, static_cast<double>(_Q))){ throw ValueError("vapor quality is not in (0,1)"); } else{ cached_saturation_iL = iL; cached_saturation_iV = iV; } _p = pure_saturation.evaluate(iP, _T, _Q, iL, iV); } else{ // Find and cache the indices i, j selected_table = SELECTED_PT_TABLE; single_phase_logpT.find_nearest_neighbor(iT, _T, otherkey, otherval, cached_single_phase_i, cached_single_phase_j); CellCoeffs &cell = coeffs_pT[cached_single_phase_i][cached_single_phase_j]; if (!cell.valid()){ if (cell.has_valid_neighbor()){ // Get new good neighbor cell.get_alternate(cached_single_phase_i, cached_single_phase_j); } else{ if (!cell.valid()){throw ValueError(format("Cell is invalid and has no good neighbors for p = %g Pa, T= %g K",val1,val2));} } } // Now find the y variable (Dmolar or Smolar in this case) invert_single_phase_y(single_phase_logpT, coeffs_pT, otherkey, otherval, _T, cached_single_phase_i, cached_single_phase_j); } break; } case PQ_INPUTS:{ std::size_t iL = 0, iV = 0; CoolPropDbl hL = 0, hV = 0; _p = val1; _Q = val2; if (_p < pure_saturation.pL[0]){throw ValueError(format("p (%g) Pa below minimum pressure", static_cast<double>(_p)));} pure_saturation.is_inside(iP, _p, iQ, _Q, iL, iV, hL, hV); using_single_phase_table = false; if(!is_in_closed_range(0.0,1.0,static_cast<double>(_Q))){ throw ValueError("vapor quality is not in (0,1)"); } else{ cached_saturation_iL = iL; cached_saturation_iV = iV; } break; } case QT_INPUTS:{ std::size_t iL = 0, iV = 0; CoolPropDbl dummyL = 0, dummyV = 0; _Q = val1; _T = val2; pure_saturation.is_inside(iT, _T, iQ, _Q, iL, iV, dummyL, dummyV); using_single_phase_table = false; if(!is_in_closed_range(0.0, 1.0, static_cast<double>(_Q))){ throw ValueError("vapor quality is not in (0,1)"); } else{ _p = pure_saturation.evaluate(iP, _T, _Q, iL, iV); cached_saturation_iL = iL; cached_saturation_iV = iV; } break; } default: throw ValueError("Sorry, but this set of inputs is not supported for Bicubic backend"); } }
void CoolProp::BicubicBackend::update(CoolProp::input_pairs input_pair, double val1, double val2) { if (get_debug_level() > 0){ std::cout << format("update(%s,%g,%g)\n", get_input_pair_short_desc(input_pair).c_str(), val1, val2); } // Clear cached values clear(); // To start, set quality to value that is for single-phase _Q = -1000; // Flush the cached indices (set to large number) cached_single_phase_i = std::numeric_limits<std::size_t>::max(); cached_single_phase_j = std::numeric_limits<std::size_t>::max(); cached_saturation_iL = std::numeric_limits<std::size_t>::max(); cached_saturation_iV = std::numeric_limits<std::size_t>::max(); PureFluidSaturationTableData &pure_saturation = dataset->pure_saturation; PhaseEnvelopeData & phase_envelope = dataset->phase_envelope; SinglePhaseGriddedTableData &single_phase_logph = dataset->single_phase_logph; SinglePhaseGriddedTableData &single_phase_logpT = dataset->single_phase_logpT; switch(input_pair){ case HmolarP_INPUTS:{ _hmolar = val1; _p = val2; if (!single_phase_logph.native_inputs_are_in_range(_hmolar, _p)){ // Use the AbstractState instance using_single_phase_table = false; if (get_debug_level() > 5){ std::cout << "inputs are not in range"; } throw ValueError(format("inputs are not in range, hmolar=%Lg, p=%Lg", static_cast<CoolPropDbl>(_hmolar), _p)); } else{ using_single_phase_table = true; // Use the table ! std::size_t iL, iV, iclosest = 0; CoolPropDbl hL = 0, hV = 0; SimpleState closest_state; bool is_two_phase = false; // Phase is imposed, use it if (imposed_phase_index != iphase_not_imposed){ is_two_phase = (imposed_phase_index == iphase_twophase); } else{ if (is_mixture){ is_two_phase = PhaseEnvelopeRoutines::is_inside(phase_envelope, iP, _p, iHmolar, _hmolar, iclosest, closest_state); } else{ is_two_phase = pure_saturation.is_inside(iP, _p, iHmolar, _hmolar, iL, iV, hL, hV); } } if ( is_two_phase ) { using_single_phase_table = false; _Q = (static_cast<double>(_hmolar)-hL)/(hV-hL); if(!is_in_closed_range(0.0,1.0,static_cast<double>(_Q))){ throw ValueError("vapor quality is not in (0,1)"); } else{ cached_saturation_iL = iL; cached_saturation_iV = iV; _phase = iphase_twophase; } } else{ // Find and cache the indices i, j selected_table = SELECTED_PH_TABLE; single_phase_logph.find_native_nearest_good_cell(_hmolar, _p, cached_single_phase_i, cached_single_phase_j); CellCoeffs &cell = dataset->coeffs_ph[cached_single_phase_i][cached_single_phase_j]; if (!cell.valid()){ if (cell.has_valid_neighbor()){ // Get new good neighbor cell.get_alternate(cached_single_phase_i, cached_single_phase_j); } else{ if (!cell.valid()){throw ValueError(format("Cell is invalid and has no good neighbors for hmolar = %g, p= %g",val1,val2));} } } // Recalculate the phase recalculate_singlephase_phase(); } } break; } case HmassP_INPUTS:{ update(HmolarP_INPUTS, val1 * AS->molar_mass(), val2); // H: [J/kg] * [kg/mol] -> [J/mol] return; } case PUmolar_INPUTS: case PSmolar_INPUTS: case DmolarP_INPUTS:{ CoolPropDbl otherval; parameters otherkey; switch(input_pair){ case PUmolar_INPUTS: _p = val1; _umolar = val2; otherval = val2; otherkey = iUmolar; break; case PSmolar_INPUTS: _p = val1; _smolar = val2; otherval = val2; otherkey = iSmolar; break; case DmolarP_INPUTS: _rhomolar = val1; _p = val2; otherval = val1; otherkey = iDmolar; break; default: throw ValueError("Bad (impossible) pair"); } using_single_phase_table = true; // Use the table (or first guess is that it is single-phase)! std::size_t iL, iV; CoolPropDbl zL = 0, zV = 0; std::size_t iclosest = 0; SimpleState closest_state; bool is_two_phase = false; // Phase is imposed, use it if (imposed_phase_index != iphase_not_imposed){ is_two_phase = (imposed_phase_index == iphase_twophase); } else{ if (is_mixture){ is_two_phase = PhaseEnvelopeRoutines::is_inside(phase_envelope, iP, _p, otherkey, otherval, iclosest, closest_state); } else{ is_two_phase = pure_saturation.is_inside(iP, _p, otherkey, otherval, iL, iV, zL, zV); } } if ( is_two_phase ){ using_single_phase_table = false; if (otherkey == iDmolar){ _Q = (1/otherval - 1/zL)/(1/zV - 1/zL); } else{ _Q = (otherval - zL)/(zV - zL); } if(!is_in_closed_range(0.0, 1.0, static_cast<double>(_Q))){ throw ValueError("vapor quality is not in (0,1)"); } else{ cached_saturation_iL = iL; cached_saturation_iV = iV; } _phase = iphase_twophase; } else{ // Find and cache the indices i, j selected_table = SELECTED_PH_TABLE; single_phase_logph.find_nearest_neighbor(iP, _p, otherkey, otherval, cached_single_phase_i, cached_single_phase_j); CellCoeffs &cell = dataset->coeffs_ph[cached_single_phase_i][cached_single_phase_j]; if (!cell.valid()){ if (cell.has_valid_neighbor()){ // Get new good neighbor cell.get_alternate(cached_single_phase_i, cached_single_phase_j); } else{ if (!cell.valid()){throw ValueError(format("Cell is invalid and has no good neighbors for p = %g Pa, T= %g K",val1,val2));} } } // Now find hmolar given P, X for X in Hmolar, Smolar, Umolar invert_single_phase_x(single_phase_logph, dataset->coeffs_ph, otherkey, otherval, _p, cached_single_phase_i, cached_single_phase_j); // Recalculate the phase recalculate_singlephase_phase(); } break; } case DmassP_INPUTS:{ // Call again, but this time with molar units; D: [kg/m^3] / [kg/mol] -> [mol/m^3] update(DmassP_INPUTS, val1 / AS->molar_mass(), val2); return; } case PUmass_INPUTS:{ // Call again, but this time with molar units; U: [J/kg] * [kg/mol] -> [J/mol] update(PUmolar_INPUTS, val1, val2*AS->molar_mass()); return; } case PSmass_INPUTS:{ // Call again, but this time with molar units; S: [J/kg/K] * [kg/mol] -> [J/mol/K] update(PSmolar_INPUTS, val1, val2*AS->molar_mass()); return; } case PT_INPUTS:{ _p = val1; _T = val2; if (!single_phase_logpT.native_inputs_are_in_range(_T, _p)){ // Use the AbstractState instance using_single_phase_table = false; if (get_debug_level() > 5){ std::cout << "inputs are not in range"; } throw ValueError(format("inputs are not in range, p=%g Pa, T=%g K", _p, _T)); } else{ using_single_phase_table = true; // Use the table ! std::size_t iL = 0, iV = 0, iclosest = 0; CoolPropDbl TL = 0, TV = 0; SimpleState closest_state; bool is_two_phase = false; // Phase is imposed, use it if (imposed_phase_index != iphase_not_imposed){ is_two_phase = (imposed_phase_index == iphase_twophase); } else{ if (is_mixture){ is_two_phase = PhaseEnvelopeRoutines::is_inside(phase_envelope, iP, _p, iT, _T, iclosest, closest_state); } else{ is_two_phase = pure_saturation.is_inside(iP, _p, iT, _T, iL, iV, TL, TV); } } if ( is_two_phase ) { using_single_phase_table = false; throw ValueError(format("P,T with TTSE cannot be two-phase for now")); } else{ // Find and cache the indices i, j selected_table = SELECTED_PT_TABLE; single_phase_logpT.find_native_nearest_good_cell(_T, _p, cached_single_phase_i, cached_single_phase_j); CellCoeffs &cell = dataset->coeffs_pT[cached_single_phase_i][cached_single_phase_j]; if (!cell.valid()){ if (cell.has_valid_neighbor()){ // Get new good neighbor cell.get_alternate(cached_single_phase_i, cached_single_phase_j); } else{ if (!cell.valid()){throw ValueError(format("Cell is invalid and has no good neighbors for p = %g Pa, T= %g K",val1,val2));} } } // If p < pc, you might be getting a liquid solution when you want a vapor solution or vice versa // if you are very close to the saturation curve, so we figure out what the saturation temperature // is for the given pressure if (_p < this->AS->p_critical()) { double Ts = pure_saturation.evaluate(iT, _p, _Q, iL, iV); double TL = single_phase_logpT.T[cached_single_phase_i][cached_single_phase_j]; double TR = single_phase_logpT.T[cached_single_phase_i+1][cached_single_phase_j]; if (TL < Ts && Ts < TR){ if (_T < Ts){ if (cached_single_phase_i == 0){throw ValueError(format("P, T are near saturation, but cannot move the cell to the left")); } // It's liquid, move the cell to the left cached_single_phase_i--; }else{ if (cached_single_phase_i > single_phase_logpT.Nx-2){ throw ValueError(format("P,T are near saturation, but cannot move the cell to the right")); } // It's vapor, move to the right cached_single_phase_i++; } } } // Recalculate the phase recalculate_singlephase_phase(); } } break; } case DmassT_INPUTS:{ // Call again, but this time with molar units; D: [kg/m^3] / [kg/mol] -> [mol/m^3] update(DmolarT_INPUTS, val1 / AS->molar_mass(), val2); return; } case SmassT_INPUTS:{ // Call again, but this time with molar units; S: [J/kg/K] * [kg/mol] -> [J/mol/K] update(SmolarT_INPUTS, val1*AS->molar_mass(), val2); return; } case SmolarT_INPUTS: case DmolarT_INPUTS:{ CoolPropDbl otherval; parameters otherkey; switch(input_pair){ case SmolarT_INPUTS: _smolar = val1; _T = val2; otherval = val1; otherkey = iSmolar; break; case DmolarT_INPUTS: _rhomolar = val1; _T = val2; otherval = val1; otherkey = iDmolar; break; default: throw ValueError("Bad (impossible) pair"); } using_single_phase_table = true; // Use the table (or first guess is that it is single-phase)! std::size_t iL, iV; CoolPropDbl zL = 0, zV = 0; std::size_t iclosest = 0; SimpleState closest_state; bool is_two_phase = false; // Phase is imposed, use it if (imposed_phase_index != iphase_not_imposed){ is_two_phase = (imposed_phase_index == iphase_twophase); } else{ if (is_mixture){ is_two_phase = PhaseEnvelopeRoutines::is_inside(phase_envelope, iT, _T, otherkey, otherval, iclosest, closest_state); } else{ is_two_phase = pure_saturation.is_inside(iT, _T, otherkey, otherval, iL, iV, zL, zV); } } if ( is_two_phase ) { using_single_phase_table = false; if (otherkey == iDmolar){ _Q = (1/otherval - 1/zL)/(1/zV - 1/zL); } else{ _Q = (otherval - zL)/(zV - zL); } if(!is_in_closed_range(0.0, 1.0, static_cast<double>(_Q))){ throw ValueError("vapor quality is not in (0,1)"); } else{ cached_saturation_iL = iL; cached_saturation_iV = iV; } _p = pure_saturation.evaluate(iP, _T, _Q, iL, iV); } else{ // Find and cache the indices i, j selected_table = SELECTED_PT_TABLE; single_phase_logpT.find_nearest_neighbor(iT, _T, otherkey, otherval, cached_single_phase_i, cached_single_phase_j); CellCoeffs &cell = dataset->coeffs_pT[cached_single_phase_i][cached_single_phase_j]; if (!cell.valid()){ if (cell.has_valid_neighbor()){ // Get new good neighbor cell.get_alternate(cached_single_phase_i, cached_single_phase_j); } else{ if (!cell.valid()){throw ValueError(format("Cell is invalid and has no good neighbors for p = %g Pa, T= %g K",val1,val2));} } } // Now find the y variable (Dmolar or Smolar in this case) invert_single_phase_y(single_phase_logpT, dataset->coeffs_pT, otherkey, otherval, _T, cached_single_phase_i, cached_single_phase_j); // Recalculate the phase recalculate_singlephase_phase(); } break; } case PQ_INPUTS:{ std::size_t iL = 0, iV = 0; _p = val1; _Q = val2; using_single_phase_table = false; if (!is_in_closed_range(0.0, 1.0, static_cast<double>(_Q))){ throw ValueError("vapor quality is not in (0,1)"); } else{ CoolPropDbl TL = _HUGE, TV = _HUGE; if (is_mixture){ std::vector<std::pair<std::size_t, std::size_t> > intersect = PhaseEnvelopeRoutines::find_intersections(phase_envelope, iP, _p); if (intersect.empty()){ throw ValueError(format("p [%g Pa] is not within phase envelope", _p)); } iV = intersect[0].first; iL = intersect[1].first; } else{ bool it_is_inside = pure_saturation.is_inside(iP, _p, iQ, _Q, iL, iV, TL, TV); if (!it_is_inside){ throw ValueError("Not possible to determine whether pressure is inside or not"); } } _T = _Q*TV + (1-_Q)*TL; cached_saturation_iL = iL; cached_saturation_iV = iV; _phase = iphase_twophase; } break; } case QT_INPUTS:{ std::size_t iL = 0, iV = 0; _Q = val1; _T = val2; using_single_phase_table = false; if(!is_in_closed_range(0.0, 1.0, static_cast<double>(_Q))){ throw ValueError("vapor quality is not in (0,1)"); } else{ CoolPropDbl pL, pV; if (is_mixture){ std::vector<std::pair<std::size_t,std::size_t> > intersect = PhaseEnvelopeRoutines::find_intersections(phase_envelope, iT, _T); if (intersect.empty()){ throw ValueError(format("T [%g K] is not within phase envelope", _T)); } iV = intersect[0].first; iL = intersect[1].first; pL = PhaseEnvelopeRoutines::evaluate(phase_envelope, iP, iT, _T, iL); pV = PhaseEnvelopeRoutines::evaluate(phase_envelope, iP, iT, _T, iV); _p = _Q*pV + (1-_Q)*pL; } else{ pure_saturation.is_inside(iT, _T, iQ, _Q, iL, iV, pL, pV); } _p = _Q*pV + (1-_Q)*pL; cached_saturation_iL = iL; cached_saturation_iV = iV; _phase = iphase_twophase; } break; } default: throw ValueError("Sorry, but this set of inputs is not supported for Bicubic backend"); } }
void CoolProp::TTSEBackend::update(CoolProp::input_pairs input_pair, double val1, double val2) { // Clear cached variables clear(); // Check the tables, build if neccessary check_tables(); // Flush the cached indices (set to large number) cached_single_phase_i = std::numeric_limits<std::size_t>::max(); cached_single_phase_j = std::numeric_limits<std::size_t>::max(); cached_saturation_iL = std::numeric_limits<std::size_t>::max(); cached_saturation_iV = std::numeric_limits<std::size_t>::max(); // To start, set quality to value that is impossible _Q = -1000; switch(input_pair){ case HmolarP_INPUTS:{ _hmolar = val1; _p = val2; if (!single_phase_logph.native_inputs_are_in_range(_hmolar, _p)){ // Use the AbstractState instance using_single_phase_table = false; if (get_debug_level() > 5){ std::cout << "inputs are not in range"; } throw ValueError(format("inputs are not in range, hmolar=%Lg, p=%Lg", static_cast<CoolPropDbl>(_hmolar), _p)); } else{ using_single_phase_table = true; // Use the table ! std::size_t iL, iV; CoolPropDbl hL = 0, hV = 0; if (pure_saturation.is_inside(iP, _p, iHmolar, _hmolar, iL, iV, hL, hV)){ using_single_phase_table = false; _Q = (static_cast<double>(_hmolar)-hL)/(hV-hL); if(!is_in_closed_range(0.0,1.0,static_cast<double>(_Q))){ throw ValueError("vapor quality is not in (0,1)"); } else{ cached_saturation_iL = iL; cached_saturation_iV = iV; } } else{ // Find and cache the indices i, j selected_table = SELECTED_PH_TABLE; single_phase_logph.find_native_nearest_good_neighbor(_hmolar, _p, cached_single_phase_i, cached_single_phase_j); } } break; } case HmassP_INPUTS:{ update(HmolarP_INPUTS, val1 * AS->molar_mass(), val2); // H: [J/kg] * [kg/mol] -> [J/mol] return; } case PUmolar_INPUTS: case PSmolar_INPUTS: case DmolarP_INPUTS:{ CoolPropDbl otherval; parameters otherkey; switch(input_pair){ case PUmolar_INPUTS: _p = val1; _umolar = val2; otherval = val2; otherkey = iUmolar; break; case PSmolar_INPUTS: _p = val1; _smolar = val2; otherval = val2; otherkey = iSmolar; break; case DmolarP_INPUTS: _rhomolar = val1; _p = val2; otherval = val1; otherkey = iDmolar; break; default: throw ValueError("Bad (impossible) pair"); } using_single_phase_table = true; // Use the table (or first guess is that it is single-phase)! std::size_t iL, iV; CoolPropDbl zL = 0, zV = 0; if (pure_saturation.is_inside(iP, _p, otherkey, otherval, iL, iV, zL, zV)){ using_single_phase_table = false; if (otherkey == iDmolar){ _Q = (1/otherval - 1/zL)/(1/zV - 1/zL); } else{ _Q = (otherval - zL)/(zV - zL); } if(!is_in_closed_range(0.0, 1.0, static_cast<double>(_Q))){ throw ValueError("vapor quality is not in (0,1)"); } else{ cached_saturation_iL = iL; cached_saturation_iV = iV; } } else{ // Find and cache the indices i, j selected_table = SELECTED_PH_TABLE; single_phase_logph.find_nearest_neighbor(iP, _p, otherkey, otherval, cached_single_phase_i, cached_single_phase_j); // Now find hmolar invert_single_phase_x(single_phase_logph, otherkey, otherval, _p, cached_single_phase_i, cached_single_phase_j); } break; } case DmassP_INPUTS:{ // Call again, but this time with molar units; D: [kg/m^3] / [kg/mol] -> [mol/m^3] update(DmassP_INPUTS, val1 / AS->molar_mass(), val2); return; } case PUmass_INPUTS:{ // Call again, but this time with molar units; U: [J/kg] * [kg/mol] -> [J/mol] update(PUmolar_INPUTS, val1, val2*AS->molar_mass()); return; } case PSmass_INPUTS:{ // Call again, but this time with molar units; S: [J/kg/K] * [kg/mol] -> [J/mol/K] update(PSmolar_INPUTS, val1, val2*AS->molar_mass()); return; } case PT_INPUTS:{ _p = val1; _T = val2; if (!single_phase_logpT.native_inputs_are_in_range(_T, _p)){ // Use the AbstractState instance using_single_phase_table = false; if (get_debug_level() > 5){ std::cout << "inputs are not in range"; } throw ValueError(format("inputs are not in range, p=%Lg, T=%Lg", _p, _T)); } else{ using_single_phase_table = true; // Use the table ! std::size_t iL = 0, iV = 0; CoolPropDbl TL = 0, TV = 0; if (pure_saturation.is_inside(iP, _p, iT, _T, iL, iV, TL, TV)){ using_single_phase_table = false; throw ValueError(format("P,T with TTSE cannot be two-phase for now")); } else{ // Find and cache the indices i, j selected_table = SELECTED_PT_TABLE; single_phase_logpT.find_native_nearest_neighbor(_T, _p, cached_single_phase_i, cached_single_phase_j); } } break; } case SmolarT_INPUTS: case DmolarT_INPUTS:{ CoolPropDbl otherval; parameters otherkey; switch(input_pair){ case SmolarT_INPUTS: _smolar = val1; _T = val2; otherval = val1; otherkey = iSmolar; break; case DmolarT_INPUTS: _rhomolar = val1; _T = val2; otherval = val1; otherkey = iDmolar; break; default: throw ValueError("Bad (impossible) pair"); } using_single_phase_table = true; // Use the table (or first guess is that it is single-phase)! std::size_t iL, iV; CoolPropDbl zL = 0, zV = 0; if (pure_saturation.is_inside(iT, _T, otherkey, otherval, iL, iV, zL, zV)){ using_single_phase_table = false; if (otherkey == iDmolar){ _Q = (1/otherval - 1/zL)/(1/zV - 1/zL); } else{ _Q = (otherval - zL)/(zV - zL); } if(!is_in_closed_range(0.0, 1.0, static_cast<double>(_Q))){ throw ValueError("vapor quality is not in (0,1)"); } else{ cached_saturation_iL = iL; cached_saturation_iV = iV; } } else{ // Find and cache the indices i, j selected_table = SELECTED_PT_TABLE; single_phase_logpT.find_nearest_neighbor(iT, _T, otherkey, otherval, cached_single_phase_i, cached_single_phase_j); // Now find hmolar invert_single_phase_y(single_phase_logpT, otherkey, otherval, _T, cached_single_phase_i, cached_single_phase_j); } break; } case PQ_INPUTS:{ std::size_t iL = 0, iV = 0; CoolPropDbl dummyL = 0, dummyV = 0; _p = val1; _Q = val2; pure_saturation.is_inside(iP, _p, iQ, _Q, iL, iV, dummyL, dummyV); using_single_phase_table = false; if(!is_in_closed_range(0.0, 1.0, static_cast<double>(_Q))){ throw ValueError("vapor quality is not in (0,1)"); } else{ cached_saturation_iL = iL; cached_saturation_iV = iV; } break; } case QT_INPUTS:{ std::size_t iL = 0, iV = 0; CoolPropDbl dummyL = 0, dummyV = 0; _Q = val1; _T = val2; pure_saturation.is_inside(iT, _T, iQ, _Q, iL, iV, dummyL, dummyV); using_single_phase_table = false; if(!is_in_closed_range(0.0, 1.0, static_cast<double>(_Q))){ throw ValueError("vapor quality is not in (0,1)"); } else{ _p = pure_saturation.evaluate(iP, _T, _Q, iL, iV); cached_saturation_iL = iL; cached_saturation_iV = iV; } break; } default: throw ValueError("Sorry, but this set of inputs is not supported for TTSE backend"); } }