diff --git a/CMakeLists.txt b/CMakeLists.txt index 1603b7947..a2426cc4c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,8 @@ set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") include(BuildFlags) +target_link_libraries(cse_common_interface INTERFACE fmt) + set_property(GLOBAL PROPERTY USE_FOLDERS ON) find_package(Git QUIET) @@ -80,6 +82,7 @@ set(cse_common_source "${CSE_SOURCE_DIR}/src/strpak.cpp" "${CSE_SOURCE_DIR}/src/tdpak.cpp" "${CSE_SOURCE_DIR}/src/xiopak.cpp" + "${CSE_SOURCE_DIR}/src/nummeth.cpp" ) add_subdirectory(src) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dcdfa4e86..6f512dfd7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,8 +54,14 @@ add_custom_command( WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" ) -# Needed to establish dependency for other targets that depend on dtypes.h -add_custom_target(generate_dtypes DEPENDS ${CSE_BINARY_DIR}/src/dtypes.h) +# Needed to establish dependency for other targets that depend on generated RCDEF outputs +add_custom_target(generate_rcdef_outputs DEPENDS + "${CSE_BINARY_DIR}/src/dtypes.h" + "${CSE_BINARY_DIR}/src/rccn.h" + "${CSE_BINARY_DIR}/src/dttab.cpp" + "${CSE_BINARY_DIR}/src/srfd.cpp" + "${CSE_BINARY_DIR}/src/untab.cpp" +) # Set CSE Version add_custom_target(version_header diff --git a/src/cnloads.cpp b/src/cnloads.cpp index fa2ec3995..4dd7b1492 100644 --- a/src/cnloads.cpp +++ b/src/cnloads.cpp @@ -50,6 +50,8 @@ static RC loadsIzxSh2(); static RC loadsSurfaces( BOO subhrly); static RC loadsXFans(); +/*------------------------------- CONSTANTS -------------------------------*/ +static constexpr double tol_tF = 1.e-12; // temperature tolerance (degF) /*------------------------ The MAIN EQUATION story ------------------------*/ // rob 12-89 prelim @@ -1647,7 +1649,7 @@ double ZNR::zn_AmfHvacCR( // sensible hvac air requirements w/ add'l radiant hea // does NOT change ZNR state // returns dry-air mass flow rate required to hold tza, lbm/hr { - double amf = (fabs( tza - tSup) < .00001) + double amf = (fabs( tza - tSup) < tol_tF) ? DBL_MAX : (zn_balC1 - zn_balC2*tza + zn_cxSh*qRad) / (zn_dRpCx * (tza - tSup) * Top.tp_airSH); @@ -1661,7 +1663,7 @@ double ZNR::zn_AmfHvacCR( // sensible hvac air requirements // does NOT change ZNR state // returns dry-air mass flow rate required to hold tza, lbm/hr { - double amf = (fabs(tza - tSup) < .00001) + double amf = (fabs(tza - tSup) < tol_tF) ? DBL_MAX : (zn_balC1 - zn_balC2 * tza) / (zn_dRpCx * (tza - tSup) * Top.tp_airSH); @@ -5905,10 +5907,11 @@ RC RSYS::rs_AllocateZoneAir() // finalize zone air flows }, this, amfXTarg, .0001*amfXTarg, tSup, amfX, // x1, f1 - rs_asSupAux.as_tdb, 1. / rs_amfReq[1]); // x2, f2 - if (ret != 0) - { - warn("RSYS '%s': ASHP aux heat supply temp fail (%d)", Name(), ret); + rs_asSupAux.as_tdb, DBL_MIN); // x2, f2 + if (ret != 0) { + tSup = rs_asSup.as_tdb; + oWarn("Failed to solve for ASHP aux heat supply temperature to deliver %g lb/hr.\n" + " Resuming with previous value of tSup=%.2f.", 1. / amfXTarg, tSup); } #if defined( _DEBUG) // check tSup -- should be between noAux and fullAux temps diff --git a/src/cvpak.cpp b/src/cvpak.cpp index 30d3b2856..4fab2bf7f 100644 --- a/src/cvpak.cpp +++ b/src/cvpak.cpp @@ -38,7 +38,7 @@ p static SI ipv; // ditto right-shifted for use as subscript p /* note positive value options not used (except check debugpr.c); p could remove LOTS of code in cvpak, rob grep 10-88. Done 11-91. */ #endif -static USI fmt; // cvin2s "format" argument; see cvpak.h for field definitions. +static USI fmtv; // cvin2s "format" argument; see cvpak.h for field definitions. static USI mfw; // cvin2s "max field width" argument. static SI just; // justification: left, rt, rz, squeeze. static SI ijust; // ditto right-shifted for use as subscript. @@ -170,7 +170,7 @@ char * FC cvin2s( // Convert internal format data to external format string in bool percent = false; // set true if converting a DTPERCENT; shares DTFLOAT code #endif - fmt = _fmt; // store format arg for use by callees + fmtv = _fmt; // store format arg for use by callees mfw = _mfw; // .. max field width arg /* NULL data pointer means do NOTHING */ /* for caller convenience in supporting optional stuff, tentative 9-89. @@ -188,7 +188,7 @@ char * FC cvin2s( // Convert internal format data to external format string in // Allocate temporary string space. int allocLen = mfw+3+2; // +3: some paranoia space, at least 1 needed. // +2: for FMTUNITS space or FMTPU ()'s - if (fmt & (FMTUNITS|FMTPU)) // if units to be appended + if (fmtv & (FMTUNITS|FMTPU)) // if units to be appended allocLen += static_cast(strlen( UNIT::GetSymbol( units)) ); if (allocLen < 13) allocLen = 13; // always enuf for "\0" or "\0" 2-27-92 str = strtemp( allocLen); // strpak.c; strtempPop deallocates. @@ -196,11 +196,11 @@ char * FC cvin2s( // Convert internal format data to external format string in /* Common setup */ #ifdef FMTPVMASK // define in cvpak.h to restore p positive value display options, 11-91 -p pv = fmt&FMTPVMASK; // positive value display: null, +, spc +p pv = fmtv&FMTPVMASK; // positive value display: null, +, spc p ipv = ((USI)pv) >> FMTPVSHIFT; // .. shifted for use as subscript -- frequently used to select formats p // (shift made unsigned for lint) #endif - just = fmt&FMTJMASK; // justification: left, rt, rz, squeeze + just = fmtv&FMTJMASK; // justification: left, rt, rz, squeeze ijust = ((USI)just) >> FMTJSHIFT; // .. shifted for use as subscript // (shift made unsigned for lint) lj = just==FMTLJ; // left justify flag @@ -328,7 +328,12 @@ p break; } val = *(float*)data; // conver float value to print to double } -valValue: // double, [percent] join here + valValue: // double, [percent] join here + if (std::isnan(val)) { + data = "nan"; + goto strjust; + } + val = cvIntoEx( val, units); // convert value to ext units #ifdef FMTPVMASK p wsign = !(pv==FMTPVNULL && val >= 0.); // sign width @@ -370,7 +375,7 @@ x printf("\n-0"); { if (!ISNCHOICE( *(void **)data)) // if not a choice (cnglob.h macro) goto floatCase; // (numbers, UNSET, NANDLES branch) - fmt &= ~(FMTUNITS|FMTPU); // suppress units when number-choice is choice + fmtv &= ~(FMTUNITS|FMTPU); // suppress units when number-choice is choice } choiceCase: // float types comes here if NCHOICE found (unexpected) if (dt & (DTBCHOICB|DTBCHOICN)) // if a choice type @@ -443,12 +448,12 @@ x } /* optionally add units. Note ' " for ft-in done in cvFtIn2s. Suppressed for NCHOICE choice by clearing bits above. */ - if (fmt & FMTUNITS) + if (fmtv & FMTUNITS) { strcat( str, " "); strcat( str, UNIT::GetSymbol( units)); // concat units text } - else if (fmt & FMTPU) // parenthesised units for res loads 2-90 + else if (fmtv & FMTPU) // parenthesised units for res loads 2-90 { strcat( str, "("); strcat( str, UNIT::GetSymbol( units)); // concat units text @@ -471,12 +476,12 @@ static const char ddalpha[]=" kMGTPEZY?????"; // Chars for K format output fiel LOCAL void FC cvDouble2s() // float / double output conversion case for cvin2s -// converts 'val' to '*str'; uses other global statics including: fmt, mfw, val, str, lj, lz, ppos, . +// converts 'val' to '*str'; uses other global statics including: fmtv, mfw, val, str, lj, lz, ppos, . // do not call for foot-inch conversion: see cvFtIn2s. { SI i; - SI dfw = fmt & FMTDFWMASK; // decimal field width (# decimal places) + SI dfw = fmtv & FMTDFWMASK; // decimal field width (# decimal places) ki = 0; // say not in K format overflow (cvsd/nexK) // zero exception @@ -495,7 +500,7 @@ p Cvnchars = sprintf( str, sif[lj][ipv], wid, ppos, 0); // trim trailing zeroes (specified significant digits) option - if (fmt & FMTRTZ) // "trim trailing zeroes" option + if (fmtv & FMTRTZ) // "trim trailing zeroes" option { if (cvsd( mfw, dfw)) // convert (returns false if should use cvdd: number too small relative to space) return; // if converted (returns best fit if overwide; caller checks Cvnchars) @@ -509,7 +514,7 @@ p Cvnchars = sprintf( str, sif[lj][ipv], wid, ppos, 0); // field overflowed (rest of function) - if ((fmt & FMTOVFMASK)==FMTOVFK) // if overlow is to be handled with k format + if ((fmtv & FMTOVFMASK)==FMTOVFK) // if overlow is to be handled with k format { // float overflow, K format option (default). Could recode with new fcn nexK()... 4-92 @@ -581,7 +586,7 @@ p Cvnchars = sprintf( str, sif[lj][ipv], wid, ppos, 0); //====================================================================== LOCAL void FC cvFtIn2s() // feet-inch length output conversion case for cvin2s -// converts 'val' to '*str'; uses other global statics including: fmt, mfw, val, str, lj, lz, ppos, . +// converts 'val' to '*str'; uses other global statics including: fmtv, mfw, val, str, lj, lz, ppos, . { bool biglen = (aval > 178000000.); // true to show feet only: set if > max 32-bit int inches, also set below if too wide. if (!biglen) // if feet not too big to express inches in int (in sepFtInch): ie normally @@ -590,7 +595,7 @@ LOCAL void FC cvFtIn2s() // feet-inch length output conversion case for cv bool sq = (just==FMTSQ); // squeeze (minimum columns) flag (also 'wid' is 1) int inch; // inches #ifdef FMTNOQUINCH // define in cvpak.h to restore feature, 11-91 -x SI quinch = !(fmt & FMTNOQUINCH); // 1 for " after inches +x SI quinch = !(fmtv & FMTNOQUINCH); // 1 for " after inches x ft = sepFtInch( val, &inch); // separate/fix feet, inches x justInches = (quinch && ft == 0L && inch != 0); x // true to omit feet; never happens if quinch is off -- prevents ambiguous single numbers. @@ -601,8 +606,8 @@ x // true to omit feet; never happens if quinch is off -- prevents ambiguous // digits after decimal point in INCHES - int indfw = fmt & FMTDFWMASK; // init decimal places for inches (same value as former 'dfw') - if (fmt & FMTRTZ) // with truncating trailing 0's optn, + int indfw = fmtv & FMTDFWMASK; // init decimal places for inches (same value as former 'dfw') + if (fmtv & FMTRTZ) // with truncating trailing 0's optn, { // dfw is total sig digs, not digits after . // Reduce indfw to digits to print AFTER DECIMAL in INCHES. @@ -674,7 +679,7 @@ p dinch ); // floating inches dinch ); // floating inches #endif } - if (fmt & FMTRTZ) // trim trailing zeros option + if (fmtv & FMTRTZ) // trim trailing zeros option { // rob 10-88 to support FTMRTZ with FMTSQ Cvnchars = @@ -716,7 +721,7 @@ p inch ); x x // omit zero inches with feet if ALL 3 of these options on (rob 10-88 for text files): x -x if (fmt & (FMTSQ|FMTRTZ|FMTNOQUINCH)==(FMTSQ|FMTRTZ|FMTNOQUINCH) ) +x if (fmtv & (FMTSQ|FMTRTZ|FMTNOQUINCH)==(FMTSQ|FMTRTZ|FMTNOQUINCH) ) x { x if ( !justInches // if feet shown x && *(str + Cvnchars-1)=='0' // 0 last @@ -761,7 +766,7 @@ x } DTDBL, UNNONE, // no units mfw-1, // save a column for ' - fmt ); + fmtv ); *(str+Cvnchars++) = '\''; // add foot mark ' to end *(str+Cvnchars) = '\0'; } @@ -809,7 +814,7 @@ LOCAL bool FC cvsd( // Significant-digits (trim trailing 0's, g format) outpu // and uses local statics including: // char *str, String into which to store converted value // double val Value to convert -// fmt cvin2s caller's format argument +// fmtv cvin2s caller's format argument // wid field width given in cvin2s call, or 1 for FMTSQ // wsign sign width, 0 or 1 // ik init -1 for nexK @@ -851,7 +856,7 @@ x _dfw = nDigB4Pt; // use the digits, not e or k format #endif // initial handling of k format overflow format: make value fit if can, if does not round up. - if ((fmt & FMTOVFMASK)==FMTOVFK) // if overlow is to be handled with "k format" + if ((fmtv & FMTOVFMASK)==FMTOVFK) // if overlow is to be handled with "k format" { if (_dfw < 3) // needs 3 digits or 2 + k to work in general: 499, 1k, 99k, .1M, etc. _dfw = min( SI(3), amfw); // so take them if avail; loop below reduces dfw if it helps. @@ -894,7 +899,7 @@ p Cvnchars = sprintf( str, gf[ijust][ipv], wid, _dfw, _val); // convert nu // done if fits field and not 'e' format when k format overflow specified if (Cvnchars <= amfw+wsign) // note mfw not reduced for k char if any - if ((fmt & FMTOVFMASK) != FMTOVFK || !strchr( str, 'e')) // if not 'k' fmt overflow or sprinf used no 'e' + if ((fmtv & FMTOVFMASK) != FMTOVFK || !strchr( str, 'e')) // if not 'k' fmtv overflow or sprinf used no 'e' break; // normal termination of loop // text wider than field (expected for roundup cases) or contains 'e' when k format desired @@ -905,7 +910,7 @@ p Cvnchars = sprintf( str, gf[ijust][ipv], wid, _dfw, _val); // convert nu if (--Cvnchars <= amfw+wsign) // is now 1 char narrower break; // now it fits! } - if ((fmt & FMTOVFMASK)==FMTOVFK) // if 'k' overflow format requested + if ((fmtv & FMTOVFMASK)==FMTOVFK) // if 'k' overflow format requested { if (nDigAfPt > 0 && _dfw > 3) // first trim digits after point: 123.4k-->.1234M is no gain { diff --git a/src/nummeth.cpp b/src/nummeth.cpp index 9822d60a5..3d71d4604 100644 --- a/src/nummeth.cpp +++ b/src/nummeth.cpp @@ -5,10 +5,13 @@ // nummeth.cpp -- numerical method functions /*------------------------------- INCLUDES --------------------------------*/ + #include "cnglob.h" #include "nummeth.h" // decls for this file +#include + #if 0 // Eigen experiments 3-29-2023 #include <\eigen-3.4.0\eigen\dense> @@ -166,7 +169,7 @@ int FC gaussjb( // Solve a system of equations using Gauss-Jordan elimination return 0; } //----------------------------------------------------------------------------- -int secant( // find x given f(x) (secant method) +int actualSecant( // find x given f(x) (secant method) double (*pFunc)( void *pO, double &x), // function under investigation; note that it // may CHANGE x re domain limits etc. @@ -235,7 +238,94 @@ int secant( // find x given f(x) (secant method) } return i; } // ::secant + //----------------------------------------------------------------------------- +int secant( // screen secant success (see above); report calculation if failure + double (*pFunc)(void *pO, double &x), + void *pO, // pointer passed to *pFunc, typ object pointer + double f, // f( x) value sought + double eps, // convergence tolerance, hi- or both sides + // see also epsLo + double &x1, // x 1st guess, + // returned with result + double &f1, // f( x1), if known, else pass DBL_MIN + // returned: f( x1), may be != f, if no converge + double x2, // x 2nd guess + double f2 /*=DBL_MIN*/, // f( x2), if known + double epsLo /*=-1.*/) // lo-side convergence tolerance + +{ + double x1_prev = x1; // store entry values to enable repetition of secant (above) + double f1_prev = f1; // with output for troubleshooting + + int ret = actualSecant(pFunc, pO, f, eps, x1, f1, x2, f2, epsLo); + if (ret == 0) + return ret; + + x1 = x1_prev; // secant (above) failed. Restore values and repeat with output + f1 = f1_prev; + + auto msg = fmt::format("Secant failed to converge for target f = {:g}.\n", f); + + double fHi = f + eps; + double fLo = f - (epsLo >= 0. ? epsLo : eps); + + if (f1 == DBL_MIN) + f1 = (*pFunc)(pO, x1); + + if (f1 <= fHi && f1 >= fLo) // if 1st guess good + return 0; // success: don't do *pFunc( x2) + // (side effects) + + if (f2 == DBL_MIN) + f2 = (*pFunc)(pO, x2); + + if (fabs(f - f1) > fabs(f - f2)) // make point 1 the closer + { + std::swap(x1, x2); + std::swap(f1, f2); + } + + int i; + for (i = 0; ++i < 20;) // iterate to refine solution + { + + msg += fmt::format(" Iteration {}: x1 = {:g}, x2 = {:g}, f1 = {:g}, f2 = {:g}\n", i, x1, x2, f1, f2); + + if (f1 <= fHi && f1 >= fLo) { + i = 0; // success + break; // done; last *pFunc call ... + // 1st iteration: *pFunc( x2) + swap + // >1st iteration: *pFunc( x1) below + } + + if (fabs(f1 - f2) < 1.e-20) // if slope is 0 + { + i = -i; // tell caller + msg += " Zero-slope detected.\n"; + break; + } + + double xN = x1 + (x2 - x1) * (f - f1) / (f2 - f1); + + // secant method: new guess assuming local linearity. + x2 = x1; // replace older point + f2 = f1; + x1 = xN; + f1 = (*pFunc)(pO, x1); // new value + + + } + + if (i > 0) { + msg += " Maximum iterations hit.\n"; + } + + warn(msg.c_str()); + return i; +} // ::secant + + //----------------------------------------------------------------------------- int regula( // find x given f(x) (regula-falsi method) double (*pFunc)(void* pO, double& x), // function under investigation; note that it @@ -470,4 +560,5 @@ void test() } #endif + // end of nummeth.cpp \ No newline at end of file diff --git a/src/nummeth.h b/src/nummeth.h index b45a81298..a5a695a04 100644 --- a/src/nummeth.h +++ b/src/nummeth.h @@ -20,7 +20,6 @@ int secant( double (*pFunc)( void *pO, double &x), void *pO, double f, int regula(double (*pFunc)(void* pO, double& x), void* pO, double f, double eps, double& x1, double xMin, double xMax); - /////////////////////////////////////////////////////////////////////////////// // class DGRAPH: directed graph /////////////////////////////////////////////////////////////////////////////// diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 07cafab19..0ceba702a 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -3,12 +3,21 @@ set(source strpak.unit.cpp xiopak.unit.cpp cvpak.unit.cpp + nummeth.unit.cpp ${CSE_SOURCE_DIR}/src/libstubs.cpp + ${CSE_BINARY_DIR}/src/untab.cpp + ${CSE_BINARY_DIR}/src/dttab.cpp ) +set_source_files_properties( + "${CSE_BINARY_DIR}/src/untab.cpp" + "${CSE_BINARY_DIR}/src/dttab.cpp" + PROPERTIES GENERATED TRUE +) + list(APPEND source ${cse_common_source}) add_executable(cse_tests ${source}) -add_dependencies(cse_tests generate_dtypes) +add_dependencies(cse_tests generate_rcdef_outputs) target_compile_definitions(cse_tests PRIVATE SOURCE_DIR="${CMAKE_SOURCE_DIR}") target_include_directories(cse_tests PRIVATE diff --git a/test/unit/cvpak.unit.cpp b/test/unit/cvpak.unit.cpp index 1c8ff7cdd..4daf8df47 100644 --- a/test/unit/cvpak.unit.cpp +++ b/test/unit/cvpak.unit.cpp @@ -1,3 +1,5 @@ +#include + #include "gtest/gtest.h" #include "cnglob.h" @@ -25,13 +27,15 @@ TEST(cvpak, output_convert) struct FVTOS { float fV; - int units; + SI units; int mfw; int fmt; int xfw; const char* exp; }; + float nanf = std::numeric_limits::infinity()/std::numeric_limits::infinity(); + FVTOS fvt[] = { { 5.f, UNNONE, 10, FMTLJ, 0, "5 " }, #if 0 @@ -40,7 +44,9 @@ TEST(cvpak, output_convert) #endif { 5.f, UNNONE, 10, FMTRJ, 0, " 5" }, { 5000000.f, UNNONE, 6, FMTLJ, 0, "5000 k" }, - { -11.3700f, UNNONE, 10, (FMTSQ | FMTRTZ) + 4, 0, "-11.37" } + { -11.3700f, UNNONE, 10, (FMTSQ | FMTRTZ) + 4, 0, "-11.37" }, + { nanf, UNNONE, 10, (FMTSQ | FMTRTZ) + 4, 0, "nan" }, + { nanf, UNLENGTH, 10, (FMTSQ | FMTRTZ) + 4, 0, "nan" }, }; for (FVTOS& fv : fvt) @@ -49,6 +55,30 @@ TEST(cvpak, output_convert) EXPECT_STREQ(str, fv.exp); } + // double tests -- numeric values + struct DVTOS + { + double dV; + SI units; + int mfw; + int fmt; + int xfw; + const char* exp; + }; + + double nand = std::numeric_limits::infinity()/std::numeric_limits::infinity(); + + DVTOS dvt[] = + { + { nand, UNENERGY1, 10, FMTSQ+FMTUNITS+4, 0, "nan kBtu" } + }; + + for (DVTOS& dv : dvt) + { + const char* str = cvin2s(&dv.dV, DTDBL, dv.units, dv.mfw, dv.fmt, dv.xfw); + EXPECT_STREQ(str, dv.exp); + } + #if 0 const void* data, // Pointer to data in internal form, or NULL to do nothing and return NULL @@ -67,5 +97,4 @@ TEST(cvpak, output_convert) // Also sets global Cvnchars to the number of characters placed in str (not incl. '\0'). #endif - } diff --git a/test/unit/nummeth.unit.cpp b/test/unit/nummeth.unit.cpp new file mode 100644 index 000000000..0d88efb8a --- /dev/null +++ b/test/unit/nummeth.unit.cpp @@ -0,0 +1,91 @@ +// nummeth.unit.cpp - test uses of numerical-method functions + +#include "gtest/gtest.h" + +#include "cnglob.h" +#include "cvpak.h" +#include "nummeth.h" + +// linear function +static double linear_func(void *, double &x) { + return x; +} + +// inverse function +static double inverse_func(void *, double &x) { + double f = DBL_MAX; + if (abs(x) > 0.0001) { + f = 1. / x; + } + return f; +} + +TEST(nummeth, secant_test) { + + double eps = .0001; + + { // solution of linear function + double x1 = 1., x2 = 3.; + double f1 = DBL_MIN, f2 = DBL_MIN; + double fTarg = 2.; + + int ret = secant(linear_func, NULL, fTarg, eps * fTarg, x1, f1, // x1, f1 + x2, f2); // x2, f2 + + EXPECT_EQ(ret, 0) << "secant solution of linear function failed."; + double xExpected = fTarg; + EXPECT_NEAR(x1, xExpected, eps) + << "expected solution of linear function not found."; + } + + { // solution of inverse function + double x1 = 0.01, x2 = 1.0; + double f1 = DBL_MIN, f2 = DBL_MIN; + double fTarg = 2.; + + int ret = + secant(inverse_func, NULL, fTarg, eps * fTarg, x1, f1, // x1, f1 + x2, f2); // x2, f2 + + EXPECT_EQ(ret, 0) << "secant solution of inverse function failed."; + double xExpected = 1. / fTarg; + EXPECT_NEAR(x1, xExpected, eps) + << "expected solution of inverse function not found."; + } + + { // solution of inverted inverse function + double x1 = 0.01, x2 = 1.0; + double f1 = DBL_MIN, f2 = DBL_MIN; + double fTarg = 1. / 2.; + + + + int ret = + secant([](void*, double& x){return 1./inverse_func(NULL, x);}, NULL, fTarg, eps * fTarg, x1, f1, // x1, f1 + x2, f2); // x2, f2 + + EXPECT_EQ(ret, 0) << "secant solution of inverted inverse function failed."; + double xExpected = fTarg; + EXPECT_NEAR(x1, xExpected, eps) + << "expected solution of inverted inverse function not found."; + } + + { // solution of inverted inverse function (below "zero" tolerance) + double x1 = 0.01, x2 = 1.0; + double f1 = DBL_MIN, f2 = DBL_MIN; + double fTarg = 0.00005; + + + + int ret = + secant([](void*, double& x){return 1./inverse_func(NULL, x);}, NULL, fTarg, eps * fTarg, x1, f1, // x1, f1 + x2, f2); // x2, f2 + + EXPECT_EQ(ret, -3) << "secant solution of inverted inverse function failed."; + double xExpected = fTarg; + EXPECT_NEAR(x1, xExpected, eps) + << "expected solution of inverted inverse function not found."; + } + + +} diff --git a/test/unit/xiopak.unit.cpp b/test/unit/xiopak.unit.cpp index 2c87a4f63..f28612280 100644 --- a/test/unit/xiopak.unit.cpp +++ b/test/unit/xiopak.unit.cpp @@ -2,7 +2,6 @@ #include "cnglob.h" #include "xiopak.h" -#include "srd.h" // Include filesystem #if __has_include() @@ -15,12 +14,6 @@ namespace filesystem = std::experimental::filesystem; #error "no filesystem support" #endif -// Stubs defined separately for RCDEF -ULI Dttab[691]; -UNIT Untab[80*sizeof(UNIT)]; -int Unsysext = 0; - - RC check_sec(SEC sec) { // If sec == SECOK, return RC of zero