diff --git a/src/Backends/Tabular/BicubicBackend.cpp b/src/Backends/Tabular/BicubicBackend.cpp index 208c370a..f6e2dca1 100644 --- a/src/Backends/Tabular/BicubicBackend.cpp +++ b/src/Backends/Tabular/BicubicBackend.cpp @@ -8,87 +8,34 @@ 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(); + + // Clear cached variables + clear(); // Convert to mass-based units if necessary CoolPropDbl ld_value1 = val1, ld_value2 = val2; mass_to_molar_inputs(input_pair, ld_value1, ld_value2); val1 = ld_value1; val2 = ld_value2; - // To start, set quality to value that is for single-phase + // Check the tables, build if neccessary + check_tables(); + + // Flush the cached indices (set to large number) + cached_single_phase_i = std::numeric_limits::max(); + cached_single_phase_j = std::numeric_limits::max(); + cached_saturation_iL = std::numeric_limits::max(); + cached_saturation_iV = std::numeric_limits::max(); + + // To start, set quality to value that is impossible _Q = -1000; - // Flush the cached indices (set to large number) - cached_single_phase_i = std::numeric_limits::max(); - cached_single_phase_j = std::numeric_limits::max(); - cached_saturation_iL = std::numeric_limits::max(); - cached_saturation_iV = std::numeric_limits::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(_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(_hmolar)-hL)/(hV-hL); - if(!is_in_closed_range(0.0,1.0,static_cast(_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 PUmolar_INPUTS: case PSmolar_INPUTS: case DmolarP_INPUTS:{ @@ -302,6 +249,7 @@ void CoolProp::BicubicBackend::update(CoolProp::input_pairs input_pair, double v } break; } + case HmolarP_INPUTS: case PQ_INPUTS: case QT_INPUTS: TabularBackend::update(input_pair, val1, val2); break; @@ -310,6 +258,21 @@ void CoolProp::BicubicBackend::update(CoolProp::input_pairs input_pair, double v } } +void CoolProp::BicubicBackend::find_native_nearest_good_indices(SinglePhaseGriddedTableData &table, const std::vector > &coeffs, double x, double y, std::size_t &i, std::size_t &j) +{ + table.find_native_nearest_good_cell(x, y, i, j); + const CellCoeffs &cell = coeffs[i][j]; + if (!cell.valid()){ + if (cell.has_valid_neighbor()){ + // Get new good neighbor + cell.get_alternate(i, j); + } + else{ + if (!cell.valid()){ throw ValueError(format("Cell is invalid and has no good neighbors for x = %g, y= %g", x, y)); } + } + } +} + /** Use the single_phase table to evaluate an output for a transport property * * Here we use linear interpolation because we don't have any information about the derivatives with respect to the diff --git a/src/Backends/Tabular/BicubicBackend.h b/src/Backends/Tabular/BicubicBackend.h index 4d746617..024ed3f9 100644 --- a/src/Backends/Tabular/BicubicBackend.h +++ b/src/Backends/Tabular/BicubicBackend.h @@ -134,6 +134,8 @@ class BicubicBackend : public TabularBackend double evaluate_single_phase_pT(parameters output, std::size_t i, std::size_t j){ return evaluate_single_phase(dataset->single_phase_logpT, dataset->coeffs_pT, output, _T, _p, i, j); }; + + virtual void find_native_nearest_good_indices(SinglePhaseGriddedTableData &table, const std::vector > &coeffs, double x, double y, std::size_t &i, std::size_t &j); /** * @brief Evaluate the single-phase transport properties using linear interpolation. Works well except for near the critical point diff --git a/src/Backends/Tabular/TTSEBackend.cpp b/src/Backends/Tabular/TTSEBackend.cpp index 9cc8cfc1..be832a1a 100644 --- a/src/Backends/Tabular/TTSEBackend.cpp +++ b/src/Backends/Tabular/TTSEBackend.cpp @@ -6,6 +6,8 @@ void CoolProp::TTSEBackend::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 variables clear(); @@ -16,69 +18,22 @@ void CoolProp::TTSEBackend::update(CoolProp::input_pairs input_pair, double val1 // Check the tables, build if neccessary check_tables(); - + // Flush the cached indices (set to large number) - cached_single_phase_i = std::numeric_limits::max(); + cached_single_phase_i = std::numeric_limits::max(); cached_single_phase_j = std::numeric_limits::max(); - cached_saturation_iL = std::numeric_limits::max(); + cached_saturation_iL = std::numeric_limits::max(); cached_saturation_iV = std::numeric_limits::max(); // To start, set quality to value that is impossible _Q = -1000; - PhaseEnvelopeData & phase_envelope = dataset->phase_envelope; 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(_hmolar), _p)); - } - else{ - using_single_phase_table = true; // Use the table ! - std::size_t iL, iV; - CoolPropDbl hL = 0, hV = 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, 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(_hmolar)-hL)/(hV-hL); - if(!is_in_closed_range(0.0,1.0,static_cast(_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_neighbor(_hmolar, _p, cached_single_phase_i, cached_single_phase_j); - // Recalculate the phase - recalculate_singlephase_phase(); - } - } - break; - } case PUmolar_INPUTS: case PSmolar_INPUTS: case DmolarP_INPUTS:{ @@ -224,6 +179,7 @@ void CoolProp::TTSEBackend::update(CoolProp::input_pairs input_pair, double val1 } break; } + case HmolarP_INPUTS: case PQ_INPUTS: case QT_INPUTS: TabularBackend::update(input_pair, val1, val2); break; diff --git a/src/Backends/Tabular/TTSEBackend.h b/src/Backends/Tabular/TTSEBackend.h index 4f59945c..ad895a17 100644 --- a/src/Backends/Tabular/TTSEBackend.h +++ b/src/Backends/Tabular/TTSEBackend.h @@ -45,6 +45,11 @@ class TTSEBackend : public TabularBackend double invert_single_phase_x(SinglePhaseGriddedTableData &table, parameters output, double x, double y, std::size_t i, std::size_t j); double invert_single_phase_y(SinglePhaseGriddedTableData &table, parameters output, double y, double x, std::size_t i, std::size_t j); + /// Find the best set of i,j for native inputs. + virtual void find_native_nearest_good_indices(SinglePhaseGriddedTableData &table, const std::vector > &coeffs, double x, double y, std::size_t &i, std::size_t &j){ + return table.find_native_nearest_good_neighbor(x, y, i, j); + }; + /** * @brief Evaluate a derivative in terms of the native inputs of the table * @param table A reference to the table to be used diff --git a/src/Backends/Tabular/TabularBackends.cpp b/src/Backends/Tabular/TabularBackends.cpp index 5bffbac5..2dfebae7 100644 --- a/src/Backends/Tabular/TabularBackends.cpp +++ b/src/Backends/Tabular/TabularBackends.cpp @@ -658,6 +658,53 @@ void CoolProp::TabularBackend::update(CoolProp::input_pairs input_pair, double v 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(_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(_hmolar)-hL)/(hV-hL); + if (!is_in_closed_range(0.0, 1.0, static_cast(_Q))){ + throw ValueError("vapor quality is not in (0,1)"); + } + else{ + cached_saturation_iL = iL; cached_saturation_iV = iV; + _phase = iphase_twophase; + } + } + else{ + selected_table = SELECTED_PH_TABLE; + // Find and cache the indices i, j + find_native_nearest_good_indices(single_phase_logph, dataset->coeffs_ph, _hmolar, _p, 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; diff --git a/src/Backends/Tabular/TabularBackends.h b/src/Backends/Tabular/TabularBackends.h index 48de92a4..cc046ea1 100644 --- a/src/Backends/Tabular/TabularBackends.h +++ b/src/Backends/Tabular/TabularBackends.h @@ -818,6 +818,16 @@ class TabularBackend : public AbstractState */ void calc_unspecify_phase(){ imposed_phase_index = iphase_not_imposed; }; + virtual double evaluate_single_phase_phmolar(parameters output, std::size_t i, std::size_t j) = 0; + virtual double evaluate_single_phase_pT(parameters output, std::size_t i, std::size_t j) = 0; + virtual double evaluate_single_phase_phmolar_transport(parameters output, std::size_t i, std::size_t j) = 0; + virtual double evaluate_single_phase_pT_transport(parameters output, std::size_t i, std::size_t j) = 0; + virtual double evaluate_single_phase_phmolar_derivative(parameters output, std::size_t i, std::size_t j, std::size_t Nx, std::size_t Ny) = 0; + virtual double evaluate_single_phase_pT_derivative(parameters output, std::size_t i, std::size_t j, std::size_t Nx, std::size_t Ny) = 0; + + /// Ask the derived class to find the nearest good set of i,j that it wants to use (pure virtual) + virtual void find_native_nearest_good_indices(SinglePhaseGriddedTableData &table, const std::vector > &coeffs, double x, double y, std::size_t &i, std::size_t &j) = 0; + phases calc_phase(void){ return _phase; } CoolPropDbl calc_T_critical(void){return this->AS->T_critical();}; CoolPropDbl calc_Ttriple(void){return this->AS->Ttriple();}; @@ -837,12 +847,7 @@ class TabularBackend : public AbstractState const std::vector calc_mass_fractions(void){ return AS->get_mass_fractions(); }; CoolPropDbl calc_molar_mass(void){return AS->molar_mass();}; - virtual double evaluate_single_phase_phmolar(parameters output, std::size_t i, std::size_t j) = 0; - virtual double evaluate_single_phase_pT(parameters output, std::size_t i, std::size_t j) = 0; - virtual double evaluate_single_phase_phmolar_transport(parameters output, std::size_t i, std::size_t j) = 0; - virtual double evaluate_single_phase_pT_transport(parameters output, std::size_t i, std::size_t j) = 0; - virtual double evaluate_single_phase_phmolar_derivative(parameters output, std::size_t i, std::size_t j, std::size_t Nx, std::size_t Ny) = 0; - virtual double evaluate_single_phase_pT_derivative(parameters output, std::size_t i, std::size_t j, std::size_t Nx, std::size_t Ny) = 0; + CoolPropDbl calc_saturated_liquid_keyed_output(parameters key); CoolPropDbl calc_saturated_vapor_keyed_output(parameters key);