From f849988a0e02d361dd9cb455f9db24a718a040fe Mon Sep 17 00:00:00 2001 From: Phil Ahrenkiel Date: Mon, 18 Mar 2024 13:10:42 -0600 Subject: [PATCH 01/21] Check isnan. --- src/cvpak.cpp | 3361 ++++++++++++++++++++++++++----------------------- 1 file changed, 1767 insertions(+), 1594 deletions(-) diff --git a/src/cvpak.cpp b/src/cvpak.cpp index 30d3b2856..a874273c9 100644 --- a/src/cvpak.cpp +++ b/src/cvpak.cpp @@ -7,10 +7,10 @@ #include "cnglob.h" -#undef M0BUG // #define to enable code looking for -0 / -1 bug 4-3-2022 - // see github issue #213 +#undef M0BUG // #define to enable code looking for -0 / -1 bug 4-3-2022 + // see github issue #213 -#if defined( M0BUG) +#if defined(M0BUG) #include "ancrec.h" #include "rccn.h" #endif @@ -18,112 +18,121 @@ /*-------------------------------- OPTIONS --------------------------------*/ /*------------------------------- INCLUDES --------------------------------*/ -#include "msghans.h" // MH_xxxx +#include "msghans.h" // MH_xxxx -#include "srd.h" // Unsysext, Untab, GetDttab +#include "srd.h" // Unsysext, Untab, GetDttab #include "tdpak.h" -#include "cvpak.h" // decls for this file +#include "cvpak.h" // decls for this file /*-------------------------------- DEFINES --------------------------------*/ /*--------------------------- PUBLIC VARIABLES ----------------------------*/ /*---------------------------- LOCAL VARIABLES ----------------------------*/ -static int Cvnchars = 0; // # output chars from internal to external conversion. +static int Cvnchars = 0; // # output chars from internal to external conversion. // cvin2s and callees working variables -#ifdef FMTPVMASK // define in cvpak.h to restore p positive value display options, 11-91 -p static SI pv; // positive value display: null, +, spc -p static SI ipv; // ditto right-shifted for use as subscript -p /* note positive value options not used (except check debugpr.c); +#ifdef FMTPVMASK // define in cvpak.h to restore p positive value display + // options, 11-91 +p static SI pv; // positive value display: null, +, spc +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 mfw; // cvin2s "max field width" argument. -static SI just; // justification: left, rt, rz, squeeze. -static SI ijust; // ditto right-shifted for use as subscript. -static SI lj; // left justify (just & FLMLJ). -static SI lz; // leading 0's (right justify). note FMTLZ not used, could code out. rob 10-88 -static SI wsign; // 0 or 1 columns for sign, set by cvin2s -static SI wid; // width for sprintf: 1 for squeeze, else mfw. cvdd expects caller to set. -static SI ppos; // precomputed sprintf precision (prcsn) for several cases for values >= 0 -static SI pneg; // precomputed sprintf precision (prcsn) for serveral cases for values < 0 -static double aval; // fabs(val): set by cvsd, cvdd, ft-in; updated by nexK. -static double val; // value for float/double print cases -static char * str; // Tmpstr destination location + static USI + fmt; // 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. +static SI lj; // left justify (just & FLMLJ). +static SI lz; // leading 0's (right justify). note FMTLZ not used, could code + // out. rob 10-88 +static SI wsign; // 0 or 1 columns for sign, set by cvin2s +static SI wid; // width for sprintf: 1 for squeeze, else mfw. cvdd expects + // caller to set. +static SI ppos; // precomputed sprintf precision (prcsn) for several cases for + // values >= 0 +static SI pneg; // precomputed sprintf precision (prcsn) for serveral cases for + // values < 0 +static double aval; // fabs(val): set by cvsd, cvdd, ft-in; updated by nexK. +static double val; // value for float/double print cases +static char *str; // Tmpstr destination location /*----------------------------- INITIALIZED DATA --------------------------*/ -#ifdef FMTPVMASK // define in cvpak.h to restore p positive value display options, 11-91 -p -p /* Idea: left-justify format appears to ALWAYS differ by having leading -; -p could create from base format at run time ? */ -p /* These format definitions must match definitions of FMTPVxxx in cvpak.h */ -p /* ipv: if >= 0, show null, +, space */ -p add NEAR's if restored. -p /* SI and floating 0 formats */ -p static char *sif[2][3] = {"%*.*d", "%+*.*d", "% *.*d", /* other */ -p "%-*.*d", "%-+*.*d", "%- *.*d"}; /* left just */ -p static char *lif[2][3] = {"%*.*ld", "%+*.*ld", "% *.*ld", /* other */ -p "%-*.*ld","%-+*.*ld","%- *.*ld"}; /* lj */ -p static char *usif[2] = {"%*.*u", /* other */ -p "%-*.*u"}; /* lj */ -p static char *sf[2] = {"%*.*s", /* other */ -p "%-*.*s"}; /* lj */ -p /* basic float: done in cvdd(). */ -p -p /* for trim trailing 0's. ipv: -null + spc */ -p static char *gf[4][3] = {"%-*.*g", "%-+*.*g", "%- *.*g", /* left j */ -p "%*.*g", "%+*.*g", "% *.*g", /* rj */ -p "%0*.*g", "%0+*.*g", "%0 *.*g", /* rj 0s */ -p "%*.*g", "%+*.*g", "% *.*g" -p }; /* sqz */ -p -p /* IP lengths. subscript ipv: null, +, space */ -p /* dfw 0, feet and inches */ -p static char *ff1[3] = {"%*.*ld'%*d", "%+*.*ld'%*d", "% *.*ld'%*d"}; +#ifdef FMTPVMASK // define in cvpak.h to restore p positive value display + // options, 11-91 +p p /* Idea: left-justify format appears to ALWAYS differ by having leading -; + p could create from base format at run time ? */ + p /* These format definitions must match definitions of FMTPVxxx in + cvpak.h */ + p /* ipv: if >= 0, show null, +, space */ + p add NEAR's if restored. p /* SI and floating 0 formats */ p static char + *sif[2][3] = {"%*.*d", "%+*.*d", "% *.*d", /* other */ + p "%-*.*d", "%-+*.*d", "%- *.*d"}; /* left just */ +p static char *lif[2][3] = {"%*.*ld", "%+*.*ld", "% *.*ld", /* other */ + p "%-*.*ld", "%-+*.*ld", "%- *.*ld"}; /* lj */ +p static char *usif[2] = {"%*.*u", /* other */ + p "%-*.*u"}; /* lj */ +p static char *sf[2] = {"%*.*s", /* other */ + p "%-*.*s"}; /* lj */ +p /* basic float: done in cvdd(). */ + p p /* for trim trailing 0's. ipv: + null + spc */ + p static char *gf[4][3] = { + "%-*.*g", "%-+*.*g", "%- *.*g", /* left j */ + p "%*.*g", "%+*.*g", "% *.*g", /* rj */ + p "%0*.*g", "%0+*.*g", "%0 *.*g", /* rj 0s */ + p "%*.*g", "%+*.*g", "% *.*g" p}; /* sqz */ +p p /* IP lengths. subscript ipv: null, +, space */ + p /* dfw 0, feet and inches */ + p static char *ff1[3] = {"%*.*ld'%*d", "%+*.*ld'%*d", "% *.*ld'%*d"}; p /* dfw 0, inches only */ -p static char *ff3[3] = {"%*.*d", "%+*.*d", "% *.*d"}; + p static char *ff3[3] = {"%*.*d", "%+*.*d", "% *.*d"}; p /* dfw nz, feet and inches */ -p static char *ff4[3] = {"%*.*ld'%*.*f", "%+*.*ld'%*.*f", "% *.*ld'%*.*f"}; + p static char *ff4[3] = {"%*.*ld'%*.*f", "%+*.*ld'%*.*f", "% *.*ld'%*.*f"}; p /* dfw nz, inches only */ -p static char *ff5[3] = {"%*.*f", "%+*.*f", "% *.*f"}; + p static char *ff5[3] = {"%*.*f", "%+*.*f", "% *.*f"}; p #else -// sprintf formats, used in cvin2s, cvin2Ft_in. Basic float formats are in cvdd. +// sprintf formats, used in cvin2s, cvin2Ft_in. Basic float formats are in +// cvdd. // -// Idea: left-justify formats appear to differ by leading -; create from base format at run time ? +// Idea: left-justify formats appear to differ by leading -; create +// from base format at run time ? // // SI and floating 0 formats. other left just -static char * sif[2] = { "%*.*d", "%-*.*d" }; -static char * lif[2] = { "%*.*ld", "%-*.*ld" }; -static char * usif[2] = { "%*.*u", "%-*.*u" }; -static char * sf[2] = { "%*.*s", "%-*.*s" }; +static char *sif[2] = {"%*.*d", "%-*.*d"}; +static char *lif[2] = {"%*.*ld", "%-*.*ld"}; +static char *usif[2] = {"%*.*u", "%-*.*u"}; +static char *sf[2] = {"%*.*s", "%-*.*s"}; // // for trim trailing 0's: left j rj rj 0s sqz -static char * gf[4] = { "%-*.*g", "%*.*g", "%0*.*g", "%*.*g" }; +static char *gf[4] = {"%-*.*g", "%*.*g", "%0*.*g", "%*.*g"}; // // IP length formats -static char * ff1 = "%*.*ld'%*d"; // dfw 0, feet and inches -static char * ff3 = "%*.*d"; // dfw 0, inches only -static char * ff4 = "%*.*ld'%*.*f"; // dfw nz, feet and inches -static char * ff5 = "%*.*f"; // dfw nz, inches only +static char *ff1 = "%*.*ld'%*d"; // dfw 0, feet and inches +static char *ff3 = "%*.*d"; // dfw 0, inches only +static char *ff4 = "%*.*ld'%*.*f"; // dfw nz, feet and inches +static char *ff5 = "%*.*f"; // dfw nz, inches only #endif -/*----------------------- LOCAL FUNCTION DECLARATIONS ---------------------*/ -LOCAL SI FC cvttz( char *str, SI trailChar, SI minWid); + /*----------------------- LOCAL FUNCTION DECLARATIONS + ---------------------*/ + LOCAL SI FC + cvttz(char *str, SI trailChar, SI minWid); LOCAL bool FC nexK(); -LOCAL bool FC cvsd( SI mfw, SI dfw); -LOCAL bool FC cvdd( SI mfw, SI dfw); +LOCAL bool FC cvsd(SI mfw, SI dfw); +LOCAL bool FC cvdd(SI mfw, SI dfw); LOCAL void FC cvDouble2s(void); LOCAL void FC cvFtIn2s(void); -LOCAL int FC sepFtInch( double d, int& inch); +LOCAL int FC sepFtInch(double d, int &inch); // unmaintained test code is at end //====================================================================== -char * FC cvin2sBuf( char *buf, const void *data, USI dt, SI units, USI _mfw, USI _fmt) +char *FC cvin2sBuf(char *buf, const void *data, USI dt, SI units, USI _mfw, + USI _fmt) // Convert internal format data to external format string in caller's buffer @@ -132,253 +141,286 @@ char * FC cvin2sBuf( char *buf, const void *data, USI dt, SI units, USI _mfw, US // returns buf (or Tmpstr location if NULL given). // Also sets Cvnchars to strlen(result). { - char *p; + char *p; - p = cvin2s( data, dt, units, _mfw, _fmt); // convert (next) - if (p==NULL) // if data is NULL, cvin2s nops 9-89 - return NULL; /* nop here too 9-89 (putting "" or right # blanks - in buf might make more sense?). */ - return buf ? strcpy( buf, // copy into caller's buffer - strtempPop(p)) // free Tmpstr space (so less likely to wrap onto caller's stuff). - // recursive. returns p. strpak.c. - : p; // no buffer given, return Tmpstr location + p = cvin2s(data, dt, units, _mfw, _fmt); // convert (next) + if (p == NULL) // if data is NULL, cvin2s nops 9-89 + return NULL; /* nop here too 9-89 (putting "" or right # blanks + in buf might make more sense?). */ + return buf ? strcpy( + buf, // copy into caller's buffer + strtempPop( + p)) // free Tmpstr space (so less likely to wrap onto + // caller's stuff). recursive. returns p. strpak.c. + : p; // no buffer given, return Tmpstr location -} // cvin2sBuf +} // cvin2sBuf //====================================================================== -char * FC cvin2s( // Convert internal format data to external format string in Tmpstr - - const void* data, // Pointer to data in internal form, or NULL to do nothing and return NULL - // (for DTCULSTR, is ptr to ptr to string to print, 11-91) */ - USI dt, // Data type of internal data, or DTNA for "--" or DTUNDEF for "?" from cvfddisp() - SI units, // Units of internal data (made signed 5-89) - USI _mfw, // Maximum field width (not including '\0'). If requested format results in string longer - // than mfw, format will be altered if possible to give max significance within mfw; - // when not possible (never possible for inteters or if field too narrow for e or k format), - // field of **** is returned (but see _xfw). - USI _fmt, // Format. See cvpak.h for definition of fields. - USI _xfw /*=0*/ ) /* 0 or extra field width available: if value cannot be formatted in _mfw columns does fit - in _mfw + _xfw or fewer cols, return the overlong text rather than ******. added by rob, 4-92. */ +char *FC +cvin2s( // Convert internal format data to external format string in Tmpstr + + const void + *data, // Pointer to data in internal form, or NULL to do nothing and + // return NULL + // (for DTCULSTR, is ptr to ptr to string to print, 11-91) */ + USI dt, // Data type of internal data, or DTNA for "--" or DTUNDEF for "?" + // from cvfddisp() + SI units, // Units of internal data (made signed 5-89) + USI _mfw, // Maximum field width (not including '\0'). If requested format + // results in string longer than mfw, format will be altered if + // possible to give max significance within mfw; when not possible + // (never possible for inteters or if field too narrow for e or k + // format), field of **** is returned (but see _xfw). + USI _fmt, // Format. See cvpak.h for definition of fields. + USI _xfw /*=0*/) /* 0 or extra field width available: if value cannot be + formatted in _mfw columns does fit in _mfw + _xfw or fewer cols, + return the overlong text rather than ******. added by rob, + 4-92. */ // Returns pointer to result in Tmpstr. -// Also sets global Cvnchars to the number of characters placed in str (not incl. '\0'). +// Also sets global Cvnchars to the number of characters placed in str (not +// incl. '\0'). -// This routine will in in some cases overwrite one or more chars beyond the specified field. \0 always appended. +// This routine will in in some cases overwrite one or more chars beyond the +// specified field. \0 always appended. { #ifdef DTPERCENT - bool percent = false; // set true if converting a DTPERCENT; shares DTFLOAT code + bool percent = + false; // set true if converting a DTPERCENT; shares DTFLOAT code #endif - fmt = _fmt; // store format arg for use by callees - mfw = _mfw; // .. max field width arg + fmt = _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. - (note pgw also nops on getting null 9-89; using 9-89 in crlout.c) */ - if ( data==NULL + /* NULL data pointer means do NOTHING */ /* for caller convenience in + supporting optional stuff, + tentative 9-89. (note pgw also + nops on getting null 9-89; using + 9-89 in crlout.c) */ + if (data == NULL #ifdef DTUNDEF - && dt != DTUNDEF // these use no data and "data" may be garbage + && dt != DTUNDEF // these use no data and "data" may be garbage #endif #ifdef DTNA - && dt != DTNA // .. from cvpak3:cvfddisp() + && dt != DTNA // .. from cvpak3:cvfddisp() #endif - ) - return NULL; // or should it ret blank field? set Cvnchars? 9-89. - -// 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 - 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. - - /* 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 ipv = ((USI)pv) >> FMTPVSHIFT; // .. shifted for use as subscript -- frequently used to select formats -p // (shift made unsigned for lint) + ) + return NULL; // or should it ret blank field? set Cvnchars? 9-89. + + // 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 + 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. + + /* 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 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 - ijust = ((USI)just) >> FMTJSHIFT; // .. shifted for use as subscript - // (shift made unsigned for lint) - lj = just==FMTLJ; // left justify flag - lz = just==FMTLZ; // leading zeroes (right justify) flag - wid = just==FMTSQ ? 1 : mfw; // width for sprintf: 1 to squeeze (float only). note cvdd uses file-global. - // for several cases, preset sprintf precision (ppos, pneg, prcsn): min digits for many formats other than e, f. - if (lz) // if leading zero format requested - { + just = fmt & 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 + lz = just == FMTLZ; // leading zeroes (right justify) flag + wid = just == FMTSQ ? 1 : mfw; // width for sprintf: 1 to squeeze (float + // only). note cvdd uses file-global. + // for several cases, preset sprintf precision (ppos, pneg, prcsn): min digits + // for many formats other than e, f. + if (lz) // if leading zero format requested + { #ifdef FMTPVMASK -p ppos = // precision positive/unsigned data -p pv==FMTPVNULL // if null for +, -p ? wid : wid - 1; // full width, else 1 less + p ppos = // precision positive/unsigned data + p pv == FMTPVNULL // if null for +, + p + ? wid + : wid - 1; // full width, else 1 less #else - ppos = wid; // precision for positive/unsigned data is full width + ppos = wid; // precision for positive/unsigned data is full width #endif - pneg = wid - 1; // precision for neg several cases: leave column for - - } - else // for other formats - ppos = pneg = 1; // init pos and neg precision to 1 - - // Datatype specific internal-->external processing - INT iV{ 0 }; - UINT uiV{ 0 }; - switch (dt) - { + pneg = wid - 1; // precision for neg several cases: leave column for - + } else // for other formats + ppos = pneg = 1; // init pos and neg precision to 1 + + // Datatype specific internal-->external processing + INT iV{0}; + UINT uiV{0}; + switch (dt) { #ifdef FMTPVMASK -p case DTSI: -p if (*(SI *)data == 0 && pv==FMTPVPLUS) -p pv=FMTPVSPACE; -p Cvnchars = sprintf( str, sif[lj][ipv], wid, *(SI *)data < 0 ? pneg : ppos, *(SI *)data); -p break; + p case DTSI: + p if (*(SI *)data == 0 && pv == FMTPVPLUS) p pv = FMTPVSPACE; + p Cvnchars = sprintf(str, sif[lj][ipv], wid, *(SI *)data < 0 ? pneg : ppos, + *(SI *)data); + p break; #else - case DTINT: - iV = *(INT*)data; - goto intOut; - - case DTSI: - iV = *(SI*)data; - intOut: - Cvnchars = sprintf( str, sif[lj], wid, iV < 0 ? pneg : ppos, iV); - break; + case DTINT: + iV = *(INT *)data; + goto intOut; + + case DTSI: + iV = *(SI *)data; + intOut: + Cvnchars = sprintf(str, sif[lj], wid, iV < 0 ? pneg : ppos, iV); + break; #endif - case DTUINT: - uiV = *(UINT*)data; - goto uintOut; + case DTUINT: + uiV = *(UINT *)data; + goto uintOut; - case DTUSI: - uiV = *(USI*)data; - uintOut: - Cvnchars = sprintf( str, usif[lj], wid, ppos, uiV); - break; + case DTUSI: + uiV = *(USI *)data; + uintOut: + Cvnchars = sprintf(str, usif[lj], wid, ppos, uiV); + break; #ifdef FMTPVMASK -p case DTLI: -p if (*(LI *)data == 0 && pv==FMTPVPLUS) -p pv=FMTPVSPACE; -p Cvnchars = sprintf( str, lif[lj][ipv], wid, *(LI *)data < 0 ? pneg : ppos, *(LI *)data); -p break; + p case DTLI: + p if (*(LI *)data == 0 && pv == FMTPVPLUS) p pv = FMTPVSPACE; + p Cvnchars = sprintf(str, lif[lj][ipv], wid, *(LI *)data < 0 ? pneg : ppos, + *(LI *)data); + p break; #else - case DTLI: - Cvnchars = sprintf( str, lif[lj], wid, *(LI *)data < 0 ? pneg : ppos, *(LI *)data); - break; + case DTLI: + Cvnchars = + sprintf(str, lif[lj], wid, *(LI *)data < 0 ? pneg : ppos, *(LI *)data); + break; #endif - case DTDOY: - data = tddys( *(DOY *)data, TDYRNODOW, NULL); - goto strjust; + case DTDOY: + data = tddys(*(DOY *)data, TDYRNODOW, NULL); + goto strjust; - case DTDOW: - data = tddDowName( *(SI *)data); - goto strjust; + case DTDOW: + data = tddDowName(*(SI *)data); + goto strjust; - case DTMONTH: - data = tddMonAbbrev( *(SI *)data); - goto strjust; + case DTMONTH: + data = tddMonAbbrev(*(SI *)data); + goto strjust; - case DTIDATE: - data = tddis( *(IDATE *)data); - goto strjust; + case DTIDATE: + data = tddis(*(IDATE *)data); + goto strjust; - case DTITIME: - data = tdtis( (ITIME *)data, NULL); - goto strjust; + case DTITIME: + data = tdtis((ITIME *)data, NULL); + goto strjust; - case DTIDATETIME: - data = tddtis( (IDATETIME *)data, NULL); - goto strjust; + case DTIDATETIME: + data = tddtis((IDATETIME *)data, NULL); + goto strjust; #ifdef DTLDATETIME - case DTLDATETIME: - data = tdldts( *((LDATETIME *)data), NULL); - goto strjust; + case DTLDATETIME: + data = tdldts(*((LDATETIME *)data), NULL); + goto strjust; #endif - case DTDBL: - val = *(double *)data; - goto valValue; - -#ifdef DTPERCENT // put back in cndtypes.def to restore code, 12-3-91 - case DTPERCENT: - val = (*(float *)data)*100.; - mfw--; - if (wid > 1) - wid--; - percent = true; // flag tested after float formatting - goto valValue; // join float + case DTDBL: + val = *(double *)data; + goto valValue; + +#ifdef DTPERCENT // put back in cndtypes.def to restore code, 12-3-91 + case DTPERCENT: + val = (*(float *)data) * 100.; + mfw--; + if (wid > 1) + wid--; + percent = true; // flag tested after float formatting + goto valValue; // join float #endif - case DTFLOAT: -floatCase: // number-choice comes here (from default) if does not contain choice - { - NANDAT nd = *(NANDAT *)data; - if (!ISNUM(nd)) // check for non-number, cnglob.h macro, debug aid 2-27-92. - { - if (ISNCHOICE(nd)) // if number-choice choice (nan; unexpected here) - goto choiceCase; - if (ISNANDLE(nd)) // if unset or expr n (nan's) (insurance) - { - if (ISUNSET(nd)) - strcpy(str, ""); // say - else - sprintf(str, "", EXN(nd)); // say - break; - } - } - val = *(float*)data; // conver float value to print to double - } -valValue: // double, [percent] join here - val = cvIntoEx( val, units); // convert value to ext units + case DTFLOAT: + floatCase: // number-choice comes here (from default) if does not contain + // choice + { + NANDAT nd = *(NANDAT *)data; + if (!ISNUM(nd)) // check for non-number, cnglob.h macro, debug aid 2-27-92. + { + if (ISNCHOICE(nd)) // if number-choice choice (nan; unexpected here) + goto choiceCase; + if (ISNANDLE(nd)) // if unset or expr n (nan's) (insurance) + { + if (ISUNSET(nd)) + strcpy(str, ""); // say + else + sprintf(str, "", EXN(nd)); // say + break; + } + } + val = *(float *)data; // conver float value to print to double + } + valValue: // double, [percent] join here + val = cvIntoEx(val, units); // convert value to ext units #ifdef FMTPVMASK -p wsign = !(pv==FMTPVNULL && val >= 0.); // sign width + p wsign = !(pv == FMTPVNULL && val >= 0.); // sign width #else - wsign = (val < 0.); // sign width + wsign = (val < 0.); // sign width #endif - if (units == UNLENGTH && Unsysext==UNSYSIP) // if feet-inch float/double value - cvFtIn2s(); // convert 'val' to '*str' as feet-inches, local function, below. - else - cvDouble2s(); // convert 'val' to '*str', using many other variables. local function, below. + if (units == UNLENGTH && + Unsysext == UNSYSIP) // if feet-inch float/double value + cvFtIn2s(); // convert 'val' to '*str' as feet-inches, local function, + // below. + else + cvDouble2s(); // convert 'val' to '*str', using many other variables. + // local function, below. #ifdef DTPERCENT - if (percent) // finish percent: add '%'; make 1 col wider due to wid-- above. - { - SI i; - SI l = strlen(str); - for (i = 0; ; i++) if (*(str+i) != ' ') break; // find nonspace - while (*(str+(++i))) if (*(str+i)==' ') break; // find space after the nonspace, or end - *(str+i) = '%'; // put % there - if (i != l) *(str+l) = ' '; // append space unless just put % there - *(str+l+1) = '\0'; // reterminate - } + if (percent) // finish percent: add '%'; make 1 col wider due to wid-- + // above. + { + SI i; + SI l = strlen(str); + for (i = 0;; i++) + if (*(str + i) != ' ') + break; // find nonspace + while (*(str + (++i))) + if (*(str + i) == ' ') + break; // find space after the nonspace, or end + *(str + i) = '%'; // put % there + if (i != l) + *(str + l) = ' '; // append space unless just put % there + *(str + l + 1) = '\0'; // reterminate + } #endif -#if defined( M0BUG) - if (Top.tp_date.month == 12 && Top.tp_date.mday == 3 && Top.iHr == 5 && Top.iSubhr == 1) - { // trap subhour where output mismatch occurs - if (strcmp("-0", str) == 0 || strcmp("-1", str) == 0) - printf("\nHit str='%s' val=%f", str, val); - } +#if defined(M0BUG) + if (Top.tp_date.month == 12 && Top.tp_date.mday == 3 && Top.iHr == 5 && + Top.iSubhr == 1) { // trap subhour where output mismatch occurs + if (strcmp("-0", str) == 0 || strcmp("-1", str) == 0) + printf("\nHit str='%s' val=%f", str, val); + } #endif #if 0 x consider changing "-0" to "0"? x if (strcmp("-0", str) == 0) x printf("\n-0"); #endif - break; - - default: // a choice or number-choice data type -- else error - if (dt & DTBCHOICN) // if a number-choice see if now contains number - { - 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 - } -choiceCase: // float types comes here if NCHOICE found (unexpected) - if (dt & (DTBCHOICB|DTBCHOICN)) // if a choice type - { - SI handle = (dt & DTBCHOICN) - ? CHN(*(void **)data) // extract choicn choice value (cnglob.h macro) - : *(SI *)data; // choicb choice handle/index -#if 0 // not used in CSE 9-2-91 + break; + + default: // a choice or number-choice data type -- else error + if (dt & DTBCHOICN) // if a number-choice see if now contains number + { + 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 + } + choiceCase: // float types comes here if NCHOICE found (unexpected) + if (dt & (DTBCHOICB | DTBCHOICN)) // if a choice type + { + SI handle = + (dt & DTBCHOICN) + ? CHN(*(void **) + data) // extract choicn choice value (cnglob.h macro) + : *(SI *)data; // choicb choice handle/index +#if 0 // not used in CSE 9-2-91 x if (dt==DTPERINCH && Unsysext != UNSYSIP) x // fudge inches to mm for per-inch in other system x { if (handle == C_PERINCH_YES) @@ -387,424 +429,451 @@ x else if (handle == C_PERINCH_NO) x handle = C_PERMM_NO; x } #endif - data = ::getChoiTxI( dt, handle); // get choicb/choicn text - goto strjust; - } - else // not a choice type - goto undef; + data = ::getChoiTxI(dt, handle); // get choicb/choicn text + goto strjust; + } else // not a choice type + goto undef; #ifdef DTNA - case DTNA: - data = "--"; - goto strjust; + case DTNA: + data = "--"; + goto strjust; #endif - case DTCULSTR: - data = (*(CULSTR*)data).CStr(); - goto strjust; // data is pointer to string + case DTCULSTR: + data = (*(CULSTR *)data).CStr(); + goto strjust; // data is pointer to string - case DTCH: // for char array or string ptr already dereferenced, rob 11-91 - case DTANAME: // char[ ] RAT name -strjust: - Cvnchars = sprintf( str, sf[ lj], wid, mfw, data); - break; + case DTCH: // for char array or string ptr already dereferenced, rob 11-91 + case DTANAME: // char[ ] RAT name + strjust: + Cvnchars = sprintf(str, sf[lj], wid, mfw, data); + break; #ifdef DTUNDEF - case DTUNDEF: + case DTUNDEF: #endif -undef: - if (wid > 1) - memset( str, ' ', wid); - if (lj) - *str = '?'; - else - *(str+wid-1) = '?'; - *(str+wid)='\0'; - Cvnchars = wid; - break; - - } // switch (dt) - - /* if still too wide [and not variable data], fill with *******. Note most floating values have been made to fit. */ - - if ((USI)Cvnchars > mfw + _xfw) // return oversize field anyway if <= _xfw (argument) chars overwide - { + undef: + if (wid > 1) + memset(str, ' ', wid); + if (lj) + *str = '?'; + else + *(str + wid - 1) = '?'; + *(str + wid) = '\0'; + Cvnchars = wid; + break; + + } // switch (dt) + + /* if still too wide [and not variable data], fill with *******. Note most + * floating values have been made to fit. */ + + if ((USI)Cvnchars > mfw + _xfw) // return oversize field anyway if <= _xfw + // (argument) chars overwide + { #ifdef DTPERCENT - if (percent) - { - mfw++; - percent = false; - } + if (percent) { + mfw++; + percent = false; + } #endif - memset( str, '*', mfw); // fill with ****** - *(str + mfw)='\0'; - Cvnchars = mfw; - } - - /* optionally add units. Note ' " for ft-in done in cvFtIn2s. Suppressed for NCHOICE choice by clearing bits above. */ - - if (fmt & FMTUNITS) - { - strcat( str, " "); - strcat( str, UNIT::GetSymbol( units)); // concat units text - } - else if (fmt & FMTPU) // parenthesised units for res loads 2-90 - { - strcat( str, "("); - strcat( str, UNIT::GetSymbol( units)); // concat units text - strcat( str, ")"); - } - - return str; - // another return at entry -} // cvin2s + memset(str, '*', mfw); // fill with ****** + *(str + mfw) = '\0'; + Cvnchars = mfw; + } + + /* optionally add units. Note ' " for ft-in done in cvFtIn2s. Suppressed for + * NCHOICE choice by clearing bits above. */ + + if (fmt & FMTUNITS) { + strcat(str, " "); + strcat(str, UNIT::GetSymbol(units)); // concat units text + } else if (fmt & FMTPU) // parenthesised units for res loads 2-90 + { + strcat(str, "("); + strcat(str, UNIT::GetSymbol(units)); // concat units text + strcat(str, ")"); + } + + return str; + // another return at entry +} // cvin2s //====================================================================== //--- data shared by cvDouble2s, cvsd, nexK 4-92 -static SI amfw; // set by cvdd/cvsd: mfw adjusted for sign. used in cvDouble2s, nexK, -static SI ki; // 0 or number of times divided by 1000 for K format. cvDouble2s inits. -static SI nDigB4Pt; // number of digits b4 point, set in cvsd/cvdd, adj in nexK -static const char ddalpha[]=" kMGTPEZY?????"; // Chars for K format output field overflow handling +static SI + amfw; // set by cvdd/cvsd: mfw adjusted for sign. used in cvDouble2s, nexK, +static SI + ki; // 0 or number of times divided by 1000 for K format. cvDouble2s inits. +static SI nDigB4Pt; // number of digits b4 point, set in cvsd/cvdd, adj in nexK +static const char ddalpha[] = + " kMGTPEZY?????"; // Chars for K format output field overflow handling //====================================================================== -// 4-92: needs completion of review re producing minimum-overwidth result for xfw -- cvsd at least done. +// 4-92: needs completion of review re producing minimum-overwidth result for +// xfw -- cvsd at least done. -LOCAL void FC cvDouble2s() // float / double output conversion case for cvin2s +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: fmt, 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) - ki = 0; // say not in K format overflow (cvsd/nexK) + SI i; + SI dfw = fmt & FMTDFWMASK; // decimal field width (# decimal places) + ki = 0; // say not in K format overflow (cvsd/nexK) -// zero exception + // zero exception - if (val==0.) - { + if (val == 0.) { #ifdef FMTPVMASK -p if (pv==FMTPVPLUS) // show not + -p pv=FMTPVSPACE; // but space -p Cvnchars = sprintf( str, sif[lj][ipv], wid, ppos, 0); + p if (pv == FMTPVPLUS) // show not + + p pv = FMTPVSPACE; // but space + p Cvnchars = sprintf(str, sif[lj][ipv], wid, ppos, 0); #else - Cvnchars = sprintf( str, sif[lj], wid, ppos, 0); + Cvnchars = sprintf(str, sif[lj], wid, ppos, 0); #endif - return; - } - -// trim trailing zeroes (specified significant digits) option - - if (fmt & 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) - //else fall thru to basic conversion to get 0.000 for small numbers, rather than e- format nor overwide .000000n - } // if FMTRTZ - -// basic floating conversion (not ft-in, not 0, not FMTRTZ) - - if (cvdd( mfw, dfw)) // drifting decimal conversion, nz ok. Uses str, val, etc; sets amfw; below. - return; // if successful in mfw columns - -// field overflowed (rest of function) - - if ((fmt & 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 - - // 4-92: needs review re producing minimum-overwidth result for xfw. - - //static char ddalpha[]=" kMGTPE???????"; // Chars for K format output is above - double maxfit; - - if (amfw < 2) // set by cvdd: mfw adj for sign - return; // if can't possibly fix (and Pten does not go negative) - amfw--; // cols less sign: reduce 1 for kMG.. - maxfit = Pten[amfw]-0.5; // protection for amfw > sizeof Pten needed ??? 10-88 - if (wid > 1) // desired width, 1 for FMTSQ - wid--; // leave a col for kMG - for (i = 0; ; i++) - { - // more robust to give up HERE if i too big ?? 10-88 rob - if ( fabs(val) < maxfit // if now might fit width - && cvdd( mfw-1, dfw) ) // format it and see - break; // if now ok - val /= 1000.; // divide by 1000 and bump i till it works - } - if (ddalpha[i]=='?') // if out of range of char table - Cvnchars = mfw+1; // I think this causes ***** output - else - { - *(str+Cvnchars) = ddalpha[i]; // append k, M, G, ... - Cvnchars++; - *(str+Cvnchars) = '\0'; - } - } - else // cvdd fail and not K format - { - - // float overflow, e format - - // 4-92: needs review re producing minimum-overwidth result for xfw. - - - SI owid = wid; // save wid b4 exponent - SI oamfw = amfw; // ditto max field wid less sign - SI ew = 2; // init exponent width - i = 3; // exponent: start at 3 to gain 1 column - val /= 1000.; // adjust value to match exponent - while (1) // until fits or fails - { - if (oamfw-ew < 1) // if exp leaves no place for value - break; // give up - // I think Cvnchars is too big -> ***** on fallthru w exp - if (fabs(val) < Pten[oamfw-ew]) // if could fit cols - // no protection for amfw > size of Pten?? rob 10-88 - { - if (wid > 1) // (starts 1 for FMTSQ) - wid = owid-ew; // adjust wid: cvdd uses - if (cvdd( mfw-ew, dfw)) // format / if fits - break; // ok, go add exponent - } - val /= 10.; // divide by 10, bump i till it works - if (++i == 9) // exponent 10 - ew++; // requires extra column - // so why ++ at 9 ??? rob 10-88 - } - sprintf( str+Cvnchars, "e%d", i); // add exponent i - Cvnchars += ew; - } - // additional returns above -} // cvDouble2s + return; + } + + // trim trailing zeroes (specified significant digits) option + + if (fmt & 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) + // else fall thru to basic conversion to get 0.000 for small numbers, rather + // than e- format nor overwide .000000n + } // if FMTRTZ + + // basic floating conversion (not ft-in, not 0, not FMTRTZ) + + if (cvdd(mfw, dfw)) // drifting decimal conversion, nz ok. Uses str, val, + // etc; sets amfw; below. + return; // if successful in mfw columns + + // field overflowed (rest of function) + + if ((fmt & 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 + + // 4-92: needs review re producing minimum-overwidth result for xfw. + + // static char ddalpha[]=" kMGTPE???????"; // Chars for K format output is + // above + double maxfit; + + if (amfw < 2) // set by cvdd: mfw adj for sign + return; // if can't possibly fix (and Pten does not go negative) + amfw--; // cols less sign: reduce 1 for kMG.. + maxfit = + Pten[amfw] - 0.5; // protection for amfw > sizeof Pten needed ??? 10-88 + if (wid > 1) // desired width, 1 for FMTSQ + wid--; // leave a col for kMG + for (i = 0;; i++) { + // more robust to give up HERE if i too big ?? 10-88 rob + if ((fabs(val) < maxfit // if now might fit width + && cvdd(mfw - 1, dfw)) || // format it and see + std::isnan(val)) // guard against inf loop + break; // if now ok + val /= 1000.; // divide by 1000 and bump i till it works + } + if (ddalpha[i] == '?') // if out of range of char table + Cvnchars = mfw + 1; // I think this causes ***** output + else { + *(str + Cvnchars) = ddalpha[i]; // append k, M, G, ... + Cvnchars++; + *(str + Cvnchars) = '\0'; + } + } else // cvdd fail and not K format + { + + // float overflow, e format + + // 4-92: needs review re producing minimum-overwidth result for xfw. + + SI owid = wid; // save wid b4 exponent + SI oamfw = amfw; // ditto max field wid less sign + SI ew = 2; // init exponent width + i = 3; // exponent: start at 3 to gain 1 column + val /= 1000.; // adjust value to match exponent + while (1) // until fits or fails + { + if (oamfw - ew < 1) // if exp leaves no place for value + break; // give up + // I think Cvnchars is too big -> ***** on fallthru w exp + if (fabs(val) < + Pten[oamfw - ew]) // if could fit cols + // no protection for amfw > size of Pten?? rob 10-88 + { + if (wid > 1) // (starts 1 for FMTSQ) + wid = owid - ew; // adjust wid: cvdd uses + if (cvdd(mfw - ew, dfw)) // format / if fits + break; // ok, go add exponent + } + val /= 10.; // divide by 10, bump i till it works + if (++i == 9) // exponent 10 + ew++; // requires extra column + // so why ++ at 9 ??? rob 10-88 + } + sprintf(str + Cvnchars, "e%d", i); // add exponent i + Cvnchars += ew; + } + // additional returns above +} // cvDouble2s //====================================================================== -LOCAL void FC cvFtIn2s() // feet-inch length output conversion case for cvin2s +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: fmt, 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 - { - aval = fabs(val); // absolute val - 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 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. -#else // 11-91 quinch coded out as 1 - int ft = sepFtInch( val, inch); // separate/fix feet, inches - bool justInches = (ft == 0 && inch != 0); // true to omit feet + 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 + { + aval = fabs(val); // absolute val + 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 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. +#else // 11-91 quinch coded out as 1 + int ft = sepFtInch(val, inch); // separate/fix feet, inches + bool justInches = (ft == 0 && inch != 0); // true to omit feet #endif - // 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, - { - // dfw is total sig digs, not digits after . - // Reduce indfw to digits to print AFTER DECIMAL in INCHES. - int ndig = 0; - while (indfw > 0 && Pten[ndig++] <= aval) // while value < power of 10 - indfw--; // reduce for each feet digit - // (need no Pten size cks here due to 178000000 ck above) - ndig = 0; - double dinch = abs(double(inch)); - while (indfw > 0 && Pten[ndig++] <= dinch ) // while inches < pwr of 10 - indfw--; // reduce for each inches digit b4 . - } - - // printf width/space needed for inches if with feet - - int inw = justInches ? 0 // for inches-only fw is used, else - : 2 // 2 for integral inches (space b4 0-9; exact inw needed for rj) .. - - sq // but only 1 col if squeezing (no space b4 squeezed 0-9; inw < actual ok when sq on) - + (indfw!=0) // 1 for . if needed - + indfw; // digits after . - - // printf width for feet (or inches if no feet) - - int fw = lj ? 1 // 1 to left-justify, else - : wid - inw // wid less inch space and - - (!justInches) // less ' space and + // 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, + { + // dfw is total sig digs, not digits after . + // Reduce indfw to digits to print AFTER DECIMAL in INCHES. + int ndig = 0; + while (indfw > 0 && Pten[ndig++] <= aval) // while value < power of 10 + indfw--; // reduce for each feet digit + // (need no Pten size cks here due to 178000000 ck above) + ndig = 0; + double dinch = abs(double(inch)); + while (indfw > 0 && Pten[ndig++] <= dinch) // while inches < pwr of 10 + indfw--; // reduce for each inches digit b4 . + } + + // printf width/space needed for inches if with feet + + int inw = justInches ? 0 // for inches-only fw is used, else + : 2 // 2 for integral inches (space b4 0-9; exact inw + // needed for rj) .. + - sq // but only 1 col if squeezing (no space b4 + // squeezed 0-9; inw < actual ok when sq on) + + (indfw != 0) // 1 for . if needed + + indfw; // digits after . + + // printf width for feet (or inches if no feet) + + int fw = lj ? 1 // 1 to left-justify, else + : wid - inw // wid less inch space and + - (!justInches) // less ' space and #ifdef FMTNOQUINCH -x - quinch; // less " space + x - + quinch; // less " space #else - - 1; // less " space + - 1; // less " space #endif - if (fw < 1) // Prevent fw < 0: else trails blanks to - fw = 1; // .. -fw cols. NB wid=1 for FMTSQ (10-88). - int prcsn = lz ? fw-wsign : 1; // min # digits most cases - - // format feet-inches - - if (indfw) // if non-0 # dec places for inches - { - // show with decimal inches - double dinch = 0.; - if (justInches) // if showing inches only - { - dinch = val*12.; // compute float inches + if (fw < 1) // Prevent fw < 0: else trails blanks to + fw = 1; // .. -fw cols. NB wid=1 for FMTSQ (10-88). + int prcsn = lz ? fw - wsign : 1; // min # digits most cases + + // format feet-inches + + if (indfw) // if non-0 # dec places for inches + { + // show with decimal inches + double dinch = 0.; + if (justInches) // if showing inches only + { + dinch = val * 12.; // compute float inches #ifdef FMTPVMASK -p Cvnchars = sprintf( str, ff5[ipv], // *.*f -p fw, indfw, // width, precision -p dinch ); // floating inches + p Cvnchars = sprintf(str, ff5[ipv], // *.*f + p fw, indfw, // width, precision + p dinch); // floating inches #else - Cvnchars = sprintf( str, ff5, // *.*f - fw, indfw, // width, precision - dinch ); // floating inches + Cvnchars = sprintf(str, ff5, // *.*f + fw, indfw, // width, precision + dinch); // floating inches #endif - } - else // feet and inches - { - dinch = (val - ft)*((ft >= 0) ? 12 : -12); + } else // feet and inches + { + dinch = (val - ft) * ((ft >= 0) ? 12 : -12); #ifdef FMTPVMASK -p Cvnchars = sprintf( str, ff4[ipv], // 2.*f for inches -p fw, prcsn, // feet width, digits -p ft, -p inw, indfw, // inches wid, precis -p dinch ); // floating inches + p Cvnchars = sprintf(str, ff4[ipv], // 2.*f for inches + p fw, prcsn, // feet width, digits + p ft, p inw, indfw, // inches wid, precis + p dinch); // floating inches #else - Cvnchars = sprintf( str, ff4, // 2.*f for inches - fw, prcsn, // feet width, digits - ft, - inw, indfw, // inches wid, precis - dinch ); // floating inches + Cvnchars = sprintf(str, ff4, // 2.*f for inches + fw, prcsn, // feet width, digits + ft, inw, indfw, // inches wid, precis + dinch); // floating inches #endif - } - if (fmt & FMTRTZ) // trim trailing zeros option - { - // rob 10-88 to support FTMRTZ with FMTSQ - Cvnchars = - cvttz( str, // trim .0's from decimal inches - '"', // slide final " leftward - lj ? 1 : wid ); /* if lj or FMTSQ (wid=1), do fully (lj padded below). - If rj, don't shorten < wid: wd need to pad at - front, defer doing that code til need found. */ - } - } - else // indfw 0: show inches as integer only - { -#ifdef FMTPVMASK // define in cvpak.h to restore p positive value display options, 11-91 -p -p if (justInches) // if showing inches only -p Cvnchars = sprintf( str, ff3[ipv], -p fw, prcsn, // inches wid, digits -p inch ); -p else // feet and inches -p Cvnchars = sprintf( str,ff1[ipv], // %2d for inches -p fw, prcsn, // feet wid, digits -p ft, -p inw, // inches width -p inch ); + } + if (fmt & FMTRTZ) // trim trailing zeros option + { + // rob 10-88 to support FTMRTZ with FMTSQ + Cvnchars = cvttz( + str, // trim .0's from decimal inches + '"', // slide final " leftward + lj ? 1 : wid); /* if lj or FMTSQ (wid=1), do fully (lj padded + below). If rj, don't shorten < wid: wd need to pad + at front, defer doing that code til need found. */ + } + } else // indfw 0: show inches as integer only + { +#ifdef FMTPVMASK // define in cvpak.h to restore p positive value display + // options, 11-91 + p p if (justInches) // if showing inches only + p Cvnchars = sprintf(str, ff3[ipv], p fw, prcsn, // inches wid, digits + p inch); + p else // feet and inches + p Cvnchars = sprintf(str, ff1[ipv], // %2d for inches + p fw, prcsn, // feet wid, digits + p ft, + p inw, // inches width + p inch); #else - if (justInches) // if showing inches only - Cvnchars = sprintf( str, ff3, - fw, prcsn, // inches wid, digits - inch ); - else // feet and inches - Cvnchars = sprintf( str,ff1, // %2d for inches - fw, prcsn, // feet wid, digits - ft, - inw, // inches width - inch ); + if (justInches) // if showing inches only + Cvnchars = sprintf(str, ff3, fw, prcsn, // inches wid, digits + inch); + else // feet and inches + Cvnchars = sprintf(str, ff1, // %2d for inches + fw, prcsn, // feet wid, digits + ft, + inw, // inches width + inch); #endif - } -#ifdef FMTNOQUINCH // define in cvpak.h to restore feature, 11-91 -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 { -x if ( !justInches // if feet shown -x && *(str + Cvnchars-1)=='0' // 0 last -x && !isdigitW(*(str + Cvnchars-2)) ) // nondigit b4 0 -x { -x *(str + --Cvnchars) = '\0'; // truncate the 0 -x // quinch = 0; * add no " * quinch is 0 cuz FMTNOQUINCH on if here -x } -x } -x -x // add " for inches unless suppressed -x -x if (quinch /* && Cvnchars < mfw * to omit if won't fit */) -x { *(str + Cvnchars++) = '"'; -x *(str + Cvnchars) = 0; -x } + } +#ifdef FMTNOQUINCH // define in cvpak.h to restore feature, 11-91 + 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 { + x if (!justInches // if feet shown + x && + *(str + Cvnchars - 1) == '0' // 0 last + x && + !isdigitW(*(str + Cvnchars - 2))) // nondigit b4 0 + x { + x *(str + --Cvnchars) = '\0'; // truncate the 0 + x // quinch = 0; * add no " * quinch is + // 0 cuz FMTNOQUINCH on if here + x + } + x + } + x x // add " for inches unless suppressed + x x if (quinch /* && Cvnchars < mfw * to omit if won't fit */) x { + *(str + Cvnchars++) = '"'; + x *(str + Cvnchars) = 0; + x + } #else - // add " for inches + // add " for inches - // if (Cvnchars < mfw) {.. // to omit if won't fit - *(str + Cvnchars++) = '"'; - *(str + Cvnchars) = 0; + // if (Cvnchars < mfw) {.. // to omit if won't fit + *(str + Cvnchars++) = '"'; + *(str + Cvnchars) = 0; #endif - // right pad left-justified feet-inch values (wid was 1) + // right pad left-justified feet-inch values (wid was 1) - if (lj) // if left-justified - if ((USI)Cvnchars < mfw) // (beleived unnec) - strpad( str," ", (Cvnchars=mfw)); // pad to field width + if (lj) // if left-justified + if ((USI)Cvnchars < mfw) // (beleived unnec) + strpad(str, " ", (Cvnchars = mfw)); // pad to field width - // finally, check that fit field + // finally, check that fit field - biglen = (USI)Cvnchars > mfw; // set biglen true iff feet-inches too wide for field + biglen = (USI)Cvnchars > + mfw; // set biglen true iff feet-inches too wide for field - } // if (!biglen) + } // if (!biglen) -// if feet-inch value too big, show feet only + // if feet-inch value too big, show feet only - if (biglen) // if value too big for int inches at entry, OR feet-inches format exceeded field width - { - cvin2sBuf( str, (char *)&val, // recursive call to do feet only - DTDBL, - UNNONE, // no units - mfw-1, // save a column for ' - fmt ); - *(str+Cvnchars++) = '\''; // add foot mark ' to end - *(str+Cvnchars) = '\0'; - } -} // cvFtIn2s + if (biglen) // if value too big for int inches at entry, OR feet-inches format + // exceeded field width + { + cvin2sBuf(str, (char *)&val, // recursive call to do feet only + DTDBL, + UNNONE, // no units + mfw - 1, // save a column for ' + fmt); + *(str + Cvnchars++) = '\''; // add foot mark ' to end + *(str + Cvnchars) = '\0'; + } +} // cvFtIn2s //====================================================================== -LOCAL bool FC nexK() // K format incrementer: call each time need to make string shorter. false if cannot. +LOCAL bool FC nexK() // K format incrementer: call each time need to make string + // shorter. false if cannot. -// "K format" means 1000 shortened to 1k, 1234000 shortened to 1.234M or 1.23M or 1.2M, etc. see ddalpha[] for chars. +// "K format" means 1000 shortened to 1k, 1234000 shortened to 1.234M or 1.23M +// or 1.2M, etc. see ddalpha[] for chars. -// alters val (value), ki (assumed init 0), aval, wid, amfw, nDigB4Pt. Note mfw is not changed. -// after completion of formatting, caller must append ddalpha[ki] to string if ki > 0. +// alters val (value), ki (assumed init 0), aval, wid, amfw, nDigB4Pt. Note mfw +// is not changed. after completion of formatting, caller must append +// ddalpha[ki] to string if ki > 0. // returns false with variables unaltered if clearly cannot shrink more. // if true is returned, converted string may not be shorter due to round-up. { - if (!ki) // if not in K format yet - { - if (amfw <= 2) // no way without a digit and k after (note: need 2 digits + k to handle all values) - return false; // not enough room, period. - if (fabs(val) < 500.) // 500-->1k: smaller (too misleading?); 499.9999-->.5k: no smaller than 499. - return false; // too small to gain from shrinking - amfw--; // first time, reduce field width (after sign) by 1 for the k, M, etc - if (wid > 1) wid--; // unless FMTSQ, reduce sprintf width for the ditto. - } - else // already in K format - { - if (fabs(val) < 99.5) // 99.5k -->.1M: smaller than 100k. 99.49999k-->.09M, larger than 99k - return false; // too small to shrink more - if (ddalpha[ki+1]=='?') // if we have used up all the suffix characters (ie have divided by about 1e18) - return false; // can't shrink any more, let it **** - } - ki++; // first/next k format trailing character (init -1) - val /= 1000.; // divide value to print by 1000 - aval = fabs(val); // maintain absolute val for some callers - nDigB4Pt -= 3; // 3 less digits before point - setToMax( nDigB4Pt, 0); // .. but not less than 0. - return true; -} // nexK + if (!ki) // if not in K format yet + { + if (amfw <= 2) // no way without a digit and k after (note: need 2 digits + + // k to handle all values) + return false; // not enough room, period. + if (fabs(val) < 500.) // 500-->1k: smaller (too misleading?); + // 499.9999-->.5k: no smaller than 499. + return false; // too small to gain from shrinking + amfw--; // first time, reduce field width (after sign) by 1 for the k, M, + // etc + if (wid > 1) + wid--; // unless FMTSQ, reduce sprintf width for the ditto. + } else // already in K format + { + if (fabs(val) < 99.5) // 99.5k -->.1M: smaller than 100k. 99.49999k-->.09M, + // larger than 99k + return false; // too small to shrink more + if (ddalpha[ki + 1] == '?') // if we have used up all the suffix characters + // (ie have divided by about 1e18) + return false; // can't shrink any more, let it **** + } + ki++; // first/next k format trailing character (init -1) + val /= 1000.; // divide value to print by 1000 + aval = fabs(val); // maintain absolute val for some callers + nDigB4Pt -= 3; // 3 less digits before point + setToMax(nDigB4Pt, 0); // .. but not less than 0. + return true; +} // nexK //====================================================================== -LOCAL bool FC cvsd( // Significant-digits (trim trailing 0's, g format) output conversion +LOCAL bool FC +cvsd( // Significant-digits (trim trailing 0's, g format) output conversion - SI _mfw, // Maximum field width - SI _dfw ) // Desired number of significant digits + SI _mfw, // Maximum field width + SI _dfw) // Desired number of significant digits // and uses local statics including: // char *str, String into which to store converted value @@ -817,32 +886,46 @@ LOCAL bool FC cvsd( // Significant-digits (trim trailing 0's, g format) outpu // [ipv] /* returns false if number too small for method used here: - use cvdd to get eg 0.00 for .0003, .001 for 0.0006 in 4 cols, not .000n (too wide) nor e- format (too wide). - returns true if converted. If overwide, mininum width form returned. - Also sets: Cvnchars: Number of characters output: caller checks for excess width */ + use cvdd to get eg 0.00 for .0003, .001 for 0.0006 in 4 cols, + not .000n (too wide) nor e- format (too wide). returns true if converted. If + overwide, mininum width form returned. Also sets: Cvnchars: Number of + characters output: caller checks for excess width */ { - //ki = 0; // K format index, init by caller - aval = fabs(val); // absolute val - amfw = _mfw - wsign; // width less sign (public static). wsign is !(val >= 0. [&& pv==FMTPVNULL]) - setToMin( _dfw, amfw); // fix turkey calls; reduce for sign. Note may be increased to nDigB4Pt below. - -// return false if value too small for width: don't duplicate 0 exception and cvdd's small-value formatting. -// eg for 3 cols, need at least .095 to print here as .01; let cvdd do less (cvdd prints .005 as .01, .00499 as 0.0). -#define MINHERE 1e-4 // min abs value ever done here, per preferences & sprintf %g limits, was hard-coded .001. - // was hard-coded .001 4-92; also tried 1e-5, resulted in %g using e- format. - if (aval < 1) // 1 or greater always done here - if ( aval < MINHERE // less than this never handled here - || aval < 9.5*NPten[min(amfw,NPTENSIZE)] ) // if too small for g format in width even when only 1 sig dig - return false; // examples: amfw min print min val as power of 10 - // 1 1 .95 9.5e-1 - // 2 .1 .095 9.5e-2 etc - -// determine digits left of decimal point. Note nexK adjusts. - for ( nDigB4Pt = 0; // count digits before point (b4 rounding) - nDigB4Pt < PTENSIZE && Pten[nDigB4Pt] <= aval; // Pten: powers of 10, [0]..[15]. cons.c. - nDigB4Pt++ ) ; - -#if 0 // idea not used -- leave user the control of seeing eg 99.9k not 99990 by spec'ing 4 digits in 6 columns. 4-9-92. + // ki = 0; // K format index, init by + // caller + aval = fabs(val); // absolute val + amfw = _mfw - wsign; // width less sign (public static). wsign is !(val >= 0. + // [&& pv==FMTPVNULL]) + setToMin(_dfw, amfw); // fix turkey calls; reduce for sign. Note may be + // increased to nDigB4Pt below. + +// return false if value too small for width: don't duplicate 0 exception and +// cvdd's small-value formatting. +// eg for 3 cols, need at least .095 to print here as .01; let cvdd do less +//(cvdd prints .005 as .01, .00499 as 0.0). +#define MINHERE \ + 1e-4 // min abs value ever done here, per preferences & sprintf %g limits, was + // hard-coded .001. + // was hard-coded .001 4-92; also tried 1e-5, resulted in %g using e- format. + if (aval < 1) // 1 or greater always done here + if (aval < MINHERE // less than this never handled here + || + aval < + 9.5 * NPten[min(amfw, NPTENSIZE)]) // if too small for g format in + // width even when only 1 sig dig + return false; // examples: amfw min print min val as power of 10 + // 1 1 .95 9.5e-1 + // 2 .1 .095 9.5e-2 etc + + // determine digits left of decimal point. Note nexK adjusts. + for (nDigB4Pt = 0; // count digits before point (b4 rounding) + nDigB4Pt < PTENSIZE && + Pten[nDigB4Pt] <= aval; // Pten: powers of 10, [0]..[15]. cons.c. + nDigB4Pt++) + ; + +#if 0 // idea not used -- leave user the control of seeing eg 99.9k not 99990 by + // spec'ing 4 digits in 6 columns. 4-9-92. x// conditionally use more significant digits rather than an overflow format x if ( nDigB4Pt > _dfw // if value needs more digit positions than requested x && nDigB4Pt <= amfw // if field width allows these digit positions @@ -850,109 +933,145 @@ x && nDigB4Pt <= 6 ) // up to a million (rob's judgement) 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 (_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. - while (nDigB4Pt > _dfw && nexK()) ; // while too many digits b4 ., while possible, divide by 1000 & bump ki. - } - -// for numbers still too big for small field, use enough digits to get the least excess width, to fit best in mfw+xfw. - SI idigB4Pt = nDigB4Pt + (Pten[nDigB4Pt] <= aval + 0.5); // digits in integer format: may round up - if (idigB4Pt <= 5) // if integer form not wider than e format form "1e+01" - setToMax( _dfw, idigB4Pt); // do 1st convert with at least enuf digits to not get e format - -// format number as text, retry if comes out wider than field width - - for ( ; ; ) // loop to reduce significant digits and/or do k thing to fit into field - { - setToMin( _dfw, max(amfw,idigB4Pt)); // saves an iteration if nexK reduced amfW and number clearly fits - - /* pre-round when field width is exactly 1 more than digits b4 point, to fit values that sprintf itself cannot. - example: 999.9, mfw = 4: there is no dfw that will make sprintf do it for us (dfw=4 -->999.9: 5 cols; dfw=3 overflows) - but sprintf of 1000 with dfw==4 works. */ - double _val = nDigB4Pt+1==amfw // if have 1 column + digits b4 pt (less or more we can't fix) - && nDigB4Pt+1==_dfw // and are printing that # sig digits (less-->e format) - && Pten[nDigB4Pt] <= aval + 0.5 // and rounding adds digit b4 point (--dfw loop can't fix) - ? val + 0.5 : val; // then pre-round value to sprintf, else don't meddle. - - // save an overflow iteration if string is going to contain a decimal point - SI nDigAfPt = _dfw - nDigB4Pt; - if ( _dfw==amfw // if enuf digits to fill field specified - && nDigAfPt > 0 // and some digits after point - && ( nDigAfPt != 1 // and value like 999.9 - || Pten[nDigB4Pt] > aval + 0.5 ) ) // is not going to round to 1000, dropping point - _dfw--; // then drop a digit now, save time of a sprintf + // 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 (_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. + while (nDigB4Pt > _dfw && nexK()) + ; // while too many digits b4 ., while possible, divide by 1000 & bump ki. + } + + // for numbers still too big for small field, use enough digits to get the + // least excess width, to fit best in mfw+xfw. + SI idigB4Pt = + nDigB4Pt + + (Pten[nDigB4Pt] <= aval + 0.5); // digits in integer format: may round up + if (idigB4Pt <= 5) // if integer form not wider than e format form "1e+01" + setToMax(_dfw, idigB4Pt); // do 1st convert with at least enuf digits to not + // get e format + + // format number as text, retry if comes out wider than field width + + for (;;) // loop to reduce significant digits and/or do k thing to fit into + // field + { + setToMin(_dfw, max(amfw, idigB4Pt)); // saves an iteration if nexK reduced + // amfW and number clearly fits + + /* pre-round when field width is exactly 1 more than digits b4 point, to fit + values that sprintf itself cannot. example: 999.9, mfw = 4: there is no + dfw that will make sprintf do it for us (dfw=4 -->999.9: 5 cols; dfw=3 + overflows) but sprintf of 1000 with dfw==4 works. */ + double _val = + nDigB4Pt + 1 == amfw // if have 1 column + digits b4 pt (less or more we + // can't fix) + && + nDigB4Pt + 1 == + _dfw // and are printing that # sig digits (less-->e format) + && Pten[nDigB4Pt] <= aval + 0.5 // and rounding adds digit b4 + // point (--dfw loop can't fix) + ? val + 0.5 + : val; // then pre-round value to sprintf, else don't meddle. + + // save an overflow iteration if string is going to contain a decimal point + SI nDigAfPt = _dfw - nDigB4Pt; + if (_dfw == amfw // if enuf digits to fill field specified + && nDigAfPt > 0 // and some digits after point + && + (nDigAfPt != 1 // and value like 999.9 + || Pten[nDigB4Pt] > + aval + 0.5)) // is not going to round to 1000, dropping point + _dfw--; // then drop a digit now, save time of a sprintf #ifdef FMTPVMASK -p Cvnchars = sprintf( str, gf[ijust][ipv], wid, _dfw, _val); // convert number to string (c library) + p Cvnchars = sprintf(str, gf[ijust][ipv], wid, _dfw, + _val); // convert number to string (c library) #else - Cvnchars = sprintf( str, gf[ijust], wid, _dfw, _val); // convert number to string (c library) + Cvnchars = sprintf(str, gf[ijust], wid, _dfw, + _val); // convert number to string (c library) #endif - // 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' - break; // normal termination of loop - - // text wider than field (expected for roundup cases) or contains 'e' when k format desired - - if (!memcmp(str+wsign,"0.",2)) // if begins with "0." (implies following digits) - { - strcpy( str+wsign, str+wsign+1); // take out the leading 0 before the . - if (--Cvnchars <= amfw+wsign) // is now 1 char narrower - break; // now it fits! - } - if ((fmt & FMTOVFMASK)==FMTOVFK) // if 'k' overflow format requested - { - if (nDigAfPt > 0 && _dfw > 3) // first trim digits after point: 123.4k-->.1234M is no gain - { - _dfw--; - continue; // (after can't nexK, final digits after . trimmed below) - } - if (nexK()) continue; // do a(nother) k format /1000 / if could, format again - } - - // for overwide field, produce mininimum possible width for max chance of fitting mfw+xfw columns - - /* don't reduce dfw if further reduction would go to e format if this would widen field. - Shortest string is "1e+01", or dfw==nDigB4Pt for val < 9999.5 - Stop at dfw==nDigB4Pt if nDigB4Pt < 5, or at dfw==nDigB4Pt+1 if value rounds up. */ - - if (_dfw <= 1) break; // never go to 0 digits - - if ( nDigAfPt > 0 // if there are digits AFTER point - || amfw >= 5 // or space big enuf to hold e format: "1e+01" - || _dfw > 5 // or value still wider than 1-digit e format - || _dfw > nDigB4Pt // or number is not yet as narrow as it can get (integer format) - + (Pten[nDigB4Pt] <= aval + 0.5) ) // this term is 1 if number will round up to added digit - _dfw--; // drop one sig digit and retry - else - break; // stop, can't make it any narrower - } - -// append 'k', 'M', 'G', etc if k overflow format used - - if (ki) - { - *(str+Cvnchars) = ddalpha[ki]; // append k, M, G, ... - Cvnchars++; - *(str+Cvnchars) = '\0'; - } - - return true; // true: we did format the number (see small aval false return above) -} // cvsd + // 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' + break; // normal termination of loop + + // text wider than field (expected for roundup cases) or contains 'e' when k + // format desired + + if (!memcmp(str + wsign, "0.", + 2)) // if begins with "0." (implies following digits) + { + strcpy(str + wsign, + str + wsign + 1); // take out the leading 0 before the . + if (--Cvnchars <= amfw + wsign) // is now 1 char narrower + break; // now it fits! + } + if ((fmt & FMTOVFMASK) == FMTOVFK) // if 'k' overflow format requested + { + if (nDigAfPt > 0 && + _dfw > 3) // first trim digits after point: 123.4k-->.1234M is no gain + { + _dfw--; + continue; // (after can't nexK, final digits after . trimmed below) + } + if (nexK()) + continue; // do a(nother) k format /1000 / if could, format again + } + + // for overwide field, produce mininimum possible width for max chance of + // fitting mfw+xfw columns + + /* don't reduce dfw if further reduction would go to e format if this would + widen field. Shortest string is "1e+01", or dfw==nDigB4Pt for val < + 9999.5 Stop at dfw==nDigB4Pt if nDigB4Pt < 5, or at dfw==nDigB4Pt+1 if + value rounds up. */ + + if (_dfw <= 1) + break; // never go to 0 digits + + if (nDigAfPt > 0 // if there are digits AFTER point + || amfw >= 5 // or space big enuf to hold e format: "1e+01" + || _dfw > 5 // or value still wider than 1-digit e format + || _dfw > nDigB4Pt // or number is not yet as narrow as it can get + // (integer format) + + (Pten[nDigB4Pt] <= + aval + 0.5)) // this term is 1 if number will round + // up to added digit + _dfw--; // drop one sig digit and retry + else + break; // stop, can't make it any narrower + } + + // append 'k', 'M', 'G', etc if k overflow format used + + if (ki) { + *(str + Cvnchars) = ddalpha[ki]; // append k, M, G, ... + Cvnchars++; + *(str + Cvnchars) = '\0'; + } + + return true; // true: we did format the number (see small aval false return + // above) +} // cvsd //====================================================================== // 4-92: needs review re producing minimum-overwidth result for xfw. -LOCAL bool FC cvdd( // Drifting decimal output conversion +LOCAL bool FC cvdd( // Drifting decimal output conversion - SI _mfw, // Maximum field width - SI _dfw ) /* Decimal field width: # digits after point, - also, if non-0, # sig digits for numbers < .1 (digits after . conditionally increased) */ + SI _mfw, // Maximum field width + SI _dfw) /* Decimal field width: # digits after point, + also, if non-0, # sig digits for numbers < .1 (digits after . + conditionally increased) */ // and uses local statics including: // char *str, String into which to store converted value // double val Value to convert @@ -963,573 +1082,619 @@ LOCAL bool FC cvdd( // Drifting decimal output conversion /* Returns 1 if conversion was successful, 0 otherwise. Also sets: Cvnchars (public): Number of characters output - amfw (static SI): _mfw adjusted for sign, used by caller re field overflow handling */ + amfw (static SI): _mfw adjusted for sign, used by caller re field + overflow handling */ { -#ifdef FMTPVMASK // define in cvpak.h to restore p positive value display options, 11-91 -p -p // These format definitions must match definitions of FMTPVxxx in cvpak.h -p // null + ' ' if positive -p static char *ff[4][3] = { "%-*.*f", "%-+*.*f", "%- *.*f", // left just -p "%*.*f", "%+*.*f", "% *.*f", // rt -p "%0*.*f", "%0+*.*f", "%0 *.*f", // rt zeroes -p "%*.*f", "%+*.*f", "% *.*f" -p }; // squeeze +#ifdef FMTPVMASK // define in cvpak.h to restore p positive value display + // options, 11-91 + p p // These format definitions must match definitions of FMTPVxxx in cvpak.h + p // null + ' ' if positive + p static char *ff[4][3] = { + "%-*.*f", "%-+*.*f", "%- *.*f", // left just + p "%*.*f", "%+*.*f", "% *.*f", // rt + p "%0*.*f", "%0+*.*f", "%0 *.*f", // rt zeroes + p "%*.*f", "%+*.*f", "% *.*f" p}; // squeeze #else - static char *ff[4] = { "%-*.*f", // left just - "%*.*f", // rt - "%0*.*f", // rt zeroes - "%*.*f", - }; // squeeze + static char *ff[4] = { + "%-*.*f", // left just + "%*.*f", // rt + "%0*.*f", // rt zeroes + "%*.*f", + }; // squeeze #endif - aval = fabs(val); // absolute val - amfw = _mfw - wsign; // width less sign. wsign is !(val >= 0. [&& pv==FMTPVNULL]) - -// compute precision (digits after point) to fit field, in range 0 to (possibly increased) _dfw - - int _nDigB4Pt = 0; // local - while (_nDigB4Pt < PTENSIZE && Pten[_nDigB4Pt] <= aval) // Pten: powers of 10, [0]..[15]. cons.c. - _nDigB4Pt++; // digits needed for magnitude left of . - int prcsn = amfw - _nDigB4Pt - 1; // precision: space avail for digits after . - if (prcsn <= 0) - prcsn = 0; - else // prcsn > 0: room for 1 or more digits after point - { - /* attempt to maintain dfw significant digits for numbers smaller than .1: conditionally add a few digits after point, 1-92. - goals: show more of small numbers in typical report colums (dfw 1-3), but not as much as for large numbers. - numbers too small for significance to show: don't crowd page too much with 0.0000's. - don't add lots of digits to small export numbers (FMTRTZ gets here when < .001, dfw 6 already so) */ - - if (_dfw > 0) /* don't go into decimal places if none requested (take dfw==0 as 0 sig dig here) - (??? or, do "if (!_dfw) _dfw++;" at foot of loop to get a sig dig to show) */ - { - int maxDfw = min( prcsn, _dfw + 3); // don't exceed space avail; don't add > 3 places here (shd that be 2?) - maxDfw = min( maxDfw, 7); // increase only to 7 dec places: avoid garbage detail; don't exceed NPten[]. - if (aval < NPten[maxDfw] * .5) // if only 0's would show anyway even after round, don't crowd: - maxDfw = min( maxDfw - 1, /* add 1 less digit here (re 7 or +3 limits), and */ - prcsn + wsign - 2); // leave space for sign even if > 0, here only. CAUTION: may make maxDfw < 0. - - for (int n0afPt = 1; _dfw < maxDfw; _dfw++, n0afPt++) // increase _dfw til dfw sig digits show or to maxDfw limit - { - // test if enuf significant digits will (now) show - if (aval >= NPten[n0afPt]) // if aval > .1 at entry, > .01 next iteration, then .001, .0001, etc. - break; // stop if have added a digit for each leading 0 after point - - /*if (!_dfw) _dfw++ if get here with 0 _dfw, do extra ++ for 1 sig dig. - else dfw==0 means "0 sig dig" and only leading 0's after . wd show. */ - } - } - if (prcsn > _dfw) - prcsn = _dfw; // no more than _dfw digits after point - } - -// convert / retry if too wide - - while (1) - { + aval = fabs(val); // absolute val + amfw = _mfw - + wsign; // width less sign. wsign is !(val >= 0. [&& pv==FMTPVNULL]) + + // compute precision (digits after point) to fit field, in range 0 to + // (possibly increased) _dfw + + int _nDigB4Pt = 0; // local + while (_nDigB4Pt < PTENSIZE && + Pten[_nDigB4Pt] <= aval) // Pten: powers of 10, [0]..[15]. cons.c. + _nDigB4Pt++; // digits needed for magnitude left of . + int prcsn = amfw - _nDigB4Pt - 1; // precision: space avail for digits after . + if (prcsn <= 0) + prcsn = 0; + else // prcsn > 0: room for 1 or more digits after point + { + /* attempt to maintain dfw significant digits for numbers smaller than .1: + conditionally add a few digits after point, 1-92. goals: show more of + small numbers in typical report colums (dfw 1-3), but not as much as for + large numbers. numbers too small for significance to show: don't crowd + page too much with 0.0000's. don't add lots of digits to small export + numbers (FMTRTZ gets here when < .001, dfw 6 already so) */ + + if (_dfw > + 0) /* don't go into decimal places if none requested (take dfw==0 as 0 +sig dig here) +(??? or, do "if (!_dfw) _dfw++;" at foot of loop to get a sig dig to show) */ + { + int maxDfw = min(prcsn, _dfw + 3); // don't exceed space avail; don't add + // > 3 places here (shd that be 2?) + maxDfw = min(maxDfw, 7); // increase only to 7 dec places: avoid garbage + // detail; don't exceed NPten[]. + if (aval < NPten[maxDfw] * .5) // if only 0's would show anyway even after + // round, don't crowd: + maxDfw = + min(maxDfw - 1, /* add 1 less digit here (re 7 or +3 limits), and */ + prcsn + wsign - 2); // leave space for sign even if > 0, here + // only. CAUTION: may make maxDfw < 0. + + for (int n0afPt = 1; _dfw < maxDfw; + _dfw++, n0afPt++) // increase _dfw til dfw sig digits show or to + // maxDfw limit + { + // test if enuf significant digits will (now) show + if (aval >= NPten[n0afPt]) // if aval > .1 at entry, > .01 next + // iteration, then .001, .0001, etc. + break; // stop if have added a digit for each leading 0 after point + + /*if (!_dfw) _dfw++ if get here with 0 _dfw, do + extra ++ for 1 sig dig. else dfw==0 means "0 sig dig" and only + leading 0's after . wd show. */ + } + } + if (prcsn > _dfw) + prcsn = _dfw; // no more than _dfw digits after point + } + + // convert / retry if too wide + + while (1) { #ifdef FMTPVMASK -p Cvnchars = sprintf( str, ff[ijust][ipv], wid, prcsn, val); // convert + p Cvnchars = sprintf(str, ff[ijust][ipv], wid, prcsn, val); // convert #else - Cvnchars = sprintf( str, ff[ijust], wid, prcsn, val); // convert (C library) + Cvnchars = sprintf(str, ff[ijust], wid, prcsn, val); // convert (C library) #endif - // test if fits (allowing positive # to overflow into - position) - if (Cvnchars <= _mfw) - return 1; // return if fits _mfw - // too wide. If number < 1.0, sprintf output "0.xxx". ndig did not allow for 0 b4 point. Remove it. - char* zp; - if (_nDigB4Pt==0 && *(zp=(str+Cvnchars-prcsn-2))=='0') // if of format 0.xxx - { - strcpy( zp, zp+1); // remove leading 0 from 0.xxx. - Cvnchars--; - break; // prcsn already as small as possible here, right? - } - // too wide... reduce digits after point (perhaps sprintf rounded up). - if (--prcsn < 0) - break; // done if was already at 0 digits after . - } - return (Cvnchars <= _mfw); // 1 if now fits -} // cvdd + // test if fits (allowing positive # to overflow into - position) + if (Cvnchars <= _mfw) + return 1; // return if fits _mfw + // too wide. If number < 1.0, sprintf output "0.xxx". ndig did not allow + // for 0 b4 point. Remove it. + char *zp; + if (_nDigB4Pt == 0 && + *(zp = (str + Cvnchars - prcsn - 2)) == '0') // if of format 0.xxx + { + strcpy(zp, zp + 1); // remove leading 0 from 0.xxx. + Cvnchars--; + break; // prcsn already as small as possible here, right? + } + // too wide... reduce digits after point (perhaps sprintf rounded up). + if (--prcsn < 0) + break; // done if was already at 0 digits after . + } + return (Cvnchars <= _mfw); // 1 if now fits +} // cvdd //====================================================================== -LOCAL SI FC cvttz( char *_str, SI trailChar, SI minWid) +LOCAL SI FC cvttz(char *_str, SI trailChar, SI minWid) // trim trailing 0's after point, and point, but not shorter than minWid // returns new length -// called from cvin2s to trim decimal inches 10-88; might be useful re other floats types if reimplementing. +// called from cvin2s to trim decimal inches 10-88; might be useful re other +// floats types if reimplementing. { - char *p, *pend, *q; - SI len; - - len = (SI)strlen(_str); - if (len <= minWid) - return len; // done if not shortenable - p = _str + len; // point end - while (p > _str && *(p-1)==' ') // (actual input probably has no .. - p--; // trailSpaces, could shorten code) - if (p > _str && *(p-1)==(char)trailChar) // if given " etc is at end - { - p--; - minWid--; // trim b4 it, then restore - } - else - trailChar = 0; // say no char to put back at end - while (p > _str && *(p-1)==' ') // (actual input probably has no .. - p--; // trailSpaces, could shorten code) - pend = p; // 1st trailing space (else null): truncate to here even if no .000 - while (p > _str && *(p-1)=='0') - p--; // back over trail zeroes - // can only truncate 0's if after point - if (p > _str) - { - if (*(p-1)=='.') // if point precedes first trail0 - pend = p-1; // chop zeroes and also the point - else // other digits between . and 0s ok - { - for (q = p-1; q > _str && isdigitW(*q); q--) - ; // back over digits b4 0's - if (*q == '.') // if point b4 the digits - pend = p; // chop the 0's, not other digits - } - } - if (pend < _str + minWid) // don't make < minWid - pend = _str + minWid; // nb minWid checked for < len above - if (trailChar) - *pend++ = (char)trailChar; // move " or other ending thing - *pend = (char)0; // truncate - return (SI)strlen(_str); // new length. another return above. -} // cvttz + char *p, *pend, *q; + SI len; + + len = (SI)strlen(_str); + if (len <= minWid) + return len; // done if not shortenable + p = _str + len; // point end + while (p > _str && *(p - 1) == ' ') // (actual input probably has no .. + p--; // trailSpaces, could shorten code) + if (p > _str && *(p - 1) == (char)trailChar) // if given " etc is at end + { + p--; + minWid--; // trim b4 it, then restore + } else + trailChar = 0; // say no char to put back at end + while (p > _str && *(p - 1) == ' ') // (actual input probably has no .. + p--; // trailSpaces, could shorten code) + pend = p; // 1st trailing space (else null): truncate to here even if no .000 + while (p > _str && *(p - 1) == '0') + p--; // back over trail zeroes + // can only truncate 0's if after point + if (p > _str) { + if (*(p - 1) == '.') // if point precedes first trail0 + pend = p - 1; // chop zeroes and also the point + else // other digits between . and 0s ok + { + for (q = p - 1; q > _str && isdigitW(*q); q--) + ; // back over digits b4 0's + if (*q == '.') // if point b4 the digits + pend = p; // chop the 0's, not other digits + } + } + if (pend < _str + minWid) // don't make < minWid + pend = _str + minWid; // nb minWid checked for < len above + if (trailChar) + *pend++ = (char)trailChar; // move " or other ending thing + *pend = (char)0; // truncate + return (SI)strlen(_str); // new length. another return above. +} // cvttz //---------------------------------------------------------------------- -template< typename T> static RC LimitCheck(T v, int limit) -{ - RC rc = RCOK; - switch (limit) - { - case LMLEZ: - if (v > 0) rc = MH_V0022; - break; - case LMLZ: - if (v >= 0) rc = MH_V0026; - break; - case LMNZ: - if (v == 0) rc = MH_V0025; - break; - case LMGZ: - if (v <= 0) rc = MH_V0024; - break; - case LMGEZ: - if (v < 0) rc = MH_V0023; - break; - case LMG1: - if (v <= T(1)) rc = MH_V0028; - break; - case LMGE1: - if (v < T(1)) rc = MH_V0027; - break; - case LMFR: - if (v < 0 || v > T(1)) rc = MH_V0031; - break; - case LMFGZ: - if (v <= 0 || v > T(1)) rc = MH_V0032; - break; - case LMDOY: - if (v < T(1) || v > T(365)) rc = MH_V0034; - break; - - default: - err(PWRN, "LimitCheck: missing case for limit %d", limit); - } - return rc; - -} // LimitCheck +template static RC LimitCheck(T v, int limit) { + RC rc = RCOK; + switch (limit) { + case LMLEZ: + if (v > 0) + rc = MH_V0022; + break; + case LMLZ: + if (v >= 0) + rc = MH_V0026; + break; + case LMNZ: + if (v == 0) + rc = MH_V0025; + break; + case LMGZ: + if (v <= 0) + rc = MH_V0024; + break; + case LMGEZ: + if (v < 0) + rc = MH_V0023; + break; + case LMG1: + if (v <= T(1)) + rc = MH_V0028; + break; + case LMGE1: + if (v < T(1)) + rc = MH_V0027; + break; + case LMFR: + if (v < 0 || v > T(1)) + rc = MH_V0031; + break; + case LMFGZ: + if (v <= 0 || v > T(1)) + rc = MH_V0032; + break; + case LMDOY: + if (v < T(1) || v > T(365)) + rc = MH_V0034; + break; + + default: + err(PWRN, "LimitCheck: missing case for limit %d", limit); + } + return rc; + +} // LimitCheck //====================================================================== -RC FC cvLmCk( // Check input data for being within limits for data and limit type +RC + FC + cvLmCk( // Check input data for being within limits for data and limit type - SI dt, // Data type (dtypes.h define, from dtypes.def by rcdef.exe) - SI limit, // Limit type (dtlims.h define, from limits.def by rcdef.exe) - void *p ) // pointer to data to check (points to ptr for string types) + SI dt, // Data type (dtypes.h define, from dtypes.def by rcdef.exe) + SI limit, // Limit type (dtlims.h define, from limits.def by rcdef.exe) + void *p) // pointer to data to check (points to ptr for string types) // returns RCOK if ok, or message code specifying the error. { - RC rc = RCOK; - - if (limit == LMNONE) - return rc; - - switch (dt) - { - { int iV; - - case DTDOY: - iV = *(DOY*)p; - goto iVCheck; - case DTSI: - iV = *(SI*)p; - goto iVCheck; - case DTINT: - iV = *(INT*)p; - iVCheck: - rc = LimitCheck(iV, limit); - break; - } - - { UINT uiV; - - case DTUSI: - uiV = *(USI*)p; - goto uiVCheck; - case DTUINT: - uiV = *(UINT*)p; - uiVCheck: - rc = LimitCheck(uiV, limit); - break; - } - - { double dV; - - case DTFLOAT: - dV = *(FLOAT*)p; - goto dvCheck; - case DTDBL: - dV = *(DBL*)p; - dvCheck: - rc = LimitCheck(dV, limit); - break; - } - - // case DTLI: // DTLI not supported as input data - default: - err(PWRN, "cvLmCk: unsupported DT=%d", dt); - } - - return rc; -} // cvLmCk + RC rc = RCOK; + + if (limit == LMNONE) + return rc; + + switch (dt) { + { + int iV; + + case DTDOY: + iV = *(DOY *)p; + goto iVCheck; + case DTSI: + iV = *(SI *)p; + goto iVCheck; + case DTINT: + iV = *(INT *)p; + iVCheck: + rc = LimitCheck(iV, limit); + break; + } + + { + UINT uiV; + + case DTUSI: + uiV = *(USI *)p; + goto uiVCheck; + case DTUINT: + uiV = *(UINT *)p; + uiVCheck: + rc = LimitCheck(uiV, limit); + break; + } + + { + double dV; + + case DTFLOAT: + dV = *(FLOAT *)p; + goto dvCheck; + case DTDBL: + dV = *(DBL *)p; + dvCheck: + rc = LimitCheck(dV, limit); + break; + } + + // case DTLI: // DTLI not supported as input data + default: + err(PWRN, "cvLmCk: unsupported DT=%d", dt); + } + + return rc; +} // cvLmCk //====================================================================== -double FC cvExtoIn( // Convert value from external units to internal +double FC cvExtoIn( // Convert value from external units to internal - double f, // value to be converted - int units ) // unit type: UNxxx defines from units.h generated by rcdef.exe from data\units.def + double f, // value to be converted + int units) // unit type: UNxxx defines from units.h generated by rcdef.exe + // from data\units.def // call only for DTFLOAT, DTDBL, or DTPERCENT value // Returns converted value { - if (units != UNNONE) - { - f /= UNIT::GetFact( units); // divide by units' scale factor (below) - if (units == UNTEMP && Unsysext == UNSYSSI) // if temperature - f += 32.0; // complete C to F adjusment -- ONLY units where 0 point changes - } - return f; -} // cvExtoIn + if (units != UNNONE) { + f /= UNIT::GetFact(units); // divide by units' scale factor (below) + if (units == UNTEMP && Unsysext == UNSYSSI) // if temperature + f += + 32.0; // complete C to F adjusment -- ONLY units where 0 point changes + } + return f; +} // cvExtoIn //====================================================================== -double FC cvIntoEx( // Convert value from internal units to external +double FC cvIntoEx( // Convert value from internal units to external - double f, // value to be converted - int units ) // unit type + double f, // value to be converted + int units) // unit type // call only for DTFLOAT, DTDBL, (DTPERCENT), // returns converted value { - if (units != UNNONE) // if it has units - { - if (units == UNANGLE) // if angle - f = cvstdangle(f); // normalize to 0..2*Pi - f *= UNIT::GetFact( units); // get factor (below), conv to extnl - if (units == UNTEMP // if temperature - && Unsysext == UNSYSSI) // and metric - f -= 17.77777778; // adjust zero point - } - return f; -} // cvIntoEx + if (units != UNNONE) // if it has units + { + if (units == UNANGLE) // if angle + f = cvstdangle(f); // normalize to 0..2*Pi + f *= UNIT::GetFact(units); // get factor (below), conv to extnl + if (units == UNTEMP // if temperature + && Unsysext == UNSYSSI) // and metric + f -= 17.77777778; // adjust zero point + } + return f; +} // cvIntoEx //====================================================================== -double FC cvstdangle( // Normalize an angle into 0 to 2*PI range +double FC cvstdangle( // Normalize an angle into 0 to 2*PI range - double ang ) // Input angle to be normalized (radians) + double ang) // Input angle to be normalized (radians) -/* DO NOT use in high precision situations without consideration, cuz if ang > 2PI-.001, it is changed to 0. - Other such "snaps" may be added in the future. */ +/* DO NOT use in high precision situations without consideration, cuz if ang > + 2PI-.001, it is changed to 0. Other such "snaps" may be added in the future. + */ // Returns equivalent angle that falls in range 0 - 2*PI { - if (ang >= k2Pi) // if 2*Pi or greater - return fmod( ang, k2Pi ); // take mod 2 * Pi - - /* If negative, want to be independent of what fmod does if < 0. - Values 0. <= ang < k2Pi fall thru and are returned */ - while (ang < 0.0) // until positive - ang += k2Pi; // add 2 * Pi - if (ang > (k2Pi - .001)) - ang = 0.; // If angle is close to 2PI, change it to 0 so it won't display as 360. - return ang; -} // cvstdangle + if (ang >= k2Pi) // if 2*Pi or greater + return fmod(ang, k2Pi); // take mod 2 * Pi + + /* If negative, want to be independent of what fmod does if < 0. + Values 0. <= ang < k2Pi fall thru and are returned */ + while (ang < 0.0) // until positive + ang += k2Pi; // add 2 * Pi + if (ang > (k2Pi - .001)) + ang = 0.; // If angle is close to 2PI, change it to 0 so it won't display as + // 360. + return ang; +} // cvstdangle // made local and renamed from cvftinch, 2-91: //============================================================================ -LOCAL int FC sepFtInch( // Break up length into feet and inches +LOCAL int FC sepFtInch( // Break up length into feet and inches - double d, // Length to be broken up (float or double feet) - int& inch ) // return: inches + double d, // Length to be broken up (float or double feet) + int &inch) // return: inches // Returns integral feet as fcn value, plus integral inches via *inp. -// If d < 0, feet are returned negative; inches are returned positive unless feet are 0. -// Since d is converted to int inches, the largest distance which can be converted is around 178,956,970 ft -// (about 33,893 miles; I know you're disappointed). +// If d < 0, feet are returned negative; inches are returned positive unless +// feet are 0. Since d is converted to int inches, the largest distance which +// can be converted is around 178,956,970 ft (about 33,893 miles; I know you're +// disappointed). { - int nd = d < 0.0 ? -12 : 12; - int totin = (int)( d*(double)nd + 0.5 ); // total inches, pos, rounded - int ft = totin/nd; // feet, signed - inch = (ft==0 && d < 0.) ? -totin : totin%12; // inches - return ft; -} // sepFtInch + int nd = d < 0.0 ? -12 : 12; + int totin = (int)(d * (double)nd + 0.5); // total inches, pos, rounded + int ft = totin / nd; // feet, signed + inch = (ft == 0 && d < 0.) ? -totin : totin % 12; // inches + return ft; +} // sepFtInch //====================================================================== -int getChoiTxTyX( const char* chtx) // categorize choice text +int getChoiTxTyX(const char *chtx) // categorize choice text { - int tyX = *chtx == '*' ? chtyHIDDEN // hidden - : *chtx == '!' ? chtyALIAS // alias - : *chtx == '~' ? chtyALIASDEP // deprecated alias - : chtyNORMAL; - return tyX; + int tyX = *chtx == '*' ? chtyHIDDEN // hidden + : *chtx == '!' ? chtyALIAS // alias + : *chtx == '~' ? chtyALIASDEP // deprecated alias + : chtyNORMAL; + return tyX; } //====================================================================== -const char* getChoiTxI( // text for choice of choice data type - USI dt, // data type (for bits and choicb Dttab index) - SI chan, // choicb index (use CHN(float), cnglob.h, to extract hi word of choicn) - // (1 based) - int* pTyX/*=NULL*/, // NULL or receives choice type - // chtyNORMAL, chtyHIDDEN - int options /*=0*/) // option bits - // 1: nz = get entire string (including any aliases) - // 0 = get primary (1st) text only +const char *getChoiTxI( // text for choice of choice data type + USI dt, // data type (for bits and choicb Dttab index) + SI chan, // choicb index (use CHN(float), cnglob.h, to extract hi word of + // choicn) + // (1 based) + int *pTyX /*=NULL*/, // NULL or receives choice type + // chtyNORMAL, chtyHIDDEN + int options /*=0*/) // option bits + // 1: nz = get entire string (including any aliases) + // 0 = get primary (1st) text only // note: return value may be TmpStr { - if (dt & (DTBCHOICB|DTBCHOICN)) // verify that is a choice type -- debug aid - { - // check for out of range or garbage value -- debug aid - if (dt & DTBCHOICN) // for choicn, chan may be hi word of float, including bits to make a NAN - if ((chan & 0xff80)==NCNAN) // if proper NAN bits (NCNAN: 7f80, cnglob.h), not just garbage - chan &= ~NCNAN; // remove them for check. But leave improper bits to evoke error msg. - if (chan <= 0 || chan > GetDttab(dt).nchoices) // check that choice is in range 1 to # choices for dt. GetDttab: srd.h. - { - err( PWRN, MH_V0036, chan, dt ); // display program error err msg - // "cvpak:getChoiTx(): choice %d out of range for dt 0x%x" - return "bad choice"; - } - - // access text - const char* chtx = GetChoiceText( dt, chan); // access text for fcn. srd.h fcn. - - // return info - if (!(options&1)) - { // retrieve 1st piece to TmpStr - const char* p = chtx; - chtx = strxtok( NULL, p, "|", true); - } - int tyX = getChoiTxTyX( chtx); - if (tyX > chtyNORMAL) - chtx++; // drop prefix - if (pTyX) - *pTyX = tyX; - return chtx; - } - err( PWRN, MH_V0037, dt ); // program (internal) err msg - // "cvpak:getChoiTx(): given data type 0x%x not a choice type" - return "bad dt"; -} // getChoiTxI + if (dt & (DTBCHOICB | DTBCHOICN)) // verify that is a choice type -- debug aid + { + // check for out of range or garbage value -- debug aid + if (dt & DTBCHOICN) // for choicn, chan may be hi word of float, including + // bits to make a NAN + if ((chan & 0xff80) == + NCNAN) // if proper NAN bits (NCNAN: 7f80, cnglob.h), not just garbage + chan &= ~NCNAN; // remove them for check. But leave improper bits to + // evoke error msg. + if (chan <= 0 || + chan > GetDttab(dt).nchoices) // check that choice is in range 1 to # + // choices for dt. GetDttab: srd.h. + { + err(PWRN, MH_V0036, chan, dt); // display program error err msg + // "cvpak:getChoiTx(): choice %d out of range for dt 0x%x" + return "bad choice"; + } + + // access text + const char *chtx = + GetChoiceText(dt, chan); // access text for fcn. srd.h fcn. + + // return info + if (!(options & 1)) { // retrieve 1st piece to TmpStr + const char *p = chtx; + chtx = strxtok(NULL, p, "|", true); + } + int tyX = getChoiTxTyX(chtx); + if (tyX > chtyNORMAL) + chtx++; // drop prefix + if (pTyX) + *pTyX = tyX; + return chtx; + } + err(PWRN, MH_V0037, dt); // program (internal) err msg + // "cvpak:getChoiTx(): given data type 0x%x not a choice type" + return "bad dt"; +} // getChoiTxI //====================================================================== -RC FC cvS2Choi( // convert string to choice value for given data type else format (do not display) error msg - const char *s, // string to convert - USI dt, // choicb or choicn data type (dtypes.h) to convert: specifies choice strings in Dttab[]. - void* pv, // NULL or receives choice value: 2 bytes for choicb, 4 bytes (hi 2 significant) for choicn. - USI* pSz, // NULL or receives size: 2 or 4 - MSGORHANDLE* pms ) // if non-NULL - // string not found: receives ptr to Tmpstr message insert: "%s not one of xxx yyy zzz ..." - // string=alias: receives ptr to deprecation Tmpstr message - // else receives NULL +RC FC cvS2Choi( // convert string to choice value for given data type else + // format (do not display) error msg + const char *s, // string to convert + USI dt, // choicb or choicn data type (dtypes.h) to convert: specifies + // choice strings in Dttab[]. + void *pv, // NULL or receives choice value: 2 bytes for choicb, 4 bytes (hi + // 2 significant) for choicn. + USI *pSz, // NULL or receives size: 2 or 4 + MSGORHANDLE + *pms) // if non-NULL + // string not found: receives ptr to Tmpstr message insert: "%s + // not one of xxx yyy zzz ..." string=alias: receives ptr to + // deprecation Tmpstr message else receives NULL // returns RCOK if ok // RCBAD with *pms set on error // RCBAD2 with *pms set on info/warning (use of alias,) { - if (pms) - *pms = nullptr; // init to no message - if (dt & (DTBCHOICB|DTBCHOICN)) // if a choice type - { - // search this choice data type's strings for a match, using getChoiTxI (just above). - int v; - for (v = 1; v <= GetDttab(dt).nchoices; v++) // loop data type's choices (GetDttab: srd.h) - { int tyX; - const char* chtx = getChoiTxI( dt, v, &tyX, 1); - if (tyX == chtyHIDDEN) - continue; // hidden, cannot match - - const char* p = chtx; - while (1) - { chtx = strxtok( NULL, p, "|", true); - if (!chtx) - break; - tyX = getChoiTxTyX( chtx); - if (tyX > chtyNORMAL) - chtx++; // drop prefix if any - if (_stricmp( s, chtx)) - continue; - if (dt & DTBCHOICN) // for choice in number-choice store bit pattern of - { if (pv) - *reinterpret_cast(pv) = NCHOICE(v | NCNAN); - // .. NCNAN (7f80, cnglob.h) + choice 1,2,3.. in hi word of float. - if (pSz) - *pSz = sizeof(float); // tell caller value size is 4 bytes - } - else // plain choice (cannot also hold number) - { if (pv) - *(SI *)pv = v; // store choicb value: integer 1,2,3... - if (pSz) - *pSz = sizeof( SI); // size is 2 bytes - } - if (tyX == chtyALIASDEP) // if deprecated - { if (pms) - { const char* ms = strtprintf( "Deprecated '%s' converted to '%s'", - chtx, getChoiTxI( dt, v)); - *pms = ms; - } - return RCBAD2; // warning return - } - return RCOK; // good return - } - } - // not found. generate error message insert showing given string and choices - // list of choices does not include aliases 8-12 - if (pms) // if message return pointer given - { - USI maxll = getCpl() - 5; - // -5: leave some space for adding punctuation, indent, final " ...", etc. - const char* ms = strtprintf( MH_V0039, s); // start assembling string "'%s' is not one of choice1 choice2 ..." - for (v = 1; v <= GetDttab(dt).nchoices; v++) // loop data type's choices, concatenate each to ms - { int tyX; - const char* chtx = getChoiTxI( dt, v, &tyX ); // get vth choice - if (tyX != chtyHIDDEN) // if not hidden - { if (strlen(ms) + strlen(chtx) > 200) // if there's an unexpectedly large # long choices - { - ms = strtcat( ms, " ...", NULL); // limit amount shown - break; - } - const char* sep - = strJoinLen( ms, chtx) // length of line if joined, strpak.c - > maxll ? "\n " : " "; // separator: newline if needed - ms = strtcat( ms, sep, chtx, NULL ); // concatenate to Tmpstr, strpak.c - } - } - *pms = ms; // return pointer to message insert text to caller - } - return RCBAD; // bad value for data type return - } - if (pms) - *pms = strtprintf( MH_V0038, dt ); // "cvpak:cvS2Choi(): given data type 0x%x not a choice type" - return RCBAD; // bad data type. 2+ other returns above -} // cvS2Choi + if (pms) + *pms = nullptr; // init to no message + if (dt & (DTBCHOICB | DTBCHOICN)) // if a choice type + { + // search this choice data type's strings for a match, using getChoiTxI + // (just above). + int v; + for (v = 1; v <= GetDttab(dt).nchoices; + v++) // loop data type's choices (GetDttab: srd.h) + { + int tyX; + const char *chtx = getChoiTxI(dt, v, &tyX, 1); + if (tyX == chtyHIDDEN) + continue; // hidden, cannot match + + const char *p = chtx; + while (1) { + chtx = strxtok(NULL, p, "|", true); + if (!chtx) + break; + tyX = getChoiTxTyX(chtx); + if (tyX > chtyNORMAL) + chtx++; // drop prefix if any + if (_stricmp(s, chtx)) + continue; + if (dt & DTBCHOICN) // for choice in number-choice store bit pattern of + { + if (pv) + *reinterpret_cast(pv) = NCHOICE(v | NCNAN); + // .. NCNAN (7f80, cnglob.h) + choice 1,2,3.. in hi word of float. + if (pSz) + *pSz = sizeof(float); // tell caller value size is 4 bytes + } else // plain choice (cannot also hold number) + { + if (pv) + *(SI *)pv = v; // store choicb value: integer 1,2,3... + if (pSz) + *pSz = sizeof(SI); // size is 2 bytes + } + if (tyX == chtyALIASDEP) // if deprecated + { + if (pms) { + const char *ms = strtprintf("Deprecated '%s' converted to '%s'", + chtx, getChoiTxI(dt, v)); + *pms = ms; + } + return RCBAD2; // warning return + } + return RCOK; // good return + } + } + // not found. generate error message insert showing given string and + // choices + // list of choices does not include aliases 8-12 + if (pms) // if message return pointer given + { + USI maxll = getCpl() - 5; + // -5: leave some space for adding punctuation, indent, final " ...", etc. + const char *ms = + strtprintf(MH_V0039, s); // start assembling string "'%s' is not one + // of choice1 choice2 ..." + for (v = 1; v <= GetDttab(dt).nchoices; + v++) // loop data type's choices, concatenate each to ms + { + int tyX; + const char *chtx = getChoiTxI(dt, v, &tyX); // get vth choice + if (tyX != chtyHIDDEN) // if not hidden + { + if (strlen(ms) + strlen(chtx) > + 200) // if there's an unexpectedly large # long choices + { + ms = strtcat(ms, " ...", NULL); // limit amount shown + break; + } + const char *sep = + strJoinLen(ms, chtx) // length of line if joined, strpak.c + > maxll + ? "\n " + : " "; // separator: newline if needed + ms = strtcat(ms, sep, chtx, NULL); // concatenate to Tmpstr, strpak.c + } + } + *pms = ms; // return pointer to message insert text to caller + } + return RCBAD; // bad value for data type return + } + if (pms) + *pms = strtprintf( + MH_V0038, + dt); // "cvpak:cvS2Choi(): given data type 0x%x not a choice type" + return RCBAD; // bad data type. 2+ other returns above +} // cvS2Choi /*=============================== TEST CODE ================================*/ /*==========================================================================*/ /* UNMAINTAINED Test code for output format */ /* #define TEST */ -#ifdef TEST /* #endif is about 250 lines down */ -t -t #include /* test input routines, in lib\ but not in library.*/ -t -t static float testf = 123.345; -t -t main () -t { -t char buf[200],buf2[200],dbuf[10]; -t #define NV 11 -t static SI vallist[NV] = {-10000,-1000,-100,-10,-1,0,1,10,100,1000,10000}; -t #define NFV 12 -t static float fvlist[NFV] = -t { -13945789,-999.99,-100.,-99.99,-1.,0.,.006,.999,1.0,9.999,10., -t 978437852312. }; -t static SI fmts[] = { FMTSQ, FMTLJ, FMTRJ, FMTLZ }; -t static char *fmtnames[] = {"sq","lj","rj","lz"}; -t SI iv, m; -t float val; -t double d; -t USI i; -t SI dt, mfw, dfw, f, ifx; -t LI ft; -t SI in; -t char *p; -t IDATE tdate; -t ITIME ttime; -t IDATETIME tdatetime; -t RC rc; -t SEC sec; -t SI un, lm; -t CHOICE *ch; -t val = 2.5; -t f = FMTRJ+2; -t while (1) -t { cvin2sBuf(buf,(char *)&val,DTFLOAT,UNLENGTH,10,f); -t } -t -t /* #define PERCENTTEST */ -t #ifdef PERCENTTEST -t dt = DTPERCENT; -t val = .30; -t cvin2sBuf(buf,(char *)&val,dt,UNNONE,7,FMTSQ+2); -t printf("\n'%s'",buf); -t cvin2sBuf(buf,(char *)&val,dt,UNNONE,10,FMTLJ+1); -t printf("\n'%s'",buf); -t cvin2sBuf(buf,(char *)&val,dt,UNNONE,10,FMTRJ+1); -t printf("\n'%s'",buf); -t cvin2sBuf(buf,(char *)&val,dt,UNNONE,1,FMTRJ+1); -t printf("\n'%s'",buf); -t #endif /* PERCENTTEST */ -t -t /* #define CHOICETEST */ -t #ifdef CHOICETEST -t dt = DTYESNO; -t un = UNNONE; -t lm = LMNONE; -t dfw = 0; -t Cp4snake = dmalloc(50,ABT); -t Dttab = (SI *) dmalloc(100,ABT); -t ch = DTCHOICEP(DTYESNO); -t ch->size = 2; -t ch->nchoices = 2; -t ch->choices[0] = C_YESNO_YES; -t ch->choices[1] = C_YESNO_NO; -t strcpy( rcHan2Tx( C_YESNO_YES), "Yes"); -t strcpy( rcHan2Tx( C_YESNO_NO), "No"); -t #endif /* CHOICETEST */ -t -t /* #define INTEST */ -t #ifdef INTEST -t dt = DTPERCENT; -t dfw = 2; -t while (strlen(getstr(">>> ",buf2)) > 0) -t { *(long *)dbuf = 0L; -t rc = cvs2in( buf2, dt, un, lm, dbuf, WRN); -t if (dbuf != Pgb) -t ??? -t cvin2sBuf(buf,Pgb,dt,un,7,FMTSQ+dfw); -t if (rc == RCOK) -t printf("Cvnchars=%d Gbsize=%d %s\n",Cvnchars,Gbsize,buf); -t } -t #endif /* INTEST */ -t -t -t /* #define TESTINT */ -t #ifdef TESTINT -t f = FMTLZ; -t dt = DTUSI; -t mfw = 6; -t for (iv = 0; iv < NV; iv++) -t { i = vallist[iv]; -t cvin2sBuf(buf,(char *)&i,dt,UNNONE,mfw,f); -t printf("Plus ... [%s] %d\n",buf,Cvnchars); -t /* +#ifdef TEST /* #endif is about 250 lines down */ +t t #include /* test input routines, in lib\ but not in library.*/ + t t static float testf = 123.345; +t t main() t { + t char buf[200], buf2[200], dbuf[10]; + t #define NV 11 t static SI vallist[NV] = {-10000, -1000, -100, -10, -1, 0, + 1, 10, 100, 1000, 10000}; + t #define NFV 12 t static float fvlist[NFV] = + t{-13945789, -999.99, -100., -99.99, -1., 0., + .006, .999, 1.0, 9.999, 10., t 978437852312.}; + t static SI fmts[] = {FMTSQ, FMTLJ, FMTRJ, FMTLZ}; + t static char *fmtnames[] = {"sq", "lj", "rj", "lz"}; + t SI iv, m; + t float val; + t double d; + t USI i; + t SI dt, mfw, dfw, f, ifx; + t LI ft; + t SI in; + t char *p; + t IDATE tdate; + t ITIME ttime; + t IDATETIME tdatetime; + t RC rc; + t SEC sec; + t SI un, lm; + t CHOICE *ch; + t val = 2.5; + t f = FMTRJ + 2; + t while (1) t { + cvin2sBuf(buf, (char *)&val, DTFLOAT, UNLENGTH, 10, f); + t + } + t t /* #define PERCENTTEST */ + t #ifdef PERCENTTEST t dt = DTPERCENT; + t val = .30; + t cvin2sBuf(buf, (char *)&val, dt, UNNONE, 7, FMTSQ + 2); + t printf("\n'%s'", buf); + t cvin2sBuf(buf, (char *)&val, dt, UNNONE, 10, FMTLJ + 1); + t printf("\n'%s'", buf); + t cvin2sBuf(buf, (char *)&val, dt, UNNONE, 10, FMTRJ + 1); + t printf("\n'%s'", buf); + t cvin2sBuf(buf, (char *)&val, dt, UNNONE, 1, FMTRJ + 1); + t printf("\n'%s'", buf); + t #endif /* PERCENTTEST */ + t t /* #define CHOICETEST */ + t #ifdef CHOICETEST t dt = DTYESNO; + t un = UNNONE; + t lm = LMNONE; + t dfw = 0; + t Cp4snake = dmalloc(50, ABT); + t Dttab = (SI *)dmalloc(100, ABT); + t ch = DTCHOICEP(DTYESNO); + t ch->size = 2; + t ch->nchoices = 2; + t ch->choices[0] = C_YESNO_YES; + t ch->choices[1] = C_YESNO_NO; + t strcpy(rcHan2Tx(C_YESNO_YES), "Yes"); + t strcpy(rcHan2Tx(C_YESNO_NO), "No"); + t #endif /* CHOICETEST */ + t t /* #define INTEST */ + t #ifdef INTEST t dt = DTPERCENT; + t dfw = 2; + t while (strlen(getstr(">>> ", buf2)) > 0) t { + *(long *)dbuf = 0L; + t rc = cvs2in(buf2, dt, un, lm, dbuf, WRN); + t if (dbuf != Pgb) t ? ? ? t cvin2sBuf(buf, Pgb, dt, un, 7, FMTSQ + dfw); + t if (rc == RCOK) + t printf("Cvnchars=%d Gbsize=%d %s\n", Cvnchars, Gbsize, buf); + t + } + t #endif /* INTEST */ + t t t /* #define TESTINT */ + t #ifdef TESTINT t f = FMTLZ; + t dt = DTUSI; + t mfw = 6; + t for (iv = 0; iv < NV; iv++) t { + i = vallist[iv]; + t cvin2sBuf(buf, (char *)&i, dt, UNNONE, mfw, f); + t printf("Plus ... [%s] %d\n", buf, Cvnchars); + t /* t cvin2sBuf(buf,(char *)&i,dt,UNNONE,mfw,FMTPVPLUS+f); t printf("Plus ... [%s] %d ",buf,Cvnchars); t cvin2sBuf(buf,(char *)&i,dt,UNNONE,mfw,FMTPVNULL+f); @@ -1537,136 +1702,129 @@ t printf("Null ... [%s] %d ",buf,Cvnchars); t cvin2sBuf(buf,(char *)&i,dt,UNNONE,mfw,FMTPVSPACE+f); t printf("Space ... [%s] %d\n",buf,Cvnchars); t */ -t } -t /* + t + } + t /* t i = 100; t cvin2sBuf(buf,(char *)&i,dt,UNNONE,mfw,FMTPVNULL+FMTLJ); t printf("Left just [%s] %d\n",buf,Cvnchars); t cvin2sBuf(buf,(char *)&i,dt,UNNONE,mfw,FMTPVNULL+FMTRJ); t printf("Right just [%s] %d\n",buf,Cvnchars); t */ -t #endif /* TESTINT */ -t -t #ifdef TESTVAR -t f = FMTRJ; -t mfw = VARWIDTH*Vardisp; -t cvin2sBuf(buf,(char *)vallist,DTVAR,UNNONE,mfw,FMTPVNULL+f); -t printf("[%s] %d\n",buf,Cvnchars); -t #endif /* TESTVAR */ -t -t /* #define TESTSTR */ -t #ifdef TESTSTR -t dt = DTSTRING; -t cvin2sBuf(buf,"Hello test",dt,UNNONE,7,0); -t printf("[%s] %d\n",buf,Cvnchars); -t cvin2sBuf(buf,"Hello test",dt,UNNONE,11,0); -t printf("[%s] %d\n",buf,Cvnchars); -t cvin2sBuf(buf,"Hello test",dt,UNNONE,15,0); -t printf("[%s] %d\n",buf,Cvnchars); -t cvin2sBuf(buf,"Hello test",dt,UNNONE,15,FMTRJ); -t printf("[%s] %d\n",buf,Cvnchars); -t cvin2sBuf(buf,"Hello test",dt,UNNONE,256,FMTSQ); -t printf("[%s] %d\n",buf,Cvnchars); -t #endif /* TESTSTR */ -t -t #undef TESTDATE -t #ifdef TESTDATE -t /* -t dt = DTDOY; -t mfw = 10; -t i = 1; -t p = (char *)&i; -t */ -t /* -t mfw=30; -t dt = DTIDATETIME; -t tdatetime.year = 86; -t tdatetime.month = 11; -t tdatetime.mday = 24; -t tdatetime.wday = 4; -t tdatetime.hour = 12; -t tdatetime.min = 11; -t tdatetime.sec = 34; -t p = (char *)&tdatetime; -t */ -t /* -t mfw=15; -t dt = DTITIME; -t ttime.hour = 12; -t ttime.min = 4; -t ttime.sec = -1; -t p = (char *)&ttime; -t */ -t mfw=10; -t dt = DTMONTH; -t i = 4; -t p = (char *)&i; -t -t cvin2sBuf(buf,p,dt,UNNONE,mfw,FMTLJ); -t printf("[%s] %d\n",buf,Cvnchars); -t cvin2sBuf(buf,p,dt,UNNONE,mfw,FMTRJ); -t printf("[%s] %d\n",buf,Cvnchars); -t cvin2sBuf(buf,p,dt,UNNONE,mfw,FMTSQ); -t printf("[%s] %d\n",buf,Cvnchars); -t #endif /* TESTDATE */ -t -t /* #define TESTFL */ -t #ifdef TESTFL -t dt = DTFLOAT; -t for (iv = 0; iv < NFV; iv++) -t { val = fvlist[iv]; -t mfw = 10; -t dfw = 2; -t f = FMTLJ; -t printf("%12.3f ",val); -t cvin2sBuf(buf,(char *)&val,dt,UNNONE,mfw,FMTPVPLUS+dfw+f); -t printf("Plus [%s] %d\t",buf,Cvnchars); -t cvin2sBuf(buf,(char *)&val,dt,UNNONE,mfw,FMTPVNULL+dfw+f); -t printf("Null [%s] %d\t",buf,Cvnchars); -t cvin2sBuf(buf,(char *)&val,dt,UNNONE,mfw,FMTPVSPACE+dfw+f); -t printf("Space [%s] %d\n",buf,Cvnchars); -t } -t #endif /* TESTFL */ -t -t #ifdef TESTFTIN -t val = 100.9; -t mfw = 6; -t dt = DTFLOAT; -t *buf = '\0'; -t for (iv = 0; iv < 2; iv++) -t { for (ifx = 0; ifx < 4; ifx++) -t { f = fmts[ifx]; -t printf("\n%s ... ",fmtnames[ifx]); -t cvin2sBuf(buf,(char *)&val,dt,UNLENGTH,mfw,FMTPVPLUS+f); -t printf("Plus %2d [%s]\t",Cvnchars,buf); -t cvin2sBuf(buf,(char *)&val,dt,UNLENGTH,mfw,FMTPVNULL+f); -t printf("Null %2d [%s]\t",Cvnchars,buf); -t cvin2sBuf(buf,(char *)&val,dt,UNLENGTH,mfw,FMTPVSPACE+f); -t printf("Space %2d [%s]",Cvnchars,buf); -t } -t val = -val; -t } -t #endif /* TESTFTIN */ -t -t /* #define TESTCF */ -t #ifdef TESTCF -t #define GETFDDT(r,f) DTFLOAT -t /* #define GETFDP(r,f) (&testf) */ -t #define GETFDUN(r,f) UNENERGY -t #define GETUNTAG(u) ("kBtuh") -t #define RCRT(r) (0) -t m = 30; -t for (mfw = 5; mfw < 9; mfw++) -t { cvcf2init( m, mfw, m+20, mfw+1); -t for (dfw = 0; dfw < 3; dfw++) -t { p = cvcf2( buf, "Dog", &testf, 1, FMTRJ+FMTUNITS+dfw, -t &testf, 1, FMTRJ+FMTUNITS+FMTNODATA+dfw ); -t printf("[%s] Len=%d\n",p,strlen(p)); -t } -t } -t #endif /* TESTCF */ -t } -#endif /* TEST */ + t #endif /* TESTINT */ + t t #ifdef TESTVAR t f = FMTRJ; + t mfw = VARWIDTH * Vardisp; + t cvin2sBuf(buf, (char *)vallist, DTVAR, UNNONE, mfw, FMTPVNULL + f); + t printf("[%s] %d\n", buf, Cvnchars); + t #endif /* TESTVAR */ + t t /* #define TESTSTR */ + t #ifdef TESTSTR t dt = DTSTRING; + t cvin2sBuf(buf, "Hello test", dt, UNNONE, 7, 0); + t printf("[%s] %d\n", buf, Cvnchars); + t cvin2sBuf(buf, "Hello test", dt, UNNONE, 11, 0); + t printf("[%s] %d\n", buf, Cvnchars); + t cvin2sBuf(buf, "Hello test", dt, UNNONE, 15, 0); + t printf("[%s] %d\n", buf, Cvnchars); + t cvin2sBuf(buf, "Hello test", dt, UNNONE, 15, FMTRJ); + t printf("[%s] %d\n", buf, Cvnchars); + t cvin2sBuf(buf, "Hello test", dt, UNNONE, 256, FMTSQ); + t printf("[%s] %d\n", buf, Cvnchars); + t #endif /* TESTSTR */ + t t #undef TESTDATE t #ifdef TESTDATE t /* + t dt = DTDOY; + t mfw = 10; + t i = 1; + t p = (char *)&i; + t */ + t /* + t mfw=30; + t dt = DTIDATETIME; + t tdatetime.year = 86; + t tdatetime.month = 11; + t tdatetime.mday = 24; + t tdatetime.wday = 4; + t tdatetime.hour = 12; + t tdatetime.min = 11; + t tdatetime.sec = 34; + t p = (char *)&tdatetime; + t */ + t /* + t mfw=15; + t dt = DTITIME; + t ttime.hour = 12; + t ttime.min = 4; + t ttime.sec = -1; + t p = (char *)&ttime; + t */ + t mfw = 10; + t dt = DTMONTH; + t i = 4; + t p = (char *)&i; + t t cvin2sBuf(buf, p, dt, UNNONE, mfw, FMTLJ); + t printf("[%s] %d\n", buf, Cvnchars); + t cvin2sBuf(buf, p, dt, UNNONE, mfw, FMTRJ); + t printf("[%s] %d\n", buf, Cvnchars); + t cvin2sBuf(buf, p, dt, UNNONE, mfw, FMTSQ); + t printf("[%s] %d\n", buf, Cvnchars); + t #endif /* TESTDATE */ + t t /* #define TESTFL */ + t #ifdef TESTFL t dt = DTFLOAT; + t for (iv = 0; iv < NFV; iv++) t { + val = fvlist[iv]; + t mfw = 10; + t dfw = 2; + t f = FMTLJ; + t printf("%12.3f ", val); + t cvin2sBuf(buf, (char *)&val, dt, UNNONE, mfw, FMTPVPLUS + dfw + f); + t printf("Plus [%s] %d\t", buf, Cvnchars); + t cvin2sBuf(buf, (char *)&val, dt, UNNONE, mfw, FMTPVNULL + dfw + f); + t printf("Null [%s] %d\t", buf, Cvnchars); + t cvin2sBuf(buf, (char *)&val, dt, UNNONE, mfw, FMTPVSPACE + dfw + f); + t printf("Space [%s] %d\n", buf, Cvnchars); + t + } + t #endif /* TESTFL */ + t t #ifdef TESTFTIN t val = 100.9; + t mfw = 6; + t dt = DTFLOAT; + t *buf = '\0'; + t for (iv = 0; iv < 2; iv++) t { + for (ifx = 0; ifx < 4; ifx++) + t { + f = fmts[ifx]; + t printf("\n%s ... ", fmtnames[ifx]); + t cvin2sBuf(buf, (char *)&val, dt, UNLENGTH, mfw, FMTPVPLUS + f); + t printf("Plus %2d [%s]\t", Cvnchars, buf); + t cvin2sBuf(buf, (char *)&val, dt, UNLENGTH, mfw, FMTPVNULL + f); + t printf("Null %2d [%s]\t", Cvnchars, buf); + t cvin2sBuf(buf, (char *)&val, dt, UNLENGTH, mfw, FMTPVSPACE + f); + t printf("Space %2d [%s]", Cvnchars, buf); + t + } + t val = -val; + t + } + t #endif /* TESTFTIN */ + t t /* #define TESTCF */ + t #ifdef TESTCF t #define GETFDDT(r, f) + DTFLOAT t /* #define GETFDP(r,f) (&testf) */ + t #define GETFDUN(r, f) + UNENERGY t #define GETUNTAG(u)("kBtuh") + t #define RCRT(r)(0) t m = 30; + t for (mfw = 5; mfw < 9; mfw++) t { + cvcf2init(m, mfw, m + 20, mfw + 1); + t for (dfw = 0; dfw < 3; dfw++) t { + p = cvcf2(buf, "Dog", &testf, 1, FMTRJ + FMTUNITS + dfw, t & testf, 1, + FMTRJ + FMTUNITS + FMTNODATA + dfw); + t printf("[%s] Len=%d\n", p, strlen(p)); + t + } + t + } + t #endif /* TESTCF */ + t +} +#endif /* TEST */ /////////////////////////////////////////////////////////////////////////////// // basic string-binary conversion functions including @@ -1677,64 +1835,69 @@ t } // be about 3 times slower than cvatof() and introduces MSC dependency. /*=============================== TEST CODE ================================*/ #ifdef TEST -t -t main() -t{ -t RC rc; -t LI li; -t SI i, test, loop, loopLimit, printFlg; -t double d; -t char *s; -t static struct /* test driver table */ -t { char *s; /* string to convert, NULL to term table */ -t SI test; /* 0: cvatol, hexoct = 0 +t t main() t { + t RC rc; + t LI li; + t SI i, test, loop, loopLimit, printFlg; + t double d; + t char *s; + t static struct /* test driver table */ + t { + char *s; /* string to convert, NULL to term table */ + t SI test; /* 0: cvatol, hexoct = 0 t 1: cvatol, hexoct = 1 t 2: cvatof, percent = 0 t 3: cvatof, percent = 1 */ -t } tab[] = -t { " 3", 2, -t " 3.33e+4", 2, -t " 3.e+4", 2, -t " 3e-10", 2, -t "5.34952945293",2, -t " 3.33%e17", 3, -t " 3.33 %", 3, -t ".", 2, -t "0000.0", 2, -t ".0", 2, -t NULL -t }; -t -t loopLimit = 1; -t for (loop = 0; ++loop <= loopLimit; ) -t { -t printFlg = loop == loopLimit; -t i = -1; -t while ( (s = tab[++i].s) != NULL) -t { test = tab[i].test; -t if (printFlg) -t printf("s=|%s| ",s); -t if (test <= 1) -t { -t li = 0L; -t rc = cvatol( s, &li, test ); -t if (printFlg) -t printf("li = %ld", li ); -t } -t else -t { -t d = 0.; -t rc = cvatof( s, &d, test-2 ); -t if (printFlg) -t printf("d = %f", d ); -t } -t if (printFlg) -t printf(" rc = %d\n", rc ); -t } -t } -t return 0; /* keep compiler happy */ -t} /* main */ -#endif /* TEST */ + t + } tab[] = t{" 3", + 2, + t " 3.33e+4", + 2, + t " 3.e+4", + 2, + t " 3e-10", + 2, + t "5.34952945293", + 2, + t " 3.33%e17", + 3, + t " 3.33 %", + 3, + t ".", + 2, + t "0000.0", + 2, + t ".0", + 2, + t NULL t}; + t t loopLimit = 1; + t for (loop = 0; ++loop <= loopLimit;) t { + t printFlg = loop == loopLimit; + t i = -1; + t while ((s = tab[++i].s) != NULL) t { + test = tab[i].test; + t if (printFlg) t printf("s=|%s| ", s); + t if (test <= 1) t { + t li = 0L; + t rc = cvatol(s, &li, test); + t if (printFlg) t printf("li = %ld", li); + t + } + t else t { + t d = 0.; + t rc = cvatof(s, &d, test - 2); + t if (printFlg) t printf("d = %f", d); + t + } + t if (printFlg) t printf(" rc = %d\n", rc); + t + } + t + } + t return 0; /* keep compiler happy */ + t +} /* main */ +#endif /* TEST */ //=========================================================================== #if 0 @@ -1818,15 +1981,15 @@ RC FC cvatol( /* convert string to (signed) long integer */ return RCOK; /* good return */ } /* cvatol */ -#endif // unused +#endif // unused //======================================================================== -RC FC cvatof( // Convert a string to a double value +RC FC cvatof( // Convert a string to a double value - const char* _str, // String, leading and trailing ws OK - double* vp, // Pointer to double to receive value. - bool percent ) // true: trailing '%' is acceptable (and ignored) - // false '%' treated as error + const char *_str, // String, leading and trailing ws OK + double *vp, // Pointer to double to receive value. + bool percent) // true: trailing '%' is acceptable (and ignored) + // false '%' treated as error /* valid format: [ws][+|-][ws][digits][.[digits]][e|E|d|D[+|-][digits]][ws][*] @@ -1839,176 +2002,186 @@ RC FC cvatof( // Convert a string to a double value so values over about 10**15 must be input with e format. Changes 9-11-89: - 1. accepts D/d in addition to E/e for exponents - 2. new arg percent eliminates use of cvpak global. % now handled - correctly for e format ( .34e01 % ); exponents no longer go - through cvatol. - 3. combined action and state tables into single table, made entries - symbolic w/ #defines for ez reading. */ + 1. accepts D/d in addition to E/e for exponents + 2. new arg percent eliminates use of cvpak global. % now handled + correctly for e format ( .34e01 % ); exponents no longer go + through cvatol. + 3. combined action and state tables into single table, made entries + symbolic w/ #defines for ez reading. */ /* Returns RCOK if conversion succeeded, otherwise informative RC. No value is stored unless conversion succeeds. */ { - double v; - SI negVal, negExp, nd, id, idd, expo, irdd; - char c; - char d[100], dd[100]; /* characters left / right of decimal point */ - SI s, ct; - - /* String decode portion of function is implemented with finite state scheme. - The STATE records the current decoding situation; each input char is - retrieved and a state-dependent ACTION is taken. The table actState - drives the process. */ - /* States */ -#define lws 0 /* scanning over leading white space */ -#define ld 1 /* scanning for leading digit (sign has been seen) */ -#define gd 2 /* getting digits (left of dec point) */ -#define gdd 3 /* getting digits (right of dec point) */ -#define xsn 4 /* expecting exponent sign (or digit) */ -#define xld 5 /* expecting exponent leading digit */ -#define xd 6 /* expecting exponent digit (or term. non-digit) */ -#define tc 7 /* scanning over trailing chars (% or ws) */ -#define tws 8 /* scanning over trailing white space */ - /* Actions */ -#define SK 0x00 /* discard character */ -#define GSN 0x10 /* note sign */ -#define GD 0x20 /* get digit (left of dec. point) */ -#define GDD 0x30 /* get digit (right of dec. point) */ -#define GXS 0x40 /* note exponent sign */ -#define GX 0x50 /* get exponent digit */ -#define DONE 0x60 /* \0 found, go determine value */ -#define ERR 0xF0 /* syntax error, return "Invalid number" */ - static UCH actState[9][7] = - { - /* ------------------ Character ------------------------- */ - /* ws + or - 0-9 . Ee/Dd % '\0' */ - /* ------ ------ ------ ------ ------ ------ ------ */ - /* lws: leading ws */ { SK+lws, GSN+ld, GD+gd, SK+gdd, ERR, ERR, ERR }, - /* ld: leading dgt */ { SK+ld, ERR, GD+gd, SK+gdd, ERR, ERR, ERR }, - /* gd: get digits */ { SK+tc, ERR, GD+gd, SK+gdd, SK+xsn, SK+tws, DONE }, - /* gdd: get dec dgts */ { SK+tc, ERR, GDD+gdd,ERR, SK+xsn, SK+tws, DONE }, - /* xsn: get expo sign*/ { ERR, GXS+xld,GX+xd, ERR, ERR, ERR, ERR }, - /* xld: expo ldng dgt*/ { ERR, ERR, GX+xd, ERR, ERR, ERR, ERR }, - /* xd: expo digit */ { SK+tc, ERR, GX+xd, ERR, ERR, SK+tws, DONE }, - /* tc: trlng chars */ { SK+tc, ERR, ERR, ERR, ERR, SK+tws, DONE }, - /* tws: trailing ws */ { SK+tws, ERR, ERR, ERR, ERR, ERR, DONE } - }; - - negVal = negExp = nd = id = idd = irdd = expo = 0; - s = lws; /* initial state: scanning over ws */ - - for ( ; ; ) /* loop over char string */ - { - /* get and categorize character */ - c = tolower(*_str); - if (isdigitW(c)) ct = 2; - else if (isspaceW(c)) ct = 0; - else if (c=='.') ct = 3; - else if (c=='\0') ct = 6; - else if (c=='-' || c=='+') ct = 1; - else if (c=='e' || c=='d') ct = 4; - else if (c=='%' && percent) ct = 5; /* cond'l recog of '%' */ - else return MH_V0010; // "Invalid number": unrec char - // or bad format - - - switch ( actState[s][ct] & 0xF0 ) /* perform actions */ - { - case SK: - break; /* skip character */ - - case GSN: - negVal = (c=='-'); /* get sign: note if '-' */ - break; - - case GD: - nd++; /* total digits w/ ldng 0s */ - if ( id > 0 || c != '0') /* get digit, skp ldng 0s */ - d[id++] = c - '0'; - break; - - case GDD: - dd[idd++] = c - '0'; /* get dec digit */ - if (c != '0') /* if non-0 */ - { - irdd=idd; /* note rightmost dec. dig. */ - if (id==0) - id = -idd; /* and most sig. digit */ - } - break; - - case GXS: - negExp = (c=='-'); /* note negative exponent */ - break; - - case GX: - expo = 10*expo + c - '0'; /* accum exponent value */ - if (expo > 1000) - return MH_V0013; // quit b4 expo overflows - // "Value must be between 10e-38 and 10e38" - break; - - case DONE: - if (negExp) - expo = -expo; - if (id < 0) /* if only digits right of dp */ - id++; /* set most sig pos to final value */ - goto makeval; - - case ERR: - return MH_V0010; // "invalid number" - - default: - ; - } - s = actState[s][ct] & 0x0F; /* new state */ - _str++; /* next char */ - } - - /* Upon arrival here, things are set up as follows: - id: Position of most significant digit. For instance, 1000.012 - would produce 4, 0.1 would produce 0, 0.001 would produce -2. - nd: Total digits to left of decimal point including leading 0s - idd: Total digits to right of decimal point including 0s - irdd: Position of least sig. non-0 digit to right of decimal point. - 1000. would produce 0, 1000.01200 would produce 3. - expo: Exponent. Truly ridiculous values have been eliminated above. - */ + double v; + SI negVal, negExp, nd, id, idd, expo, irdd; + char c; + char d[100], dd[100]; /* characters left / right of decimal point */ + SI s, ct; + + /* String decode portion of function is implemented with finite state scheme. + The STATE records the current decoding situation; each input char is + retrieved and a state-dependent ACTION is taken. The table actState + drives the process. */ + /* States */ +#define lws 0 /* scanning over leading white space */ +#define ld 1 /* scanning for leading digit (sign has been seen) */ +#define gd 2 /* getting digits (left of dec point) */ +#define gdd 3 /* getting digits (right of dec point) */ +#define xsn 4 /* expecting exponent sign (or digit) */ +#define xld 5 /* expecting exponent leading digit */ +#define xd 6 /* expecting exponent digit (or term. non-digit) */ +#define tc 7 /* scanning over trailing chars (% or ws) */ +#define tws 8 /* scanning over trailing white space */ + /* Actions */ +#define SK 0x00 /* discard character */ +#define GSN 0x10 /* note sign */ +#define GD 0x20 /* get digit (left of dec. point) */ +#define GDD 0x30 /* get digit (right of dec. point) */ +#define GXS 0x40 /* note exponent sign */ +#define GX 0x50 /* get exponent digit */ +#define DONE 0x60 /* \0 found, go determine value */ +#define ERR 0xF0 /* syntax error, return "Invalid number" */ + static UCH actState[9][7] = { + /* ------------------ Character + ------------------------- */ + /* ws + or - 0-9 . Ee/Dd + % '\0' */ + /* ------ ------ ------ ------ ------ + ------ ------ */ + /* lws: leading ws */ {SK + lws, GSN + ld, GD + gd, SK + gdd, ERR, ERR, + ERR}, + /* ld: leading dgt */ {SK + ld, ERR, GD + gd, SK + gdd, ERR, ERR, ERR}, + /* gd: get digits */ + {SK + tc, ERR, GD + gd, SK + gdd, SK + xsn, SK + tws, DONE}, + /* gdd: get dec dgts */ + {SK + tc, ERR, GDD + gdd, ERR, SK + xsn, SK + tws, DONE}, + /* xsn: get expo sign*/ {ERR, GXS + xld, GX + xd, ERR, ERR, ERR, ERR}, + /* xld: expo ldng dgt*/ {ERR, ERR, GX + xd, ERR, ERR, ERR, ERR}, + /* xd: expo digit */ {SK + tc, ERR, GX + xd, ERR, ERR, SK + tws, DONE}, + /* tc: trlng chars */ {SK + tc, ERR, ERR, ERR, ERR, SK + tws, DONE}, + /* tws: trailing ws */ {SK + tws, ERR, ERR, ERR, ERR, ERR, DONE}}; + + negVal = negExp = nd = id = idd = irdd = expo = 0; + s = lws; /* initial state: scanning over ws */ + + for (;;) /* loop over char string */ + { + /* get and categorize character */ + c = tolower(*_str); + if (isdigitW(c)) + ct = 2; + else if (isspaceW(c)) + ct = 0; + else if (c == '.') + ct = 3; + else if (c == '\0') + ct = 6; + else if (c == '-' || c == '+') + ct = 1; + else if (c == 'e' || c == 'd') + ct = 4; + else if (c == '%' && percent) + ct = 5; /* cond'l recog of '%' */ + else + return MH_V0010; // "Invalid number": unrec char + // or bad format + + switch (actState[s][ct] & 0xF0) /* perform actions */ + { + case SK: + break; /* skip character */ + + case GSN: + negVal = (c == '-'); /* get sign: note if '-' */ + break; + + case GD: + nd++; /* total digits w/ ldng 0s */ + if (id > 0 || c != '0') /* get digit, skp ldng 0s */ + d[id++] = c - '0'; + break; + + case GDD: + dd[idd++] = c - '0'; /* get dec digit */ + if (c != '0') /* if non-0 */ + { + irdd = idd; /* note rightmost dec. dig. */ + if (id == 0) + id = -idd; /* and most sig. digit */ + } + break; + + case GXS: + negExp = (c == '-'); /* note negative exponent */ + break; + + case GX: + expo = 10 * expo + c - '0'; /* accum exponent value */ + if (expo > 1000) + return MH_V0013; // quit b4 expo overflows + // "Value must be between 10e-38 and 10e38" + break; + + case DONE: + if (negExp) + expo = -expo; + if (id < 0) /* if only digits right of dp */ + id++; /* set most sig pos to final value */ + goto makeval; + + case ERR: + return MH_V0010; // "invalid number" + + default:; + } + s = actState[s][ct] & 0x0F; /* new state */ + _str++; /* next char */ + } + + /* Upon arrival here, things are set up as follows: + id: Position of most significant digit. For instance, 1000.012 + would produce 4, 0.1 would produce 0, 0.001 would produce -2. + nd: Total digits to left of decimal point including leading 0s + idd: Total digits to right of decimal point including 0s + irdd: Position of least sig. non-0 digit to right of decimal point. + 1000. would produce 0, 1000.01200 would produce 3. + expo: Exponent. Truly ridiculous values have been eliminated above. + */ makeval: - /* various validity checks */ - if ( (nd + idd) == 0) - return MH_V0010; /* "invalid number": no digits */ - if ( id > PTENSIZE /* more than 16 signif dgts to left of d.p. */ - || irdd > PTENSIZE-1) /* more than 16 signif dgts to rght of d.p. */ - return MH_V0016; /* "Too many digits. Use E format for - very large or very small values" */ - if (expo+id > FLT_MAX_10_EXP // FLT_MAX_10_EXP = 38 (float.h) - || expo+id-1 < -FLT_MAX_10_EXP) - return MH_V0013; /* "Value must be between 10e-38 and 10e38" */ - - /* sum up value of all non-0 digits starting from least significant */ - v = 0.; - for (s = irdd; --s >= 0; ) - if ( dd[s] ) /* if non-0 digit right of dp */ - v += (double)dd[s]/Pten[s+1]; - for (s = id; --s >= 0; ) - if ( d[s] ) /* if non-0 digit left of dp */ - v += (double)d[s]*Pten[id-s-1]; - - /* scale by 10**expo */ - if (expo) /* if there's a non-0 exponent */ - { - if (expo > 0 && expo < PTENSIZE) - v *= Pten[expo]; /* pos expo: mult by pwr of 10 */ - else if (expo < 0 && -expo < PTENSIZE) - v /= Pten[-expo]; /* neg expo: div by pwr of 10 */ - else - v *= pow( 10., (double)expo); /* outside Pten[] range, use pow */ - } - *vp = negVal ? -v : v; - return RCOK; -} /* cvatof */ + /* various validity checks */ + if ((nd + idd) == 0) + return MH_V0010; /* "invalid number": no digits */ + if (id > PTENSIZE /* more than 16 signif dgts to left of d.p. */ + || irdd > PTENSIZE - 1) /* more than 16 signif dgts to rght of d.p. */ + return MH_V0016; /* "Too many digits. Use E format for + very large or very small values" */ + if (expo + id > FLT_MAX_10_EXP // FLT_MAX_10_EXP = 38 (float.h) + || expo + id - 1 < -FLT_MAX_10_EXP) + return MH_V0013; /* "Value must be between 10e-38 and 10e38" */ + + /* sum up value of all non-0 digits starting from least significant */ + v = 0.; + for (s = irdd; --s >= 0;) + if (dd[s]) /* if non-0 digit right of dp */ + v += (double)dd[s] / Pten[s + 1]; + for (s = id; --s >= 0;) + if (d[s]) /* if non-0 digit left of dp */ + v += (double)d[s] * Pten[id - s - 1]; + + /* scale by 10**expo */ + if (expo) /* if there's a non-0 exponent */ + { + if (expo > 0 && expo < PTENSIZE) + v *= Pten[expo]; /* pos expo: mult by pwr of 10 */ + else if (expo < 0 && -expo < PTENSIZE) + v /= Pten[-expo]; /* neg expo: div by pwr of 10 */ + else + v *= pow(10., (double)expo); /* outside Pten[] range, use pow */ + } + *vp = negVal ? -v : v; + return RCOK; +} /* cvatof */ // end of cvpak.cpp From 31680dc5f0b696650a4a12f964fe6805d1bddf3a Mon Sep 17 00:00:00 2001 From: Phil Ahrenkiel Date: Mon, 18 Mar 2024 19:46:19 -0600 Subject: [PATCH 02/21] Fix format. --- src/cvpak.cpp | 3362 +++++++++++++++++++++++-------------------------- 1 file changed, 1595 insertions(+), 1767 deletions(-) diff --git a/src/cvpak.cpp b/src/cvpak.cpp index a874273c9..694a4e5b7 100644 --- a/src/cvpak.cpp +++ b/src/cvpak.cpp @@ -7,10 +7,10 @@ #include "cnglob.h" -#undef M0BUG // #define to enable code looking for -0 / -1 bug 4-3-2022 - // see github issue #213 +#undef M0BUG // #define to enable code looking for -0 / -1 bug 4-3-2022 + // see github issue #213 -#if defined(M0BUG) +#if defined( M0BUG) #include "ancrec.h" #include "rccn.h" #endif @@ -18,121 +18,112 @@ /*-------------------------------- OPTIONS --------------------------------*/ /*------------------------------- INCLUDES --------------------------------*/ -#include "msghans.h" // MH_xxxx +#include "msghans.h" // MH_xxxx -#include "srd.h" // Unsysext, Untab, GetDttab +#include "srd.h" // Unsysext, Untab, GetDttab #include "tdpak.h" -#include "cvpak.h" // decls for this file +#include "cvpak.h" // decls for this file /*-------------------------------- DEFINES --------------------------------*/ /*--------------------------- PUBLIC VARIABLES ----------------------------*/ /*---------------------------- LOCAL VARIABLES ----------------------------*/ -static int Cvnchars = 0; // # output chars from internal to external conversion. +static int Cvnchars = 0; // # output chars from internal to external conversion. // cvin2s and callees working variables -#ifdef FMTPVMASK // define in cvpak.h to restore p positive value display - // options, 11-91 -p static SI pv; // positive value display: null, +, spc -p static SI ipv; // ditto right-shifted for use as subscript -p /* note positive value options not used (except check debugpr.c); +#ifdef FMTPVMASK // define in cvpak.h to restore p positive value display options, 11-91 +p static SI pv; // positive value display: null, +, spc +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 mfw; // cvin2s "max field width" argument. -static SI just; // justification: left, rt, rz, squeeze. -static SI ijust; // ditto right-shifted for use as subscript. -static SI lj; // left justify (just & FLMLJ). -static SI lz; // leading 0's (right justify). note FMTLZ not used, could code - // out. rob 10-88 -static SI wsign; // 0 or 1 columns for sign, set by cvin2s -static SI wid; // width for sprintf: 1 for squeeze, else mfw. cvdd expects - // caller to set. -static SI ppos; // precomputed sprintf precision (prcsn) for several cases for - // values >= 0 -static SI pneg; // precomputed sprintf precision (prcsn) for serveral cases for - // values < 0 -static double aval; // fabs(val): set by cvsd, cvdd, ft-in; updated by nexK. -static double val; // value for float/double print cases -static char *str; // Tmpstr destination location +static USI fmt; // 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. +static SI lj; // left justify (just & FLMLJ). +static SI lz; // leading 0's (right justify). note FMTLZ not used, could code out. rob 10-88 +static SI wsign; // 0 or 1 columns for sign, set by cvin2s +static SI wid; // width for sprintf: 1 for squeeze, else mfw. cvdd expects caller to set. +static SI ppos; // precomputed sprintf precision (prcsn) for several cases for values >= 0 +static SI pneg; // precomputed sprintf precision (prcsn) for serveral cases for values < 0 +static double aval; // fabs(val): set by cvsd, cvdd, ft-in; updated by nexK. +static double val; // value for float/double print cases +static char * str; // Tmpstr destination location /*----------------------------- INITIALIZED DATA --------------------------*/ -#ifdef FMTPVMASK // define in cvpak.h to restore p positive value display - // options, 11-91 -p p /* Idea: left-justify format appears to ALWAYS differ by having leading -; - p could create from base format at run time ? */ - p /* These format definitions must match definitions of FMTPVxxx in - cvpak.h */ - p /* ipv: if >= 0, show null, +, space */ - p add NEAR's if restored. p /* SI and floating 0 formats */ p static char - *sif[2][3] = {"%*.*d", "%+*.*d", "% *.*d", /* other */ - p "%-*.*d", "%-+*.*d", "%- *.*d"}; /* left just */ -p static char *lif[2][3] = {"%*.*ld", "%+*.*ld", "% *.*ld", /* other */ - p "%-*.*ld", "%-+*.*ld", "%- *.*ld"}; /* lj */ -p static char *usif[2] = {"%*.*u", /* other */ - p "%-*.*u"}; /* lj */ -p static char *sf[2] = {"%*.*s", /* other */ - p "%-*.*s"}; /* lj */ -p /* basic float: done in cvdd(). */ - p p /* for trim trailing 0's. ipv: - null + spc */ - p static char *gf[4][3] = { - "%-*.*g", "%-+*.*g", "%- *.*g", /* left j */ - p "%*.*g", "%+*.*g", "% *.*g", /* rj */ - p "%0*.*g", "%0+*.*g", "%0 *.*g", /* rj 0s */ - p "%*.*g", "%+*.*g", "% *.*g" p}; /* sqz */ -p p /* IP lengths. subscript ipv: null, +, space */ - p /* dfw 0, feet and inches */ - p static char *ff1[3] = {"%*.*ld'%*d", "%+*.*ld'%*d", "% *.*ld'%*d"}; +#ifdef FMTPVMASK // define in cvpak.h to restore p positive value display options, 11-91 +p +p /* Idea: left-justify format appears to ALWAYS differ by having leading -; +p could create from base format at run time ? */ +p /* These format definitions must match definitions of FMTPVxxx in cvpak.h */ +p /* ipv: if >= 0, show null, +, space */ +p add NEAR's if restored. +p /* SI and floating 0 formats */ +p static char *sif[2][3] = {"%*.*d", "%+*.*d", "% *.*d", /* other */ +p "%-*.*d", "%-+*.*d", "%- *.*d"}; /* left just */ +p static char *lif[2][3] = {"%*.*ld", "%+*.*ld", "% *.*ld", /* other */ +p "%-*.*ld","%-+*.*ld","%- *.*ld"}; /* lj */ +p static char *usif[2] = {"%*.*u", /* other */ +p "%-*.*u"}; /* lj */ +p static char *sf[2] = {"%*.*s", /* other */ +p "%-*.*s"}; /* lj */ +p /* basic float: done in cvdd(). */ +p +p /* for trim trailing 0's. ipv: +null + spc */ +p static char *gf[4][3] = {"%-*.*g", "%-+*.*g", "%- *.*g", /* left j */ +p "%*.*g", "%+*.*g", "% *.*g", /* rj */ +p "%0*.*g", "%0+*.*g", "%0 *.*g", /* rj 0s */ +p "%*.*g", "%+*.*g", "% *.*g" +p }; /* sqz */ +p +p /* IP lengths. subscript ipv: null, +, space */ +p /* dfw 0, feet and inches */ +p static char *ff1[3] = {"%*.*ld'%*d", "%+*.*ld'%*d", "% *.*ld'%*d"}; p /* dfw 0, inches only */ - p static char *ff3[3] = {"%*.*d", "%+*.*d", "% *.*d"}; +p static char *ff3[3] = {"%*.*d", "%+*.*d", "% *.*d"}; p /* dfw nz, feet and inches */ - p static char *ff4[3] = {"%*.*ld'%*.*f", "%+*.*ld'%*.*f", "% *.*ld'%*.*f"}; +p static char *ff4[3] = {"%*.*ld'%*.*f", "%+*.*ld'%*.*f", "% *.*ld'%*.*f"}; p /* dfw nz, inches only */ - p static char *ff5[3] = {"%*.*f", "%+*.*f", "% *.*f"}; +p static char *ff5[3] = {"%*.*f", "%+*.*f", "% *.*f"}; p #else -// sprintf formats, used in cvin2s, cvin2Ft_in. Basic float formats are in -// cvdd. +// sprintf formats, used in cvin2s, cvin2Ft_in. Basic float formats are in cvdd. // -// Idea: left-justify formats appear to differ by leading -; create -// from base format at run time ? +// Idea: left-justify formats appear to differ by leading -; create from base format at run time ? // // SI and floating 0 formats. other left just -static char *sif[2] = {"%*.*d", "%-*.*d"}; -static char *lif[2] = {"%*.*ld", "%-*.*ld"}; -static char *usif[2] = {"%*.*u", "%-*.*u"}; -static char *sf[2] = {"%*.*s", "%-*.*s"}; +static char * sif[2] = { "%*.*d", "%-*.*d" }; +static char * lif[2] = { "%*.*ld", "%-*.*ld" }; +static char * usif[2] = { "%*.*u", "%-*.*u" }; +static char * sf[2] = { "%*.*s", "%-*.*s" }; // // for trim trailing 0's: left j rj rj 0s sqz -static char *gf[4] = {"%-*.*g", "%*.*g", "%0*.*g", "%*.*g"}; +static char * gf[4] = { "%-*.*g", "%*.*g", "%0*.*g", "%*.*g" }; // // IP length formats -static char *ff1 = "%*.*ld'%*d"; // dfw 0, feet and inches -static char *ff3 = "%*.*d"; // dfw 0, inches only -static char *ff4 = "%*.*ld'%*.*f"; // dfw nz, feet and inches -static char *ff5 = "%*.*f"; // dfw nz, inches only +static char * ff1 = "%*.*ld'%*d"; // dfw 0, feet and inches +static char * ff3 = "%*.*d"; // dfw 0, inches only +static char * ff4 = "%*.*ld'%*.*f"; // dfw nz, feet and inches +static char * ff5 = "%*.*f"; // dfw nz, inches only #endif - /*----------------------- LOCAL FUNCTION DECLARATIONS - ---------------------*/ - LOCAL SI FC - cvttz(char *str, SI trailChar, SI minWid); +/*----------------------- LOCAL FUNCTION DECLARATIONS ---------------------*/ +LOCAL SI FC cvttz( char *str, SI trailChar, SI minWid); LOCAL bool FC nexK(); -LOCAL bool FC cvsd(SI mfw, SI dfw); -LOCAL bool FC cvdd(SI mfw, SI dfw); +LOCAL bool FC cvsd( SI mfw, SI dfw); +LOCAL bool FC cvdd( SI mfw, SI dfw); LOCAL void FC cvDouble2s(void); LOCAL void FC cvFtIn2s(void); -LOCAL int FC sepFtInch(double d, int &inch); +LOCAL int FC sepFtInch( double d, int& inch); // unmaintained test code is at end //====================================================================== -char *FC cvin2sBuf(char *buf, const void *data, USI dt, SI units, USI _mfw, - USI _fmt) +char * FC cvin2sBuf( char *buf, const void *data, USI dt, SI units, USI _mfw, USI _fmt) // Convert internal format data to external format string in caller's buffer @@ -141,286 +132,253 @@ char *FC cvin2sBuf(char *buf, const void *data, USI dt, SI units, USI _mfw, // returns buf (or Tmpstr location if NULL given). // Also sets Cvnchars to strlen(result). { - char *p; + char *p; - p = cvin2s(data, dt, units, _mfw, _fmt); // convert (next) - if (p == NULL) // if data is NULL, cvin2s nops 9-89 - return NULL; /* nop here too 9-89 (putting "" or right # blanks - in buf might make more sense?). */ - return buf ? strcpy( - buf, // copy into caller's buffer - strtempPop( - p)) // free Tmpstr space (so less likely to wrap onto - // caller's stuff). recursive. returns p. strpak.c. - : p; // no buffer given, return Tmpstr location + p = cvin2s( data, dt, units, _mfw, _fmt); // convert (next) + if (p==NULL) // if data is NULL, cvin2s nops 9-89 + return NULL; /* nop here too 9-89 (putting "" or right # blanks + in buf might make more sense?). */ + return buf ? strcpy( buf, // copy into caller's buffer + strtempPop(p)) // free Tmpstr space (so less likely to wrap onto caller's stuff). + // recursive. returns p. strpak.c. + : p; // no buffer given, return Tmpstr location -} // cvin2sBuf +} // cvin2sBuf //====================================================================== -char *FC -cvin2s( // Convert internal format data to external format string in Tmpstr - - const void - *data, // Pointer to data in internal form, or NULL to do nothing and - // return NULL - // (for DTCULSTR, is ptr to ptr to string to print, 11-91) */ - USI dt, // Data type of internal data, or DTNA for "--" or DTUNDEF for "?" - // from cvfddisp() - SI units, // Units of internal data (made signed 5-89) - USI _mfw, // Maximum field width (not including '\0'). If requested format - // results in string longer than mfw, format will be altered if - // possible to give max significance within mfw; when not possible - // (never possible for inteters or if field too narrow for e or k - // format), field of **** is returned (but see _xfw). - USI _fmt, // Format. See cvpak.h for definition of fields. - USI _xfw /*=0*/) /* 0 or extra field width available: if value cannot be - formatted in _mfw columns does fit in _mfw + _xfw or fewer cols, - return the overlong text rather than ******. added by rob, - 4-92. */ +char * FC cvin2s( // Convert internal format data to external format string in Tmpstr + + const void* data, // Pointer to data in internal form, or NULL to do nothing and return NULL + // (for DTCULSTR, is ptr to ptr to string to print, 11-91) */ + USI dt, // Data type of internal data, or DTNA for "--" or DTUNDEF for "?" from cvfddisp() + SI units, // Units of internal data (made signed 5-89) + USI _mfw, // Maximum field width (not including '\0'). If requested format results in string longer + // than mfw, format will be altered if possible to give max significance within mfw; + // when not possible (never possible for inteters or if field too narrow for e or k format), + // field of **** is returned (but see _xfw). + USI _fmt, // Format. See cvpak.h for definition of fields. + USI _xfw /*=0*/ ) /* 0 or extra field width available: if value cannot be formatted in _mfw columns does fit + in _mfw + _xfw or fewer cols, return the overlong text rather than ******. added by rob, 4-92. */ // Returns pointer to result in Tmpstr. -// Also sets global Cvnchars to the number of characters placed in str (not -// incl. '\0'). +// Also sets global Cvnchars to the number of characters placed in str (not incl. '\0'). -// This routine will in in some cases overwrite one or more chars beyond the -// specified field. \0 always appended. +// This routine will in in some cases overwrite one or more chars beyond the specified field. \0 always appended. { #ifdef DTPERCENT - bool percent = - false; // set true if converting a DTPERCENT; shares DTFLOAT code + bool percent = false; // set true if converting a DTPERCENT; shares DTFLOAT code #endif - fmt = _fmt; // store format arg for use by callees - mfw = _mfw; // .. max field width arg + fmt = _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. (note pgw also - nops on getting null 9-89; using - 9-89 in crlout.c) */ - if (data == NULL + /* NULL data pointer means do NOTHING */ /* for caller convenience in supporting optional stuff, tentative 9-89. + (note pgw also nops on getting null 9-89; using 9-89 in crlout.c) */ + if ( data==NULL #ifdef DTUNDEF - && dt != DTUNDEF // these use no data and "data" may be garbage + && dt != DTUNDEF // these use no data and "data" may be garbage #endif #ifdef DTNA - && dt != DTNA // .. from cvpak3:cvfddisp() + && dt != DTNA // .. from cvpak3:cvfddisp() #endif - ) - return NULL; // or should it ret blank field? set Cvnchars? 9-89. - - // 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 - 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. - - /* 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 ipv = ((USI)pv) >> FMTPVSHIFT; // .. shifted for use as subscript -- - // frequently used to select formats - p // (shift made unsigned for lint) + ) + return NULL; // or should it ret blank field? set Cvnchars? 9-89. + +// 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 + 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. + + /* 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 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 - ijust = ((USI)just) >> FMTJSHIFT; // .. shifted for use as subscript - // (shift made unsigned for lint) - lj = just == FMTLJ; // left justify flag - lz = just == FMTLZ; // leading zeroes (right justify) flag - wid = just == FMTSQ ? 1 : mfw; // width for sprintf: 1 to squeeze (float - // only). note cvdd uses file-global. - // for several cases, preset sprintf precision (ppos, pneg, prcsn): min digits - // for many formats other than e, f. - if (lz) // if leading zero format requested - { + just = fmt&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 + lz = just==FMTLZ; // leading zeroes (right justify) flag + wid = just==FMTSQ ? 1 : mfw; // width for sprintf: 1 to squeeze (float only). note cvdd uses file-global. + // for several cases, preset sprintf precision (ppos, pneg, prcsn): min digits for many formats other than e, f. + if (lz) // if leading zero format requested + { #ifdef FMTPVMASK - p ppos = // precision positive/unsigned data - p pv == FMTPVNULL // if null for +, - p - ? wid - : wid - 1; // full width, else 1 less +p ppos = // precision positive/unsigned data +p pv==FMTPVNULL // if null for +, +p ? wid : wid - 1; // full width, else 1 less #else - ppos = wid; // precision for positive/unsigned data is full width + ppos = wid; // precision for positive/unsigned data is full width #endif - pneg = wid - 1; // precision for neg several cases: leave column for - - } else // for other formats - ppos = pneg = 1; // init pos and neg precision to 1 - - // Datatype specific internal-->external processing - INT iV{0}; - UINT uiV{0}; - switch (dt) { + pneg = wid - 1; // precision for neg several cases: leave column for - + } + else // for other formats + ppos = pneg = 1; // init pos and neg precision to 1 + + // Datatype specific internal-->external processing + INT iV{ 0 }; + UINT uiV{ 0 }; + switch (dt) + { #ifdef FMTPVMASK - p case DTSI: - p if (*(SI *)data == 0 && pv == FMTPVPLUS) p pv = FMTPVSPACE; - p Cvnchars = sprintf(str, sif[lj][ipv], wid, *(SI *)data < 0 ? pneg : ppos, - *(SI *)data); - p break; +p case DTSI: +p if (*(SI *)data == 0 && pv==FMTPVPLUS) +p pv=FMTPVSPACE; +p Cvnchars = sprintf( str, sif[lj][ipv], wid, *(SI *)data < 0 ? pneg : ppos, *(SI *)data); +p break; #else - case DTINT: - iV = *(INT *)data; - goto intOut; - - case DTSI: - iV = *(SI *)data; - intOut: - Cvnchars = sprintf(str, sif[lj], wid, iV < 0 ? pneg : ppos, iV); - break; + case DTINT: + iV = *(INT*)data; + goto intOut; + + case DTSI: + iV = *(SI*)data; + intOut: + Cvnchars = sprintf( str, sif[lj], wid, iV < 0 ? pneg : ppos, iV); + break; #endif - case DTUINT: - uiV = *(UINT *)data; - goto uintOut; + case DTUINT: + uiV = *(UINT*)data; + goto uintOut; - case DTUSI: - uiV = *(USI *)data; - uintOut: - Cvnchars = sprintf(str, usif[lj], wid, ppos, uiV); - break; + case DTUSI: + uiV = *(USI*)data; + uintOut: + Cvnchars = sprintf( str, usif[lj], wid, ppos, uiV); + break; #ifdef FMTPVMASK - p case DTLI: - p if (*(LI *)data == 0 && pv == FMTPVPLUS) p pv = FMTPVSPACE; - p Cvnchars = sprintf(str, lif[lj][ipv], wid, *(LI *)data < 0 ? pneg : ppos, - *(LI *)data); - p break; +p case DTLI: +p if (*(LI *)data == 0 && pv==FMTPVPLUS) +p pv=FMTPVSPACE; +p Cvnchars = sprintf( str, lif[lj][ipv], wid, *(LI *)data < 0 ? pneg : ppos, *(LI *)data); +p break; #else - case DTLI: - Cvnchars = - sprintf(str, lif[lj], wid, *(LI *)data < 0 ? pneg : ppos, *(LI *)data); - break; + case DTLI: + Cvnchars = sprintf( str, lif[lj], wid, *(LI *)data < 0 ? pneg : ppos, *(LI *)data); + break; #endif - case DTDOY: - data = tddys(*(DOY *)data, TDYRNODOW, NULL); - goto strjust; + case DTDOY: + data = tddys( *(DOY *)data, TDYRNODOW, NULL); + goto strjust; - case DTDOW: - data = tddDowName(*(SI *)data); - goto strjust; + case DTDOW: + data = tddDowName( *(SI *)data); + goto strjust; - case DTMONTH: - data = tddMonAbbrev(*(SI *)data); - goto strjust; + case DTMONTH: + data = tddMonAbbrev( *(SI *)data); + goto strjust; - case DTIDATE: - data = tddis(*(IDATE *)data); - goto strjust; + case DTIDATE: + data = tddis( *(IDATE *)data); + goto strjust; - case DTITIME: - data = tdtis((ITIME *)data, NULL); - goto strjust; + case DTITIME: + data = tdtis( (ITIME *)data, NULL); + goto strjust; - case DTIDATETIME: - data = tddtis((IDATETIME *)data, NULL); - goto strjust; + case DTIDATETIME: + data = tddtis( (IDATETIME *)data, NULL); + goto strjust; #ifdef DTLDATETIME - case DTLDATETIME: - data = tdldts(*((LDATETIME *)data), NULL); - goto strjust; + case DTLDATETIME: + data = tdldts( *((LDATETIME *)data), NULL); + goto strjust; #endif - case DTDBL: - val = *(double *)data; - goto valValue; - -#ifdef DTPERCENT // put back in cndtypes.def to restore code, 12-3-91 - case DTPERCENT: - val = (*(float *)data) * 100.; - mfw--; - if (wid > 1) - wid--; - percent = true; // flag tested after float formatting - goto valValue; // join float + case DTDBL: + val = *(double *)data; + goto valValue; + +#ifdef DTPERCENT // put back in cndtypes.def to restore code, 12-3-91 + case DTPERCENT: + val = (*(float *)data)*100.; + mfw--; + if (wid > 1) + wid--; + percent = true; // flag tested after float formatting + goto valValue; // join float #endif - case DTFLOAT: - floatCase: // number-choice comes here (from default) if does not contain - // choice - { - NANDAT nd = *(NANDAT *)data; - if (!ISNUM(nd)) // check for non-number, cnglob.h macro, debug aid 2-27-92. - { - if (ISNCHOICE(nd)) // if number-choice choice (nan; unexpected here) - goto choiceCase; - if (ISNANDLE(nd)) // if unset or expr n (nan's) (insurance) - { - if (ISUNSET(nd)) - strcpy(str, ""); // say - else - sprintf(str, "", EXN(nd)); // say - break; - } - } - val = *(float *)data; // conver float value to print to double - } - valValue: // double, [percent] join here - val = cvIntoEx(val, units); // convert value to ext units + case DTFLOAT: +floatCase: // number-choice comes here (from default) if does not contain choice + { + NANDAT nd = *(NANDAT *)data; + if (!ISNUM(nd)) // check for non-number, cnglob.h macro, debug aid 2-27-92. + { + if (ISNCHOICE(nd)) // if number-choice choice (nan; unexpected here) + goto choiceCase; + if (ISNANDLE(nd)) // if unset or expr n (nan's) (insurance) + { + if (ISUNSET(nd)) + strcpy(str, ""); // say + else + sprintf(str, "", EXN(nd)); // say + break; + } + } + val = *(float*)data; // conver float value to print to double + } +valValue: // double, [percent] join here + val = cvIntoEx( val, units); // convert value to ext units #ifdef FMTPVMASK - p wsign = !(pv == FMTPVNULL && val >= 0.); // sign width +p wsign = !(pv==FMTPVNULL && val >= 0.); // sign width #else - wsign = (val < 0.); // sign width + wsign = (val < 0.); // sign width #endif - if (units == UNLENGTH && - Unsysext == UNSYSIP) // if feet-inch float/double value - cvFtIn2s(); // convert 'val' to '*str' as feet-inches, local function, - // below. - else - cvDouble2s(); // convert 'val' to '*str', using many other variables. - // local function, below. + if (units == UNLENGTH && Unsysext==UNSYSIP) // if feet-inch float/double value + cvFtIn2s(); // convert 'val' to '*str' as feet-inches, local function, below. + else + cvDouble2s(); // convert 'val' to '*str', using many other variables. local function, below. #ifdef DTPERCENT - if (percent) // finish percent: add '%'; make 1 col wider due to wid-- - // above. - { - SI i; - SI l = strlen(str); - for (i = 0;; i++) - if (*(str + i) != ' ') - break; // find nonspace - while (*(str + (++i))) - if (*(str + i) == ' ') - break; // find space after the nonspace, or end - *(str + i) = '%'; // put % there - if (i != l) - *(str + l) = ' '; // append space unless just put % there - *(str + l + 1) = '\0'; // reterminate - } + if (percent) // finish percent: add '%'; make 1 col wider due to wid-- above. + { + SI i; + SI l = strlen(str); + for (i = 0; ; i++) if (*(str+i) != ' ') break; // find nonspace + while (*(str+(++i))) if (*(str+i)==' ') break; // find space after the nonspace, or end + *(str+i) = '%'; // put % there + if (i != l) *(str+l) = ' '; // append space unless just put % there + *(str+l+1) = '\0'; // reterminate + } #endif -#if defined(M0BUG) - if (Top.tp_date.month == 12 && Top.tp_date.mday == 3 && Top.iHr == 5 && - Top.iSubhr == 1) { // trap subhour where output mismatch occurs - if (strcmp("-0", str) == 0 || strcmp("-1", str) == 0) - printf("\nHit str='%s' val=%f", str, val); - } +#if defined( M0BUG) + if (Top.tp_date.month == 12 && Top.tp_date.mday == 3 && Top.iHr == 5 && Top.iSubhr == 1) + { // trap subhour where output mismatch occurs + if (strcmp("-0", str) == 0 || strcmp("-1", str) == 0) + printf("\nHit str='%s' val=%f", str, val); + } #endif #if 0 x consider changing "-0" to "0"? x if (strcmp("-0", str) == 0) x printf("\n-0"); #endif - break; - - default: // a choice or number-choice data type -- else error - if (dt & DTBCHOICN) // if a number-choice see if now contains number - { - 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 - } - choiceCase: // float types comes here if NCHOICE found (unexpected) - if (dt & (DTBCHOICB | DTBCHOICN)) // if a choice type - { - SI handle = - (dt & DTBCHOICN) - ? CHN(*(void **) - data) // extract choicn choice value (cnglob.h macro) - : *(SI *)data; // choicb choice handle/index -#if 0 // not used in CSE 9-2-91 + break; + + default: // a choice or number-choice data type -- else error + if (dt & DTBCHOICN) // if a number-choice see if now contains number + { + 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 + } +choiceCase: // float types comes here if NCHOICE found (unexpected) + if (dt & (DTBCHOICB|DTBCHOICN)) // if a choice type + { + SI handle = (dt & DTBCHOICN) + ? CHN(*(void **)data) // extract choicn choice value (cnglob.h macro) + : *(SI *)data; // choicb choice handle/index +#if 0 // not used in CSE 9-2-91 x if (dt==DTPERINCH && Unsysext != UNSYSIP) x // fudge inches to mm for per-inch in other system x { if (handle == C_PERINCH_YES) @@ -429,451 +387,425 @@ x else if (handle == C_PERINCH_NO) x handle = C_PERMM_NO; x } #endif - data = ::getChoiTxI(dt, handle); // get choicb/choicn text - goto strjust; - } else // not a choice type - goto undef; + data = ::getChoiTxI( dt, handle); // get choicb/choicn text + goto strjust; + } + else // not a choice type + goto undef; #ifdef DTNA - case DTNA: - data = "--"; - goto strjust; + case DTNA: + data = "--"; + goto strjust; #endif - case DTCULSTR: - data = (*(CULSTR *)data).CStr(); - goto strjust; // data is pointer to string + case DTCULSTR: + data = (*(CULSTR*)data).CStr(); + goto strjust; // data is pointer to string - case DTCH: // for char array or string ptr already dereferenced, rob 11-91 - case DTANAME: // char[ ] RAT name - strjust: - Cvnchars = sprintf(str, sf[lj], wid, mfw, data); - break; + case DTCH: // for char array or string ptr already dereferenced, rob 11-91 + case DTANAME: // char[ ] RAT name +strjust: + Cvnchars = sprintf( str, sf[ lj], wid, mfw, data); + break; #ifdef DTUNDEF - case DTUNDEF: + case DTUNDEF: #endif - undef: - if (wid > 1) - memset(str, ' ', wid); - if (lj) - *str = '?'; - else - *(str + wid - 1) = '?'; - *(str + wid) = '\0'; - Cvnchars = wid; - break; - - } // switch (dt) - - /* if still too wide [and not variable data], fill with *******. Note most - * floating values have been made to fit. */ - - if ((USI)Cvnchars > mfw + _xfw) // return oversize field anyway if <= _xfw - // (argument) chars overwide - { +undef: + if (wid > 1) + memset( str, ' ', wid); + if (lj) + *str = '?'; + else + *(str+wid-1) = '?'; + *(str+wid)='\0'; + Cvnchars = wid; + break; + + } // switch (dt) + + /* if still too wide [and not variable data], fill with *******. Note most floating values have been made to fit. */ + + if ((USI)Cvnchars > mfw + _xfw) // return oversize field anyway if <= _xfw (argument) chars overwide + { #ifdef DTPERCENT - if (percent) { - mfw++; - percent = false; - } + if (percent) + { + mfw++; + percent = false; + } #endif - memset(str, '*', mfw); // fill with ****** - *(str + mfw) = '\0'; - Cvnchars = mfw; - } - - /* optionally add units. Note ' " for ft-in done in cvFtIn2s. Suppressed for - * NCHOICE choice by clearing bits above. */ - - if (fmt & FMTUNITS) { - strcat(str, " "); - strcat(str, UNIT::GetSymbol(units)); // concat units text - } else if (fmt & FMTPU) // parenthesised units for res loads 2-90 - { - strcat(str, "("); - strcat(str, UNIT::GetSymbol(units)); // concat units text - strcat(str, ")"); - } - - return str; - // another return at entry -} // cvin2s + memset( str, '*', mfw); // fill with ****** + *(str + mfw)='\0'; + Cvnchars = mfw; + } + + /* optionally add units. Note ' " for ft-in done in cvFtIn2s. Suppressed for NCHOICE choice by clearing bits above. */ + + if (fmt & FMTUNITS) + { + strcat( str, " "); + strcat( str, UNIT::GetSymbol( units)); // concat units text + } + else if (fmt & FMTPU) // parenthesised units for res loads 2-90 + { + strcat( str, "("); + strcat( str, UNIT::GetSymbol( units)); // concat units text + strcat( str, ")"); + } + + return str; + // another return at entry +} // cvin2s //====================================================================== //--- data shared by cvDouble2s, cvsd, nexK 4-92 -static SI - amfw; // set by cvdd/cvsd: mfw adjusted for sign. used in cvDouble2s, nexK, -static SI - ki; // 0 or number of times divided by 1000 for K format. cvDouble2s inits. -static SI nDigB4Pt; // number of digits b4 point, set in cvsd/cvdd, adj in nexK -static const char ddalpha[] = - " kMGTPEZY?????"; // Chars for K format output field overflow handling +static SI amfw; // set by cvdd/cvsd: mfw adjusted for sign. used in cvDouble2s, nexK, +static SI ki; // 0 or number of times divided by 1000 for K format. cvDouble2s inits. +static SI nDigB4Pt; // number of digits b4 point, set in cvsd/cvdd, adj in nexK +static const char ddalpha[]=" kMGTPEZY?????"; // Chars for K format output field overflow handling //====================================================================== -// 4-92: needs completion of review re producing minimum-overwidth result for -// xfw -- cvsd at least done. +// 4-92: needs completion of review re producing minimum-overwidth result for xfw -- cvsd at least done. -LOCAL void FC cvDouble2s() // float / double output conversion case for cvin2s +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: fmt, 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) - ki = 0; // say not in K format overflow (cvsd/nexK) + SI i; + SI dfw = fmt & FMTDFWMASK; // decimal field width (# decimal places) + ki = 0; // say not in K format overflow (cvsd/nexK) - // zero exception +// zero exception - if (val == 0.) { + if (val==0.) + { #ifdef FMTPVMASK - p if (pv == FMTPVPLUS) // show not + - p pv = FMTPVSPACE; // but space - p Cvnchars = sprintf(str, sif[lj][ipv], wid, ppos, 0); +p if (pv==FMTPVPLUS) // show not + +p pv=FMTPVSPACE; // but space +p Cvnchars = sprintf( str, sif[lj][ipv], wid, ppos, 0); #else - Cvnchars = sprintf(str, sif[lj], wid, ppos, 0); + Cvnchars = sprintf( str, sif[lj], wid, ppos, 0); #endif - return; - } - - // trim trailing zeroes (specified significant digits) option - - if (fmt & 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) - // else fall thru to basic conversion to get 0.000 for small numbers, rather - // than e- format nor overwide .000000n - } // if FMTRTZ - - // basic floating conversion (not ft-in, not 0, not FMTRTZ) - - if (cvdd(mfw, dfw)) // drifting decimal conversion, nz ok. Uses str, val, - // etc; sets amfw; below. - return; // if successful in mfw columns - - // field overflowed (rest of function) - - if ((fmt & 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 - - // 4-92: needs review re producing minimum-overwidth result for xfw. - - // static char ddalpha[]=" kMGTPE???????"; // Chars for K format output is - // above - double maxfit; - - if (amfw < 2) // set by cvdd: mfw adj for sign - return; // if can't possibly fix (and Pten does not go negative) - amfw--; // cols less sign: reduce 1 for kMG.. - maxfit = - Pten[amfw] - 0.5; // protection for amfw > sizeof Pten needed ??? 10-88 - if (wid > 1) // desired width, 1 for FMTSQ - wid--; // leave a col for kMG - for (i = 0;; i++) { - // more robust to give up HERE if i too big ?? 10-88 rob - if ((fabs(val) < maxfit // if now might fit width - && cvdd(mfw - 1, dfw)) || // format it and see - std::isnan(val)) // guard against inf loop - break; // if now ok - val /= 1000.; // divide by 1000 and bump i till it works - } - if (ddalpha[i] == '?') // if out of range of char table - Cvnchars = mfw + 1; // I think this causes ***** output - else { - *(str + Cvnchars) = ddalpha[i]; // append k, M, G, ... - Cvnchars++; - *(str + Cvnchars) = '\0'; - } - } else // cvdd fail and not K format - { - - // float overflow, e format - - // 4-92: needs review re producing minimum-overwidth result for xfw. - - SI owid = wid; // save wid b4 exponent - SI oamfw = amfw; // ditto max field wid less sign - SI ew = 2; // init exponent width - i = 3; // exponent: start at 3 to gain 1 column - val /= 1000.; // adjust value to match exponent - while (1) // until fits or fails - { - if (oamfw - ew < 1) // if exp leaves no place for value - break; // give up - // I think Cvnchars is too big -> ***** on fallthru w exp - if (fabs(val) < - Pten[oamfw - ew]) // if could fit cols - // no protection for amfw > size of Pten?? rob 10-88 - { - if (wid > 1) // (starts 1 for FMTSQ) - wid = owid - ew; // adjust wid: cvdd uses - if (cvdd(mfw - ew, dfw)) // format / if fits - break; // ok, go add exponent - } - val /= 10.; // divide by 10, bump i till it works - if (++i == 9) // exponent 10 - ew++; // requires extra column - // so why ++ at 9 ??? rob 10-88 - } - sprintf(str + Cvnchars, "e%d", i); // add exponent i - Cvnchars += ew; - } - // additional returns above -} // cvDouble2s + return; + } + +// trim trailing zeroes (specified significant digits) option + + if (fmt & 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) + //else fall thru to basic conversion to get 0.000 for small numbers, rather than e- format nor overwide .000000n + } // if FMTRTZ + +// basic floating conversion (not ft-in, not 0, not FMTRTZ) + + if (cvdd( mfw, dfw)) // drifting decimal conversion, nz ok. Uses str, val, etc; sets amfw; below. + return; // if successful in mfw columns + +// field overflowed (rest of function) + + if ((fmt & 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 + + // 4-92: needs review re producing minimum-overwidth result for xfw. + + //static char ddalpha[]=" kMGTPE???????"; // Chars for K format output is above + double maxfit; + + if (amfw < 2) // set by cvdd: mfw adj for sign + return; // if can't possibly fix (and Pten does not go negative) + amfw--; // cols less sign: reduce 1 for kMG.. + maxfit = Pten[amfw]-0.5; // protection for amfw > sizeof Pten needed ??? 10-88 + if (wid > 1) // desired width, 1 for FMTSQ + wid--; // leave a col for kMG + for (i = 0; ; i++) + { + // more robust to give up HERE if i too big ?? 10-88 rob + if ((fabs(val) < maxfit // if now might fit width + && cvdd( mfw-1, dfw)) || // format it and see + std::isnan(val)) // guard against inf loop + break; // if now ok + val /= 1000.; // divide by 1000 and bump i till it works + } + if (ddalpha[i]=='?') // if out of range of char table + Cvnchars = mfw+1; // I think this causes ***** output + else + { + *(str+Cvnchars) = ddalpha[i]; // append k, M, G, ... + Cvnchars++; + *(str+Cvnchars) = '\0'; + } + } + else // cvdd fail and not K format + { + + // float overflow, e format + + // 4-92: needs review re producing minimum-overwidth result for xfw. + + + SI owid = wid; // save wid b4 exponent + SI oamfw = amfw; // ditto max field wid less sign + SI ew = 2; // init exponent width + i = 3; // exponent: start at 3 to gain 1 column + val /= 1000.; // adjust value to match exponent + while (1) // until fits or fails + { + if (oamfw-ew < 1) // if exp leaves no place for value + break; // give up + // I think Cvnchars is too big -> ***** on fallthru w exp + if (fabs(val) < Pten[oamfw-ew]) // if could fit cols + // no protection for amfw > size of Pten?? rob 10-88 + { + if (wid > 1) // (starts 1 for FMTSQ) + wid = owid-ew; // adjust wid: cvdd uses + if (cvdd( mfw-ew, dfw)) // format / if fits + break; // ok, go add exponent + } + val /= 10.; // divide by 10, bump i till it works + if (++i == 9) // exponent 10 + ew++; // requires extra column + // so why ++ at 9 ??? rob 10-88 + } + sprintf( str+Cvnchars, "e%d", i); // add exponent i + Cvnchars += ew; + } + // additional returns above +} // cvDouble2s //====================================================================== -LOCAL void FC cvFtIn2s() // feet-inch length output conversion case for cvin2s +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: fmt, 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 - { - aval = fabs(val); // absolute val - 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 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. -#else // 11-91 quinch coded out as 1 - int ft = sepFtInch(val, inch); // separate/fix feet, inches - bool justInches = (ft == 0 && inch != 0); // true to omit feet + 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 + { + aval = fabs(val); // absolute val + 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 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. +#else // 11-91 quinch coded out as 1 + int ft = sepFtInch( val, inch); // separate/fix feet, inches + bool justInches = (ft == 0 && inch != 0); // true to omit feet #endif - // 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, - { - // dfw is total sig digs, not digits after . - // Reduce indfw to digits to print AFTER DECIMAL in INCHES. - int ndig = 0; - while (indfw > 0 && Pten[ndig++] <= aval) // while value < power of 10 - indfw--; // reduce for each feet digit - // (need no Pten size cks here due to 178000000 ck above) - ndig = 0; - double dinch = abs(double(inch)); - while (indfw > 0 && Pten[ndig++] <= dinch) // while inches < pwr of 10 - indfw--; // reduce for each inches digit b4 . - } - - // printf width/space needed for inches if with feet - - int inw = justInches ? 0 // for inches-only fw is used, else - : 2 // 2 for integral inches (space b4 0-9; exact inw - // needed for rj) .. - - sq // but only 1 col if squeezing (no space b4 - // squeezed 0-9; inw < actual ok when sq on) - + (indfw != 0) // 1 for . if needed - + indfw; // digits after . - - // printf width for feet (or inches if no feet) - - int fw = lj ? 1 // 1 to left-justify, else - : wid - inw // wid less inch space and - - (!justInches) // less ' space and + // 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, + { + // dfw is total sig digs, not digits after . + // Reduce indfw to digits to print AFTER DECIMAL in INCHES. + int ndig = 0; + while (indfw > 0 && Pten[ndig++] <= aval) // while value < power of 10 + indfw--; // reduce for each feet digit + // (need no Pten size cks here due to 178000000 ck above) + ndig = 0; + double dinch = abs(double(inch)); + while (indfw > 0 && Pten[ndig++] <= dinch ) // while inches < pwr of 10 + indfw--; // reduce for each inches digit b4 . + } + + // printf width/space needed for inches if with feet + + int inw = justInches ? 0 // for inches-only fw is used, else + : 2 // 2 for integral inches (space b4 0-9; exact inw needed for rj) .. + - sq // but only 1 col if squeezing (no space b4 squeezed 0-9; inw < actual ok when sq on) + + (indfw!=0) // 1 for . if needed + + indfw; // digits after . + + // printf width for feet (or inches if no feet) + + int fw = lj ? 1 // 1 to left-justify, else + : wid - inw // wid less inch space and + - (!justInches) // less ' space and #ifdef FMTNOQUINCH - x - - quinch; // less " space +x - quinch; // less " space #else - - 1; // less " space + - 1; // less " space #endif - if (fw < 1) // Prevent fw < 0: else trails blanks to - fw = 1; // .. -fw cols. NB wid=1 for FMTSQ (10-88). - int prcsn = lz ? fw - wsign : 1; // min # digits most cases - - // format feet-inches - - if (indfw) // if non-0 # dec places for inches - { - // show with decimal inches - double dinch = 0.; - if (justInches) // if showing inches only - { - dinch = val * 12.; // compute float inches + if (fw < 1) // Prevent fw < 0: else trails blanks to + fw = 1; // .. -fw cols. NB wid=1 for FMTSQ (10-88). + int prcsn = lz ? fw-wsign : 1; // min # digits most cases + + // format feet-inches + + if (indfw) // if non-0 # dec places for inches + { + // show with decimal inches + double dinch = 0.; + if (justInches) // if showing inches only + { + dinch = val*12.; // compute float inches #ifdef FMTPVMASK - p Cvnchars = sprintf(str, ff5[ipv], // *.*f - p fw, indfw, // width, precision - p dinch); // floating inches +p Cvnchars = sprintf( str, ff5[ipv], // *.*f +p fw, indfw, // width, precision +p dinch ); // floating inches #else - Cvnchars = sprintf(str, ff5, // *.*f - fw, indfw, // width, precision - dinch); // floating inches + Cvnchars = sprintf( str, ff5, // *.*f + fw, indfw, // width, precision + dinch ); // floating inches #endif - } else // feet and inches - { - dinch = (val - ft) * ((ft >= 0) ? 12 : -12); + } + else // feet and inches + { + dinch = (val - ft)*((ft >= 0) ? 12 : -12); #ifdef FMTPVMASK - p Cvnchars = sprintf(str, ff4[ipv], // 2.*f for inches - p fw, prcsn, // feet width, digits - p ft, p inw, indfw, // inches wid, precis - p dinch); // floating inches +p Cvnchars = sprintf( str, ff4[ipv], // 2.*f for inches +p fw, prcsn, // feet width, digits +p ft, +p inw, indfw, // inches wid, precis +p dinch ); // floating inches #else - Cvnchars = sprintf(str, ff4, // 2.*f for inches - fw, prcsn, // feet width, digits - ft, inw, indfw, // inches wid, precis - dinch); // floating inches + Cvnchars = sprintf( str, ff4, // 2.*f for inches + fw, prcsn, // feet width, digits + ft, + inw, indfw, // inches wid, precis + dinch ); // floating inches #endif - } - if (fmt & FMTRTZ) // trim trailing zeros option - { - // rob 10-88 to support FTMRTZ with FMTSQ - Cvnchars = cvttz( - str, // trim .0's from decimal inches - '"', // slide final " leftward - lj ? 1 : wid); /* if lj or FMTSQ (wid=1), do fully (lj padded - below). If rj, don't shorten < wid: wd need to pad - at front, defer doing that code til need found. */ - } - } else // indfw 0: show inches as integer only - { -#ifdef FMTPVMASK // define in cvpak.h to restore p positive value display - // options, 11-91 - p p if (justInches) // if showing inches only - p Cvnchars = sprintf(str, ff3[ipv], p fw, prcsn, // inches wid, digits - p inch); - p else // feet and inches - p Cvnchars = sprintf(str, ff1[ipv], // %2d for inches - p fw, prcsn, // feet wid, digits - p ft, - p inw, // inches width - p inch); + } + if (fmt & FMTRTZ) // trim trailing zeros option + { + // rob 10-88 to support FTMRTZ with FMTSQ + Cvnchars = + cvttz( str, // trim .0's from decimal inches + '"', // slide final " leftward + lj ? 1 : wid ); /* if lj or FMTSQ (wid=1), do fully (lj padded below). + If rj, don't shorten < wid: wd need to pad at + front, defer doing that code til need found. */ + } + } + else // indfw 0: show inches as integer only + { +#ifdef FMTPVMASK // define in cvpak.h to restore p positive value display options, 11-91 +p +p if (justInches) // if showing inches only +p Cvnchars = sprintf( str, ff3[ipv], +p fw, prcsn, // inches wid, digits +p inch ); +p else // feet and inches +p Cvnchars = sprintf( str,ff1[ipv], // %2d for inches +p fw, prcsn, // feet wid, digits +p ft, +p inw, // inches width +p inch ); #else - if (justInches) // if showing inches only - Cvnchars = sprintf(str, ff3, fw, prcsn, // inches wid, digits - inch); - else // feet and inches - Cvnchars = sprintf(str, ff1, // %2d for inches - fw, prcsn, // feet wid, digits - ft, - inw, // inches width - inch); + if (justInches) // if showing inches only + Cvnchars = sprintf( str, ff3, + fw, prcsn, // inches wid, digits + inch ); + else // feet and inches + Cvnchars = sprintf( str,ff1, // %2d for inches + fw, prcsn, // feet wid, digits + ft, + inw, // inches width + inch ); #endif - } -#ifdef FMTNOQUINCH // define in cvpak.h to restore feature, 11-91 - 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 { - x if (!justInches // if feet shown - x && - *(str + Cvnchars - 1) == '0' // 0 last - x && - !isdigitW(*(str + Cvnchars - 2))) // nondigit b4 0 - x { - x *(str + --Cvnchars) = '\0'; // truncate the 0 - x // quinch = 0; * add no " * quinch is - // 0 cuz FMTNOQUINCH on if here - x - } - x - } - x x // add " for inches unless suppressed - x x if (quinch /* && Cvnchars < mfw * to omit if won't fit */) x { - *(str + Cvnchars++) = '"'; - x *(str + Cvnchars) = 0; - x - } + } +#ifdef FMTNOQUINCH // define in cvpak.h to restore feature, 11-91 +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 { +x if ( !justInches // if feet shown +x && *(str + Cvnchars-1)=='0' // 0 last +x && !isdigitW(*(str + Cvnchars-2)) ) // nondigit b4 0 +x { +x *(str + --Cvnchars) = '\0'; // truncate the 0 +x // quinch = 0; * add no " * quinch is 0 cuz FMTNOQUINCH on if here +x } +x } +x +x // add " for inches unless suppressed +x +x if (quinch /* && Cvnchars < mfw * to omit if won't fit */) +x { *(str + Cvnchars++) = '"'; +x *(str + Cvnchars) = 0; +x } #else - // add " for inches + // add " for inches - // if (Cvnchars < mfw) {.. // to omit if won't fit - *(str + Cvnchars++) = '"'; - *(str + Cvnchars) = 0; + // if (Cvnchars < mfw) {.. // to omit if won't fit + *(str + Cvnchars++) = '"'; + *(str + Cvnchars) = 0; #endif - // right pad left-justified feet-inch values (wid was 1) + // right pad left-justified feet-inch values (wid was 1) - if (lj) // if left-justified - if ((USI)Cvnchars < mfw) // (beleived unnec) - strpad(str, " ", (Cvnchars = mfw)); // pad to field width + if (lj) // if left-justified + if ((USI)Cvnchars < mfw) // (beleived unnec) + strpad( str," ", (Cvnchars=mfw)); // pad to field width - // finally, check that fit field + // finally, check that fit field - biglen = (USI)Cvnchars > - mfw; // set biglen true iff feet-inches too wide for field + biglen = (USI)Cvnchars > mfw; // set biglen true iff feet-inches too wide for field - } // if (!biglen) + } // if (!biglen) - // if feet-inch value too big, show feet only +// if feet-inch value too big, show feet only - if (biglen) // if value too big for int inches at entry, OR feet-inches format - // exceeded field width - { - cvin2sBuf(str, (char *)&val, // recursive call to do feet only - DTDBL, - UNNONE, // no units - mfw - 1, // save a column for ' - fmt); - *(str + Cvnchars++) = '\''; // add foot mark ' to end - *(str + Cvnchars) = '\0'; - } -} // cvFtIn2s + if (biglen) // if value too big for int inches at entry, OR feet-inches format exceeded field width + { + cvin2sBuf( str, (char *)&val, // recursive call to do feet only + DTDBL, + UNNONE, // no units + mfw-1, // save a column for ' + fmt ); + *(str+Cvnchars++) = '\''; // add foot mark ' to end + *(str+Cvnchars) = '\0'; + } +} // cvFtIn2s //====================================================================== -LOCAL bool FC nexK() // K format incrementer: call each time need to make string - // shorter. false if cannot. +LOCAL bool FC nexK() // K format incrementer: call each time need to make string shorter. false if cannot. -// "K format" means 1000 shortened to 1k, 1234000 shortened to 1.234M or 1.23M -// or 1.2M, etc. see ddalpha[] for chars. +// "K format" means 1000 shortened to 1k, 1234000 shortened to 1.234M or 1.23M or 1.2M, etc. see ddalpha[] for chars. -// alters val (value), ki (assumed init 0), aval, wid, amfw, nDigB4Pt. Note mfw -// is not changed. after completion of formatting, caller must append -// ddalpha[ki] to string if ki > 0. +// alters val (value), ki (assumed init 0), aval, wid, amfw, nDigB4Pt. Note mfw is not changed. +// after completion of formatting, caller must append ddalpha[ki] to string if ki > 0. // returns false with variables unaltered if clearly cannot shrink more. // if true is returned, converted string may not be shorter due to round-up. { - if (!ki) // if not in K format yet - { - if (amfw <= 2) // no way without a digit and k after (note: need 2 digits + - // k to handle all values) - return false; // not enough room, period. - if (fabs(val) < 500.) // 500-->1k: smaller (too misleading?); - // 499.9999-->.5k: no smaller than 499. - return false; // too small to gain from shrinking - amfw--; // first time, reduce field width (after sign) by 1 for the k, M, - // etc - if (wid > 1) - wid--; // unless FMTSQ, reduce sprintf width for the ditto. - } else // already in K format - { - if (fabs(val) < 99.5) // 99.5k -->.1M: smaller than 100k. 99.49999k-->.09M, - // larger than 99k - return false; // too small to shrink more - if (ddalpha[ki + 1] == '?') // if we have used up all the suffix characters - // (ie have divided by about 1e18) - return false; // can't shrink any more, let it **** - } - ki++; // first/next k format trailing character (init -1) - val /= 1000.; // divide value to print by 1000 - aval = fabs(val); // maintain absolute val for some callers - nDigB4Pt -= 3; // 3 less digits before point - setToMax(nDigB4Pt, 0); // .. but not less than 0. - return true; -} // nexK + if (!ki) // if not in K format yet + { + if (amfw <= 2) // no way without a digit and k after (note: need 2 digits + k to handle all values) + return false; // not enough room, period. + if (fabs(val) < 500.) // 500-->1k: smaller (too misleading?); 499.9999-->.5k: no smaller than 499. + return false; // too small to gain from shrinking + amfw--; // first time, reduce field width (after sign) by 1 for the k, M, etc + if (wid > 1) wid--; // unless FMTSQ, reduce sprintf width for the ditto. + } + else // already in K format + { + if (fabs(val) < 99.5) // 99.5k -->.1M: smaller than 100k. 99.49999k-->.09M, larger than 99k + return false; // too small to shrink more + if (ddalpha[ki+1]=='?') // if we have used up all the suffix characters (ie have divided by about 1e18) + return false; // can't shrink any more, let it **** + } + ki++; // first/next k format trailing character (init -1) + val /= 1000.; // divide value to print by 1000 + aval = fabs(val); // maintain absolute val for some callers + nDigB4Pt -= 3; // 3 less digits before point + setToMax( nDigB4Pt, 0); // .. but not less than 0. + return true; +} // nexK //====================================================================== -LOCAL bool FC -cvsd( // Significant-digits (trim trailing 0's, g format) output conversion +LOCAL bool FC cvsd( // Significant-digits (trim trailing 0's, g format) output conversion - SI _mfw, // Maximum field width - SI _dfw) // Desired number of significant digits + SI _mfw, // Maximum field width + SI _dfw ) // Desired number of significant digits // and uses local statics including: // char *str, String into which to store converted value @@ -886,46 +818,32 @@ cvsd( // Significant-digits (trim trailing 0's, g format) output conversion // [ipv] /* returns false if number too small for method used here: - use cvdd to get eg 0.00 for .0003, .001 for 0.0006 in 4 cols, - not .000n (too wide) nor e- format (too wide). returns true if converted. If - overwide, mininum width form returned. Also sets: Cvnchars: Number of - characters output: caller checks for excess width */ + use cvdd to get eg 0.00 for .0003, .001 for 0.0006 in 4 cols, not .000n (too wide) nor e- format (too wide). + returns true if converted. If overwide, mininum width form returned. + Also sets: Cvnchars: Number of characters output: caller checks for excess width */ { - // ki = 0; // K format index, init by - // caller - aval = fabs(val); // absolute val - amfw = _mfw - wsign; // width less sign (public static). wsign is !(val >= 0. - // [&& pv==FMTPVNULL]) - setToMin(_dfw, amfw); // fix turkey calls; reduce for sign. Note may be - // increased to nDigB4Pt below. - -// return false if value too small for width: don't duplicate 0 exception and -// cvdd's small-value formatting. -// eg for 3 cols, need at least .095 to print here as .01; let cvdd do less -//(cvdd prints .005 as .01, .00499 as 0.0). -#define MINHERE \ - 1e-4 // min abs value ever done here, per preferences & sprintf %g limits, was - // hard-coded .001. - // was hard-coded .001 4-92; also tried 1e-5, resulted in %g using e- format. - if (aval < 1) // 1 or greater always done here - if (aval < MINHERE // less than this never handled here - || - aval < - 9.5 * NPten[min(amfw, NPTENSIZE)]) // if too small for g format in - // width even when only 1 sig dig - return false; // examples: amfw min print min val as power of 10 - // 1 1 .95 9.5e-1 - // 2 .1 .095 9.5e-2 etc - - // determine digits left of decimal point. Note nexK adjusts. - for (nDigB4Pt = 0; // count digits before point (b4 rounding) - nDigB4Pt < PTENSIZE && - Pten[nDigB4Pt] <= aval; // Pten: powers of 10, [0]..[15]. cons.c. - nDigB4Pt++) - ; - -#if 0 // idea not used -- leave user the control of seeing eg 99.9k not 99990 by - // spec'ing 4 digits in 6 columns. 4-9-92. + //ki = 0; // K format index, init by caller + aval = fabs(val); // absolute val + amfw = _mfw - wsign; // width less sign (public static). wsign is !(val >= 0. [&& pv==FMTPVNULL]) + setToMin( _dfw, amfw); // fix turkey calls; reduce for sign. Note may be increased to nDigB4Pt below. + +// return false if value too small for width: don't duplicate 0 exception and cvdd's small-value formatting. +// eg for 3 cols, need at least .095 to print here as .01; let cvdd do less (cvdd prints .005 as .01, .00499 as 0.0). +#define MINHERE 1e-4 // min abs value ever done here, per preferences & sprintf %g limits, was hard-coded .001. + // was hard-coded .001 4-92; also tried 1e-5, resulted in %g using e- format. + if (aval < 1) // 1 or greater always done here + if ( aval < MINHERE // less than this never handled here + || aval < 9.5*NPten[min(amfw,NPTENSIZE)] ) // if too small for g format in width even when only 1 sig dig + return false; // examples: amfw min print min val as power of 10 + // 1 1 .95 9.5e-1 + // 2 .1 .095 9.5e-2 etc + +// determine digits left of decimal point. Note nexK adjusts. + for ( nDigB4Pt = 0; // count digits before point (b4 rounding) + nDigB4Pt < PTENSIZE && Pten[nDigB4Pt] <= aval; // Pten: powers of 10, [0]..[15]. cons.c. + nDigB4Pt++ ) ; + +#if 0 // idea not used -- leave user the control of seeing eg 99.9k not 99990 by spec'ing 4 digits in 6 columns. 4-9-92. x// conditionally use more significant digits rather than an overflow format x if ( nDigB4Pt > _dfw // if value needs more digit positions than requested x && nDigB4Pt <= amfw // if field width allows these digit positions @@ -933,145 +851,109 @@ x && nDigB4Pt <= 6 ) // up to a million (rob's judgement) 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 (_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. - while (nDigB4Pt > _dfw && nexK()) - ; // while too many digits b4 ., while possible, divide by 1000 & bump ki. - } - - // for numbers still too big for small field, use enough digits to get the - // least excess width, to fit best in mfw+xfw. - SI idigB4Pt = - nDigB4Pt + - (Pten[nDigB4Pt] <= aval + 0.5); // digits in integer format: may round up - if (idigB4Pt <= 5) // if integer form not wider than e format form "1e+01" - setToMax(_dfw, idigB4Pt); // do 1st convert with at least enuf digits to not - // get e format - - // format number as text, retry if comes out wider than field width - - for (;;) // loop to reduce significant digits and/or do k thing to fit into - // field - { - setToMin(_dfw, max(amfw, idigB4Pt)); // saves an iteration if nexK reduced - // amfW and number clearly fits - - /* pre-round when field width is exactly 1 more than digits b4 point, to fit - values that sprintf itself cannot. example: 999.9, mfw = 4: there is no - dfw that will make sprintf do it for us (dfw=4 -->999.9: 5 cols; dfw=3 - overflows) but sprintf of 1000 with dfw==4 works. */ - double _val = - nDigB4Pt + 1 == amfw // if have 1 column + digits b4 pt (less or more we - // can't fix) - && - nDigB4Pt + 1 == - _dfw // and are printing that # sig digits (less-->e format) - && Pten[nDigB4Pt] <= aval + 0.5 // and rounding adds digit b4 - // point (--dfw loop can't fix) - ? val + 0.5 - : val; // then pre-round value to sprintf, else don't meddle. - - // save an overflow iteration if string is going to contain a decimal point - SI nDigAfPt = _dfw - nDigB4Pt; - if (_dfw == amfw // if enuf digits to fill field specified - && nDigAfPt > 0 // and some digits after point - && - (nDigAfPt != 1 // and value like 999.9 - || Pten[nDigB4Pt] > - aval + 0.5)) // is not going to round to 1000, dropping point - _dfw--; // then drop a digit now, save time of a sprintf +// 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 (_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. + while (nDigB4Pt > _dfw && nexK()) ; // while too many digits b4 ., while possible, divide by 1000 & bump ki. + } + +// for numbers still too big for small field, use enough digits to get the least excess width, to fit best in mfw+xfw. + SI idigB4Pt = nDigB4Pt + (Pten[nDigB4Pt] <= aval + 0.5); // digits in integer format: may round up + if (idigB4Pt <= 5) // if integer form not wider than e format form "1e+01" + setToMax( _dfw, idigB4Pt); // do 1st convert with at least enuf digits to not get e format + +// format number as text, retry if comes out wider than field width + + for ( ; ; ) // loop to reduce significant digits and/or do k thing to fit into field + { + setToMin( _dfw, max(amfw,idigB4Pt)); // saves an iteration if nexK reduced amfW and number clearly fits + + /* pre-round when field width is exactly 1 more than digits b4 point, to fit values that sprintf itself cannot. + example: 999.9, mfw = 4: there is no dfw that will make sprintf do it for us (dfw=4 -->999.9: 5 cols; dfw=3 overflows) + but sprintf of 1000 with dfw==4 works. */ + double _val = nDigB4Pt+1==amfw // if have 1 column + digits b4 pt (less or more we can't fix) + && nDigB4Pt+1==_dfw // and are printing that # sig digits (less-->e format) + && Pten[nDigB4Pt] <= aval + 0.5 // and rounding adds digit b4 point (--dfw loop can't fix) + ? val + 0.5 : val; // then pre-round value to sprintf, else don't meddle. + + // save an overflow iteration if string is going to contain a decimal point + SI nDigAfPt = _dfw - nDigB4Pt; + if ( _dfw==amfw // if enuf digits to fill field specified + && nDigAfPt > 0 // and some digits after point + && ( nDigAfPt != 1 // and value like 999.9 + || Pten[nDigB4Pt] > aval + 0.5 ) ) // is not going to round to 1000, dropping point + _dfw--; // then drop a digit now, save time of a sprintf #ifdef FMTPVMASK - p Cvnchars = sprintf(str, gf[ijust][ipv], wid, _dfw, - _val); // convert number to string (c library) +p Cvnchars = sprintf( str, gf[ijust][ipv], wid, _dfw, _val); // convert number to string (c library) #else - Cvnchars = sprintf(str, gf[ijust], wid, _dfw, - _val); // convert number to string (c library) + Cvnchars = sprintf( str, gf[ijust], wid, _dfw, _val); // convert number to string (c library) #endif - // 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' - break; // normal termination of loop - - // text wider than field (expected for roundup cases) or contains 'e' when k - // format desired - - if (!memcmp(str + wsign, "0.", - 2)) // if begins with "0." (implies following digits) - { - strcpy(str + wsign, - str + wsign + 1); // take out the leading 0 before the . - if (--Cvnchars <= amfw + wsign) // is now 1 char narrower - break; // now it fits! - } - if ((fmt & FMTOVFMASK) == FMTOVFK) // if 'k' overflow format requested - { - if (nDigAfPt > 0 && - _dfw > 3) // first trim digits after point: 123.4k-->.1234M is no gain - { - _dfw--; - continue; // (after can't nexK, final digits after . trimmed below) - } - if (nexK()) - continue; // do a(nother) k format /1000 / if could, format again - } - - // for overwide field, produce mininimum possible width for max chance of - // fitting mfw+xfw columns - - /* don't reduce dfw if further reduction would go to e format if this would - widen field. Shortest string is "1e+01", or dfw==nDigB4Pt for val < - 9999.5 Stop at dfw==nDigB4Pt if nDigB4Pt < 5, or at dfw==nDigB4Pt+1 if - value rounds up. */ - - if (_dfw <= 1) - break; // never go to 0 digits - - if (nDigAfPt > 0 // if there are digits AFTER point - || amfw >= 5 // or space big enuf to hold e format: "1e+01" - || _dfw > 5 // or value still wider than 1-digit e format - || _dfw > nDigB4Pt // or number is not yet as narrow as it can get - // (integer format) - + (Pten[nDigB4Pt] <= - aval + 0.5)) // this term is 1 if number will round - // up to added digit - _dfw--; // drop one sig digit and retry - else - break; // stop, can't make it any narrower - } - - // append 'k', 'M', 'G', etc if k overflow format used - - if (ki) { - *(str + Cvnchars) = ddalpha[ki]; // append k, M, G, ... - Cvnchars++; - *(str + Cvnchars) = '\0'; - } - - return true; // true: we did format the number (see small aval false return - // above) -} // cvsd + // 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' + break; // normal termination of loop + + // text wider than field (expected for roundup cases) or contains 'e' when k format desired + + if (!memcmp(str+wsign,"0.",2)) // if begins with "0." (implies following digits) + { + strcpy( str+wsign, str+wsign+1); // take out the leading 0 before the . + if (--Cvnchars <= amfw+wsign) // is now 1 char narrower + break; // now it fits! + } + if ((fmt & FMTOVFMASK)==FMTOVFK) // if 'k' overflow format requested + { + if (nDigAfPt > 0 && _dfw > 3) // first trim digits after point: 123.4k-->.1234M is no gain + { + _dfw--; + continue; // (after can't nexK, final digits after . trimmed below) + } + if (nexK()) continue; // do a(nother) k format /1000 / if could, format again + } + + // for overwide field, produce mininimum possible width for max chance of fitting mfw+xfw columns + + /* don't reduce dfw if further reduction would go to e format if this would widen field. + Shortest string is "1e+01", or dfw==nDigB4Pt for val < 9999.5 + Stop at dfw==nDigB4Pt if nDigB4Pt < 5, or at dfw==nDigB4Pt+1 if value rounds up. */ + + if (_dfw <= 1) break; // never go to 0 digits + + if ( nDigAfPt > 0 // if there are digits AFTER point + || amfw >= 5 // or space big enuf to hold e format: "1e+01" + || _dfw > 5 // or value still wider than 1-digit e format + || _dfw > nDigB4Pt // or number is not yet as narrow as it can get (integer format) + + (Pten[nDigB4Pt] <= aval + 0.5) ) // this term is 1 if number will round up to added digit + _dfw--; // drop one sig digit and retry + else + break; // stop, can't make it any narrower + } + +// append 'k', 'M', 'G', etc if k overflow format used + + if (ki) + { + *(str+Cvnchars) = ddalpha[ki]; // append k, M, G, ... + Cvnchars++; + *(str+Cvnchars) = '\0'; + } + + return true; // true: we did format the number (see small aval false return above) +} // cvsd //====================================================================== // 4-92: needs review re producing minimum-overwidth result for xfw. -LOCAL bool FC cvdd( // Drifting decimal output conversion +LOCAL bool FC cvdd( // Drifting decimal output conversion - SI _mfw, // Maximum field width - SI _dfw) /* Decimal field width: # digits after point, - also, if non-0, # sig digits for numbers < .1 (digits after . - conditionally increased) */ + SI _mfw, // Maximum field width + SI _dfw ) /* Decimal field width: # digits after point, + also, if non-0, # sig digits for numbers < .1 (digits after . conditionally increased) */ // and uses local statics including: // char *str, String into which to store converted value // double val Value to convert @@ -1082,619 +964,573 @@ LOCAL bool FC cvdd( // Drifting decimal output conversion /* Returns 1 if conversion was successful, 0 otherwise. Also sets: Cvnchars (public): Number of characters output - amfw (static SI): _mfw adjusted for sign, used by caller re field - overflow handling */ + amfw (static SI): _mfw adjusted for sign, used by caller re field overflow handling */ { -#ifdef FMTPVMASK // define in cvpak.h to restore p positive value display - // options, 11-91 - p p // These format definitions must match definitions of FMTPVxxx in cvpak.h - p // null + ' ' if positive - p static char *ff[4][3] = { - "%-*.*f", "%-+*.*f", "%- *.*f", // left just - p "%*.*f", "%+*.*f", "% *.*f", // rt - p "%0*.*f", "%0+*.*f", "%0 *.*f", // rt zeroes - p "%*.*f", "%+*.*f", "% *.*f" p}; // squeeze +#ifdef FMTPVMASK // define in cvpak.h to restore p positive value display options, 11-91 +p +p // These format definitions must match definitions of FMTPVxxx in cvpak.h +p // null + ' ' if positive +p static char *ff[4][3] = { "%-*.*f", "%-+*.*f", "%- *.*f", // left just +p "%*.*f", "%+*.*f", "% *.*f", // rt +p "%0*.*f", "%0+*.*f", "%0 *.*f", // rt zeroes +p "%*.*f", "%+*.*f", "% *.*f" +p }; // squeeze #else - static char *ff[4] = { - "%-*.*f", // left just - "%*.*f", // rt - "%0*.*f", // rt zeroes - "%*.*f", - }; // squeeze + static char *ff[4] = { "%-*.*f", // left just + "%*.*f", // rt + "%0*.*f", // rt zeroes + "%*.*f", + }; // squeeze #endif - aval = fabs(val); // absolute val - amfw = _mfw - - wsign; // width less sign. wsign is !(val >= 0. [&& pv==FMTPVNULL]) - - // compute precision (digits after point) to fit field, in range 0 to - // (possibly increased) _dfw - - int _nDigB4Pt = 0; // local - while (_nDigB4Pt < PTENSIZE && - Pten[_nDigB4Pt] <= aval) // Pten: powers of 10, [0]..[15]. cons.c. - _nDigB4Pt++; // digits needed for magnitude left of . - int prcsn = amfw - _nDigB4Pt - 1; // precision: space avail for digits after . - if (prcsn <= 0) - prcsn = 0; - else // prcsn > 0: room for 1 or more digits after point - { - /* attempt to maintain dfw significant digits for numbers smaller than .1: - conditionally add a few digits after point, 1-92. goals: show more of - small numbers in typical report colums (dfw 1-3), but not as much as for - large numbers. numbers too small for significance to show: don't crowd - page too much with 0.0000's. don't add lots of digits to small export - numbers (FMTRTZ gets here when < .001, dfw 6 already so) */ - - if (_dfw > - 0) /* don't go into decimal places if none requested (take dfw==0 as 0 -sig dig here) -(??? or, do "if (!_dfw) _dfw++;" at foot of loop to get a sig dig to show) */ - { - int maxDfw = min(prcsn, _dfw + 3); // don't exceed space avail; don't add - // > 3 places here (shd that be 2?) - maxDfw = min(maxDfw, 7); // increase only to 7 dec places: avoid garbage - // detail; don't exceed NPten[]. - if (aval < NPten[maxDfw] * .5) // if only 0's would show anyway even after - // round, don't crowd: - maxDfw = - min(maxDfw - 1, /* add 1 less digit here (re 7 or +3 limits), and */ - prcsn + wsign - 2); // leave space for sign even if > 0, here - // only. CAUTION: may make maxDfw < 0. - - for (int n0afPt = 1; _dfw < maxDfw; - _dfw++, n0afPt++) // increase _dfw til dfw sig digits show or to - // maxDfw limit - { - // test if enuf significant digits will (now) show - if (aval >= NPten[n0afPt]) // if aval > .1 at entry, > .01 next - // iteration, then .001, .0001, etc. - break; // stop if have added a digit for each leading 0 after point - - /*if (!_dfw) _dfw++ if get here with 0 _dfw, do - extra ++ for 1 sig dig. else dfw==0 means "0 sig dig" and only - leading 0's after . wd show. */ - } - } - if (prcsn > _dfw) - prcsn = _dfw; // no more than _dfw digits after point - } - - // convert / retry if too wide - - while (1) { + aval = fabs(val); // absolute val + amfw = _mfw - wsign; // width less sign. wsign is !(val >= 0. [&& pv==FMTPVNULL]) + +// compute precision (digits after point) to fit field, in range 0 to (possibly increased) _dfw + + int _nDigB4Pt = 0; // local + while (_nDigB4Pt < PTENSIZE && Pten[_nDigB4Pt] <= aval) // Pten: powers of 10, [0]..[15]. cons.c. + _nDigB4Pt++; // digits needed for magnitude left of . + int prcsn = amfw - _nDigB4Pt - 1; // precision: space avail for digits after . + if (prcsn <= 0) + prcsn = 0; + else // prcsn > 0: room for 1 or more digits after point + { + /* attempt to maintain dfw significant digits for numbers smaller than .1: conditionally add a few digits after point, 1-92. + goals: show more of small numbers in typical report colums (dfw 1-3), but not as much as for large numbers. + numbers too small for significance to show: don't crowd page too much with 0.0000's. + don't add lots of digits to small export numbers (FMTRTZ gets here when < .001, dfw 6 already so) */ + + if (_dfw > 0) /* don't go into decimal places if none requested (take dfw==0 as 0 sig dig here) + (??? or, do "if (!_dfw) _dfw++;" at foot of loop to get a sig dig to show) */ + { + int maxDfw = min( prcsn, _dfw + 3); // don't exceed space avail; don't add > 3 places here (shd that be 2?) + maxDfw = min( maxDfw, 7); // increase only to 7 dec places: avoid garbage detail; don't exceed NPten[]. + if (aval < NPten[maxDfw] * .5) // if only 0's would show anyway even after round, don't crowd: + maxDfw = min( maxDfw - 1, /* add 1 less digit here (re 7 or +3 limits), and */ + prcsn + wsign - 2); // leave space for sign even if > 0, here only. CAUTION: may make maxDfw < 0. + + for (int n0afPt = 1; _dfw < maxDfw; _dfw++, n0afPt++) // increase _dfw til dfw sig digits show or to maxDfw limit + { + // test if enuf significant digits will (now) show + if (aval >= NPten[n0afPt]) // if aval > .1 at entry, > .01 next iteration, then .001, .0001, etc. + break; // stop if have added a digit for each leading 0 after point + + /*if (!_dfw) _dfw++ if get here with 0 _dfw, do extra ++ for 1 sig dig. + else dfw==0 means "0 sig dig" and only leading 0's after . wd show. */ + } + } + if (prcsn > _dfw) + prcsn = _dfw; // no more than _dfw digits after point + } + +// convert / retry if too wide + + while (1) + { #ifdef FMTPVMASK - p Cvnchars = sprintf(str, ff[ijust][ipv], wid, prcsn, val); // convert +p Cvnchars = sprintf( str, ff[ijust][ipv], wid, prcsn, val); // convert #else - Cvnchars = sprintf(str, ff[ijust], wid, prcsn, val); // convert (C library) + Cvnchars = sprintf( str, ff[ijust], wid, prcsn, val); // convert (C library) #endif - // test if fits (allowing positive # to overflow into - position) - if (Cvnchars <= _mfw) - return 1; // return if fits _mfw - // too wide. If number < 1.0, sprintf output "0.xxx". ndig did not allow - // for 0 b4 point. Remove it. - char *zp; - if (_nDigB4Pt == 0 && - *(zp = (str + Cvnchars - prcsn - 2)) == '0') // if of format 0.xxx - { - strcpy(zp, zp + 1); // remove leading 0 from 0.xxx. - Cvnchars--; - break; // prcsn already as small as possible here, right? - } - // too wide... reduce digits after point (perhaps sprintf rounded up). - if (--prcsn < 0) - break; // done if was already at 0 digits after . - } - return (Cvnchars <= _mfw); // 1 if now fits -} // cvdd + // test if fits (allowing positive # to overflow into - position) + if (Cvnchars <= _mfw) + return 1; // return if fits _mfw + // too wide. If number < 1.0, sprintf output "0.xxx". ndig did not allow for 0 b4 point. Remove it. + char* zp; + if (_nDigB4Pt==0 && *(zp=(str+Cvnchars-prcsn-2))=='0') // if of format 0.xxx + { + strcpy( zp, zp+1); // remove leading 0 from 0.xxx. + Cvnchars--; + break; // prcsn already as small as possible here, right? + } + // too wide... reduce digits after point (perhaps sprintf rounded up). + if (--prcsn < 0) + break; // done if was already at 0 digits after . + } + return (Cvnchars <= _mfw); // 1 if now fits +} // cvdd //====================================================================== -LOCAL SI FC cvttz(char *_str, SI trailChar, SI minWid) +LOCAL SI FC cvttz( char *_str, SI trailChar, SI minWid) // trim trailing 0's after point, and point, but not shorter than minWid // returns new length -// called from cvin2s to trim decimal inches 10-88; might be useful re other -// floats types if reimplementing. +// called from cvin2s to trim decimal inches 10-88; might be useful re other floats types if reimplementing. { - char *p, *pend, *q; - SI len; - - len = (SI)strlen(_str); - if (len <= minWid) - return len; // done if not shortenable - p = _str + len; // point end - while (p > _str && *(p - 1) == ' ') // (actual input probably has no .. - p--; // trailSpaces, could shorten code) - if (p > _str && *(p - 1) == (char)trailChar) // if given " etc is at end - { - p--; - minWid--; // trim b4 it, then restore - } else - trailChar = 0; // say no char to put back at end - while (p > _str && *(p - 1) == ' ') // (actual input probably has no .. - p--; // trailSpaces, could shorten code) - pend = p; // 1st trailing space (else null): truncate to here even if no .000 - while (p > _str && *(p - 1) == '0') - p--; // back over trail zeroes - // can only truncate 0's if after point - if (p > _str) { - if (*(p - 1) == '.') // if point precedes first trail0 - pend = p - 1; // chop zeroes and also the point - else // other digits between . and 0s ok - { - for (q = p - 1; q > _str && isdigitW(*q); q--) - ; // back over digits b4 0's - if (*q == '.') // if point b4 the digits - pend = p; // chop the 0's, not other digits - } - } - if (pend < _str + minWid) // don't make < minWid - pend = _str + minWid; // nb minWid checked for < len above - if (trailChar) - *pend++ = (char)trailChar; // move " or other ending thing - *pend = (char)0; // truncate - return (SI)strlen(_str); // new length. another return above. -} // cvttz + char *p, *pend, *q; + SI len; + + len = (SI)strlen(_str); + if (len <= minWid) + return len; // done if not shortenable + p = _str + len; // point end + while (p > _str && *(p-1)==' ') // (actual input probably has no .. + p--; // trailSpaces, could shorten code) + if (p > _str && *(p-1)==(char)trailChar) // if given " etc is at end + { + p--; + minWid--; // trim b4 it, then restore + } + else + trailChar = 0; // say no char to put back at end + while (p > _str && *(p-1)==' ') // (actual input probably has no .. + p--; // trailSpaces, could shorten code) + pend = p; // 1st trailing space (else null): truncate to here even if no .000 + while (p > _str && *(p-1)=='0') + p--; // back over trail zeroes + // can only truncate 0's if after point + if (p > _str) + { + if (*(p-1)=='.') // if point precedes first trail0 + pend = p-1; // chop zeroes and also the point + else // other digits between . and 0s ok + { + for (q = p-1; q > _str && isdigitW(*q); q--) + ; // back over digits b4 0's + if (*q == '.') // if point b4 the digits + pend = p; // chop the 0's, not other digits + } + } + if (pend < _str + minWid) // don't make < minWid + pend = _str + minWid; // nb minWid checked for < len above + if (trailChar) + *pend++ = (char)trailChar; // move " or other ending thing + *pend = (char)0; // truncate + return (SI)strlen(_str); // new length. another return above. +} // cvttz //---------------------------------------------------------------------- -template static RC LimitCheck(T v, int limit) { - RC rc = RCOK; - switch (limit) { - case LMLEZ: - if (v > 0) - rc = MH_V0022; - break; - case LMLZ: - if (v >= 0) - rc = MH_V0026; - break; - case LMNZ: - if (v == 0) - rc = MH_V0025; - break; - case LMGZ: - if (v <= 0) - rc = MH_V0024; - break; - case LMGEZ: - if (v < 0) - rc = MH_V0023; - break; - case LMG1: - if (v <= T(1)) - rc = MH_V0028; - break; - case LMGE1: - if (v < T(1)) - rc = MH_V0027; - break; - case LMFR: - if (v < 0 || v > T(1)) - rc = MH_V0031; - break; - case LMFGZ: - if (v <= 0 || v > T(1)) - rc = MH_V0032; - break; - case LMDOY: - if (v < T(1) || v > T(365)) - rc = MH_V0034; - break; - - default: - err(PWRN, "LimitCheck: missing case for limit %d", limit); - } - return rc; - -} // LimitCheck +template< typename T> static RC LimitCheck(T v, int limit) +{ + RC rc = RCOK; + switch (limit) + { + case LMLEZ: + if (v > 0) rc = MH_V0022; + break; + case LMLZ: + if (v >= 0) rc = MH_V0026; + break; + case LMNZ: + if (v == 0) rc = MH_V0025; + break; + case LMGZ: + if (v <= 0) rc = MH_V0024; + break; + case LMGEZ: + if (v < 0) rc = MH_V0023; + break; + case LMG1: + if (v <= T(1)) rc = MH_V0028; + break; + case LMGE1: + if (v < T(1)) rc = MH_V0027; + break; + case LMFR: + if (v < 0 || v > T(1)) rc = MH_V0031; + break; + case LMFGZ: + if (v <= 0 || v > T(1)) rc = MH_V0032; + break; + case LMDOY: + if (v < T(1) || v > T(365)) rc = MH_V0034; + break; + + default: + err(PWRN, "LimitCheck: missing case for limit %d", limit); + } + return rc; + +} // LimitCheck //====================================================================== -RC - FC - cvLmCk( // Check input data for being within limits for data and limit type +RC FC cvLmCk( // Check input data for being within limits for data and limit type - SI dt, // Data type (dtypes.h define, from dtypes.def by rcdef.exe) - SI limit, // Limit type (dtlims.h define, from limits.def by rcdef.exe) - void *p) // pointer to data to check (points to ptr for string types) + SI dt, // Data type (dtypes.h define, from dtypes.def by rcdef.exe) + SI limit, // Limit type (dtlims.h define, from limits.def by rcdef.exe) + void *p ) // pointer to data to check (points to ptr for string types) // returns RCOK if ok, or message code specifying the error. { - RC rc = RCOK; - - if (limit == LMNONE) - return rc; - - switch (dt) { - { - int iV; - - case DTDOY: - iV = *(DOY *)p; - goto iVCheck; - case DTSI: - iV = *(SI *)p; - goto iVCheck; - case DTINT: - iV = *(INT *)p; - iVCheck: - rc = LimitCheck(iV, limit); - break; - } - - { - UINT uiV; - - case DTUSI: - uiV = *(USI *)p; - goto uiVCheck; - case DTUINT: - uiV = *(UINT *)p; - uiVCheck: - rc = LimitCheck(uiV, limit); - break; - } - - { - double dV; - - case DTFLOAT: - dV = *(FLOAT *)p; - goto dvCheck; - case DTDBL: - dV = *(DBL *)p; - dvCheck: - rc = LimitCheck(dV, limit); - break; - } - - // case DTLI: // DTLI not supported as input data - default: - err(PWRN, "cvLmCk: unsupported DT=%d", dt); - } - - return rc; -} // cvLmCk + RC rc = RCOK; + + if (limit == LMNONE) + return rc; + + switch (dt) + { + { int iV; + + case DTDOY: + iV = *(DOY*)p; + goto iVCheck; + case DTSI: + iV = *(SI*)p; + goto iVCheck; + case DTINT: + iV = *(INT*)p; + iVCheck: + rc = LimitCheck(iV, limit); + break; + } + + { UINT uiV; + + case DTUSI: + uiV = *(USI*)p; + goto uiVCheck; + case DTUINT: + uiV = *(UINT*)p; + uiVCheck: + rc = LimitCheck(uiV, limit); + break; + } + + { double dV; + + case DTFLOAT: + dV = *(FLOAT*)p; + goto dvCheck; + case DTDBL: + dV = *(DBL*)p; + dvCheck: + rc = LimitCheck(dV, limit); + break; + } + + // case DTLI: // DTLI not supported as input data + default: + err(PWRN, "cvLmCk: unsupported DT=%d", dt); + } + + return rc; +} // cvLmCk //====================================================================== -double FC cvExtoIn( // Convert value from external units to internal +double FC cvExtoIn( // Convert value from external units to internal - double f, // value to be converted - int units) // unit type: UNxxx defines from units.h generated by rcdef.exe - // from data\units.def + double f, // value to be converted + int units ) // unit type: UNxxx defines from units.h generated by rcdef.exe from data\units.def // call only for DTFLOAT, DTDBL, or DTPERCENT value // Returns converted value { - if (units != UNNONE) { - f /= UNIT::GetFact(units); // divide by units' scale factor (below) - if (units == UNTEMP && Unsysext == UNSYSSI) // if temperature - f += - 32.0; // complete C to F adjusment -- ONLY units where 0 point changes - } - return f; -} // cvExtoIn + if (units != UNNONE) + { + f /= UNIT::GetFact( units); // divide by units' scale factor (below) + if (units == UNTEMP && Unsysext == UNSYSSI) // if temperature + f += 32.0; // complete C to F adjusment -- ONLY units where 0 point changes + } + return f; +} // cvExtoIn //====================================================================== -double FC cvIntoEx( // Convert value from internal units to external +double FC cvIntoEx( // Convert value from internal units to external - double f, // value to be converted - int units) // unit type + double f, // value to be converted + int units ) // unit type // call only for DTFLOAT, DTDBL, (DTPERCENT), // returns converted value { - if (units != UNNONE) // if it has units - { - if (units == UNANGLE) // if angle - f = cvstdangle(f); // normalize to 0..2*Pi - f *= UNIT::GetFact(units); // get factor (below), conv to extnl - if (units == UNTEMP // if temperature - && Unsysext == UNSYSSI) // and metric - f -= 17.77777778; // adjust zero point - } - return f; -} // cvIntoEx + if (units != UNNONE) // if it has units + { + if (units == UNANGLE) // if angle + f = cvstdangle(f); // normalize to 0..2*Pi + f *= UNIT::GetFact( units); // get factor (below), conv to extnl + if (units == UNTEMP // if temperature + && Unsysext == UNSYSSI) // and metric + f -= 17.77777778; // adjust zero point + } + return f; +} // cvIntoEx //====================================================================== -double FC cvstdangle( // Normalize an angle into 0 to 2*PI range +double FC cvstdangle( // Normalize an angle into 0 to 2*PI range - double ang) // Input angle to be normalized (radians) + double ang ) // Input angle to be normalized (radians) -/* DO NOT use in high precision situations without consideration, cuz if ang > - 2PI-.001, it is changed to 0. Other such "snaps" may be added in the future. - */ +/* DO NOT use in high precision situations without consideration, cuz if ang > 2PI-.001, it is changed to 0. + Other such "snaps" may be added in the future. */ // Returns equivalent angle that falls in range 0 - 2*PI { - if (ang >= k2Pi) // if 2*Pi or greater - return fmod(ang, k2Pi); // take mod 2 * Pi - - /* If negative, want to be independent of what fmod does if < 0. - Values 0. <= ang < k2Pi fall thru and are returned */ - while (ang < 0.0) // until positive - ang += k2Pi; // add 2 * Pi - if (ang > (k2Pi - .001)) - ang = 0.; // If angle is close to 2PI, change it to 0 so it won't display as - // 360. - return ang; -} // cvstdangle + if (ang >= k2Pi) // if 2*Pi or greater + return fmod( ang, k2Pi ); // take mod 2 * Pi + + /* If negative, want to be independent of what fmod does if < 0. + Values 0. <= ang < k2Pi fall thru and are returned */ + while (ang < 0.0) // until positive + ang += k2Pi; // add 2 * Pi + if (ang > (k2Pi - .001)) + ang = 0.; // If angle is close to 2PI, change it to 0 so it won't display as 360. + return ang; +} // cvstdangle // made local and renamed from cvftinch, 2-91: //============================================================================ -LOCAL int FC sepFtInch( // Break up length into feet and inches +LOCAL int FC sepFtInch( // Break up length into feet and inches - double d, // Length to be broken up (float or double feet) - int &inch) // return: inches + double d, // Length to be broken up (float or double feet) + int& inch ) // return: inches // Returns integral feet as fcn value, plus integral inches via *inp. -// If d < 0, feet are returned negative; inches are returned positive unless -// feet are 0. Since d is converted to int inches, the largest distance which -// can be converted is around 178,956,970 ft (about 33,893 miles; I know you're -// disappointed). +// If d < 0, feet are returned negative; inches are returned positive unless feet are 0. +// Since d is converted to int inches, the largest distance which can be converted is around 178,956,970 ft +// (about 33,893 miles; I know you're disappointed). { - int nd = d < 0.0 ? -12 : 12; - int totin = (int)(d * (double)nd + 0.5); // total inches, pos, rounded - int ft = totin / nd; // feet, signed - inch = (ft == 0 && d < 0.) ? -totin : totin % 12; // inches - return ft; -} // sepFtInch + int nd = d < 0.0 ? -12 : 12; + int totin = (int)( d*(double)nd + 0.5 ); // total inches, pos, rounded + int ft = totin/nd; // feet, signed + inch = (ft==0 && d < 0.) ? -totin : totin%12; // inches + return ft; +} // sepFtInch //====================================================================== -int getChoiTxTyX(const char *chtx) // categorize choice text +int getChoiTxTyX( const char* chtx) // categorize choice text { - int tyX = *chtx == '*' ? chtyHIDDEN // hidden - : *chtx == '!' ? chtyALIAS // alias - : *chtx == '~' ? chtyALIASDEP // deprecated alias - : chtyNORMAL; - return tyX; + int tyX = *chtx == '*' ? chtyHIDDEN // hidden + : *chtx == '!' ? chtyALIAS // alias + : *chtx == '~' ? chtyALIASDEP // deprecated alias + : chtyNORMAL; + return tyX; } //====================================================================== -const char *getChoiTxI( // text for choice of choice data type - USI dt, // data type (for bits and choicb Dttab index) - SI chan, // choicb index (use CHN(float), cnglob.h, to extract hi word of - // choicn) - // (1 based) - int *pTyX /*=NULL*/, // NULL or receives choice type - // chtyNORMAL, chtyHIDDEN - int options /*=0*/) // option bits - // 1: nz = get entire string (including any aliases) - // 0 = get primary (1st) text only +const char* getChoiTxI( // text for choice of choice data type + USI dt, // data type (for bits and choicb Dttab index) + SI chan, // choicb index (use CHN(float), cnglob.h, to extract hi word of choicn) + // (1 based) + int* pTyX/*=NULL*/, // NULL or receives choice type + // chtyNORMAL, chtyHIDDEN + int options /*=0*/) // option bits + // 1: nz = get entire string (including any aliases) + // 0 = get primary (1st) text only // note: return value may be TmpStr { - if (dt & (DTBCHOICB | DTBCHOICN)) // verify that is a choice type -- debug aid - { - // check for out of range or garbage value -- debug aid - if (dt & DTBCHOICN) // for choicn, chan may be hi word of float, including - // bits to make a NAN - if ((chan & 0xff80) == - NCNAN) // if proper NAN bits (NCNAN: 7f80, cnglob.h), not just garbage - chan &= ~NCNAN; // remove them for check. But leave improper bits to - // evoke error msg. - if (chan <= 0 || - chan > GetDttab(dt).nchoices) // check that choice is in range 1 to # - // choices for dt. GetDttab: srd.h. - { - err(PWRN, MH_V0036, chan, dt); // display program error err msg - // "cvpak:getChoiTx(): choice %d out of range for dt 0x%x" - return "bad choice"; - } - - // access text - const char *chtx = - GetChoiceText(dt, chan); // access text for fcn. srd.h fcn. - - // return info - if (!(options & 1)) { // retrieve 1st piece to TmpStr - const char *p = chtx; - chtx = strxtok(NULL, p, "|", true); - } - int tyX = getChoiTxTyX(chtx); - if (tyX > chtyNORMAL) - chtx++; // drop prefix - if (pTyX) - *pTyX = tyX; - return chtx; - } - err(PWRN, MH_V0037, dt); // program (internal) err msg - // "cvpak:getChoiTx(): given data type 0x%x not a choice type" - return "bad dt"; -} // getChoiTxI + if (dt & (DTBCHOICB|DTBCHOICN)) // verify that is a choice type -- debug aid + { + // check for out of range or garbage value -- debug aid + if (dt & DTBCHOICN) // for choicn, chan may be hi word of float, including bits to make a NAN + if ((chan & 0xff80)==NCNAN) // if proper NAN bits (NCNAN: 7f80, cnglob.h), not just garbage + chan &= ~NCNAN; // remove them for check. But leave improper bits to evoke error msg. + if (chan <= 0 || chan > GetDttab(dt).nchoices) // check that choice is in range 1 to # choices for dt. GetDttab: srd.h. + { + err( PWRN, MH_V0036, chan, dt ); // display program error err msg + // "cvpak:getChoiTx(): choice %d out of range for dt 0x%x" + return "bad choice"; + } + + // access text + const char* chtx = GetChoiceText( dt, chan); // access text for fcn. srd.h fcn. + + // return info + if (!(options&1)) + { // retrieve 1st piece to TmpStr + const char* p = chtx; + chtx = strxtok( NULL, p, "|", true); + } + int tyX = getChoiTxTyX( chtx); + if (tyX > chtyNORMAL) + chtx++; // drop prefix + if (pTyX) + *pTyX = tyX; + return chtx; + } + err( PWRN, MH_V0037, dt ); // program (internal) err msg + // "cvpak:getChoiTx(): given data type 0x%x not a choice type" + return "bad dt"; +} // getChoiTxI //====================================================================== -RC FC cvS2Choi( // convert string to choice value for given data type else - // format (do not display) error msg - const char *s, // string to convert - USI dt, // choicb or choicn data type (dtypes.h) to convert: specifies - // choice strings in Dttab[]. - void *pv, // NULL or receives choice value: 2 bytes for choicb, 4 bytes (hi - // 2 significant) for choicn. - USI *pSz, // NULL or receives size: 2 or 4 - MSGORHANDLE - *pms) // if non-NULL - // string not found: receives ptr to Tmpstr message insert: "%s - // not one of xxx yyy zzz ..." string=alias: receives ptr to - // deprecation Tmpstr message else receives NULL +RC FC cvS2Choi( // convert string to choice value for given data type else format (do not display) error msg + const char *s, // string to convert + USI dt, // choicb or choicn data type (dtypes.h) to convert: specifies choice strings in Dttab[]. + void* pv, // NULL or receives choice value: 2 bytes for choicb, 4 bytes (hi 2 significant) for choicn. + USI* pSz, // NULL or receives size: 2 or 4 + MSGORHANDLE* pms ) // if non-NULL + // string not found: receives ptr to Tmpstr message insert: "%s not one of xxx yyy zzz ..." + // string=alias: receives ptr to deprecation Tmpstr message + // else receives NULL // returns RCOK if ok // RCBAD with *pms set on error // RCBAD2 with *pms set on info/warning (use of alias,) { - if (pms) - *pms = nullptr; // init to no message - if (dt & (DTBCHOICB | DTBCHOICN)) // if a choice type - { - // search this choice data type's strings for a match, using getChoiTxI - // (just above). - int v; - for (v = 1; v <= GetDttab(dt).nchoices; - v++) // loop data type's choices (GetDttab: srd.h) - { - int tyX; - const char *chtx = getChoiTxI(dt, v, &tyX, 1); - if (tyX == chtyHIDDEN) - continue; // hidden, cannot match - - const char *p = chtx; - while (1) { - chtx = strxtok(NULL, p, "|", true); - if (!chtx) - break; - tyX = getChoiTxTyX(chtx); - if (tyX > chtyNORMAL) - chtx++; // drop prefix if any - if (_stricmp(s, chtx)) - continue; - if (dt & DTBCHOICN) // for choice in number-choice store bit pattern of - { - if (pv) - *reinterpret_cast(pv) = NCHOICE(v | NCNAN); - // .. NCNAN (7f80, cnglob.h) + choice 1,2,3.. in hi word of float. - if (pSz) - *pSz = sizeof(float); // tell caller value size is 4 bytes - } else // plain choice (cannot also hold number) - { - if (pv) - *(SI *)pv = v; // store choicb value: integer 1,2,3... - if (pSz) - *pSz = sizeof(SI); // size is 2 bytes - } - if (tyX == chtyALIASDEP) // if deprecated - { - if (pms) { - const char *ms = strtprintf("Deprecated '%s' converted to '%s'", - chtx, getChoiTxI(dt, v)); - *pms = ms; - } - return RCBAD2; // warning return - } - return RCOK; // good return - } - } - // not found. generate error message insert showing given string and - // choices - // list of choices does not include aliases 8-12 - if (pms) // if message return pointer given - { - USI maxll = getCpl() - 5; - // -5: leave some space for adding punctuation, indent, final " ...", etc. - const char *ms = - strtprintf(MH_V0039, s); // start assembling string "'%s' is not one - // of choice1 choice2 ..." - for (v = 1; v <= GetDttab(dt).nchoices; - v++) // loop data type's choices, concatenate each to ms - { - int tyX; - const char *chtx = getChoiTxI(dt, v, &tyX); // get vth choice - if (tyX != chtyHIDDEN) // if not hidden - { - if (strlen(ms) + strlen(chtx) > - 200) // if there's an unexpectedly large # long choices - { - ms = strtcat(ms, " ...", NULL); // limit amount shown - break; - } - const char *sep = - strJoinLen(ms, chtx) // length of line if joined, strpak.c - > maxll - ? "\n " - : " "; // separator: newline if needed - ms = strtcat(ms, sep, chtx, NULL); // concatenate to Tmpstr, strpak.c - } - } - *pms = ms; // return pointer to message insert text to caller - } - return RCBAD; // bad value for data type return - } - if (pms) - *pms = strtprintf( - MH_V0038, - dt); // "cvpak:cvS2Choi(): given data type 0x%x not a choice type" - return RCBAD; // bad data type. 2+ other returns above -} // cvS2Choi + if (pms) + *pms = nullptr; // init to no message + if (dt & (DTBCHOICB|DTBCHOICN)) // if a choice type + { + // search this choice data type's strings for a match, using getChoiTxI (just above). + int v; + for (v = 1; v <= GetDttab(dt).nchoices; v++) // loop data type's choices (GetDttab: srd.h) + { int tyX; + const char* chtx = getChoiTxI( dt, v, &tyX, 1); + if (tyX == chtyHIDDEN) + continue; // hidden, cannot match + + const char* p = chtx; + while (1) + { chtx = strxtok( NULL, p, "|", true); + if (!chtx) + break; + tyX = getChoiTxTyX( chtx); + if (tyX > chtyNORMAL) + chtx++; // drop prefix if any + if (_stricmp( s, chtx)) + continue; + if (dt & DTBCHOICN) // for choice in number-choice store bit pattern of + { if (pv) + *reinterpret_cast(pv) = NCHOICE(v | NCNAN); + // .. NCNAN (7f80, cnglob.h) + choice 1,2,3.. in hi word of float. + if (pSz) + *pSz = sizeof(float); // tell caller value size is 4 bytes + } + else // plain choice (cannot also hold number) + { if (pv) + *(SI *)pv = v; // store choicb value: integer 1,2,3... + if (pSz) + *pSz = sizeof( SI); // size is 2 bytes + } + if (tyX == chtyALIASDEP) // if deprecated + { if (pms) + { const char* ms = strtprintf( "Deprecated '%s' converted to '%s'", + chtx, getChoiTxI( dt, v)); + *pms = ms; + } + return RCBAD2; // warning return + } + return RCOK; // good return + } + } + // not found. generate error message insert showing given string and choices + // list of choices does not include aliases 8-12 + if (pms) // if message return pointer given + { + USI maxll = getCpl() - 5; + // -5: leave some space for adding punctuation, indent, final " ...", etc. + const char* ms = strtprintf( MH_V0039, s); // start assembling string "'%s' is not one of choice1 choice2 ..." + for (v = 1; v <= GetDttab(dt).nchoices; v++) // loop data type's choices, concatenate each to ms + { int tyX; + const char* chtx = getChoiTxI( dt, v, &tyX ); // get vth choice + if (tyX != chtyHIDDEN) // if not hidden + { if (strlen(ms) + strlen(chtx) > 200) // if there's an unexpectedly large # long choices + { + ms = strtcat( ms, " ...", NULL); // limit amount shown + break; + } + const char* sep + = strJoinLen( ms, chtx) // length of line if joined, strpak.c + > maxll ? "\n " : " "; // separator: newline if needed + ms = strtcat( ms, sep, chtx, NULL ); // concatenate to Tmpstr, strpak.c + } + } + *pms = ms; // return pointer to message insert text to caller + } + return RCBAD; // bad value for data type return + } + if (pms) + *pms = strtprintf( MH_V0038, dt ); // "cvpak:cvS2Choi(): given data type 0x%x not a choice type" + return RCBAD; // bad data type. 2+ other returns above +} // cvS2Choi /*=============================== TEST CODE ================================*/ /*==========================================================================*/ /* UNMAINTAINED Test code for output format */ /* #define TEST */ -#ifdef TEST /* #endif is about 250 lines down */ -t t #include /* test input routines, in lib\ but not in library.*/ - t t static float testf = 123.345; -t t main() t { - t char buf[200], buf2[200], dbuf[10]; - t #define NV 11 t static SI vallist[NV] = {-10000, -1000, -100, -10, -1, 0, - 1, 10, 100, 1000, 10000}; - t #define NFV 12 t static float fvlist[NFV] = - t{-13945789, -999.99, -100., -99.99, -1., 0., - .006, .999, 1.0, 9.999, 10., t 978437852312.}; - t static SI fmts[] = {FMTSQ, FMTLJ, FMTRJ, FMTLZ}; - t static char *fmtnames[] = {"sq", "lj", "rj", "lz"}; - t SI iv, m; - t float val; - t double d; - t USI i; - t SI dt, mfw, dfw, f, ifx; - t LI ft; - t SI in; - t char *p; - t IDATE tdate; - t ITIME ttime; - t IDATETIME tdatetime; - t RC rc; - t SEC sec; - t SI un, lm; - t CHOICE *ch; - t val = 2.5; - t f = FMTRJ + 2; - t while (1) t { - cvin2sBuf(buf, (char *)&val, DTFLOAT, UNLENGTH, 10, f); - t - } - t t /* #define PERCENTTEST */ - t #ifdef PERCENTTEST t dt = DTPERCENT; - t val = .30; - t cvin2sBuf(buf, (char *)&val, dt, UNNONE, 7, FMTSQ + 2); - t printf("\n'%s'", buf); - t cvin2sBuf(buf, (char *)&val, dt, UNNONE, 10, FMTLJ + 1); - t printf("\n'%s'", buf); - t cvin2sBuf(buf, (char *)&val, dt, UNNONE, 10, FMTRJ + 1); - t printf("\n'%s'", buf); - t cvin2sBuf(buf, (char *)&val, dt, UNNONE, 1, FMTRJ + 1); - t printf("\n'%s'", buf); - t #endif /* PERCENTTEST */ - t t /* #define CHOICETEST */ - t #ifdef CHOICETEST t dt = DTYESNO; - t un = UNNONE; - t lm = LMNONE; - t dfw = 0; - t Cp4snake = dmalloc(50, ABT); - t Dttab = (SI *)dmalloc(100, ABT); - t ch = DTCHOICEP(DTYESNO); - t ch->size = 2; - t ch->nchoices = 2; - t ch->choices[0] = C_YESNO_YES; - t ch->choices[1] = C_YESNO_NO; - t strcpy(rcHan2Tx(C_YESNO_YES), "Yes"); - t strcpy(rcHan2Tx(C_YESNO_NO), "No"); - t #endif /* CHOICETEST */ - t t /* #define INTEST */ - t #ifdef INTEST t dt = DTPERCENT; - t dfw = 2; - t while (strlen(getstr(">>> ", buf2)) > 0) t { - *(long *)dbuf = 0L; - t rc = cvs2in(buf2, dt, un, lm, dbuf, WRN); - t if (dbuf != Pgb) t ? ? ? t cvin2sBuf(buf, Pgb, dt, un, 7, FMTSQ + dfw); - t if (rc == RCOK) - t printf("Cvnchars=%d Gbsize=%d %s\n", Cvnchars, Gbsize, buf); - t - } - t #endif /* INTEST */ - t t t /* #define TESTINT */ - t #ifdef TESTINT t f = FMTLZ; - t dt = DTUSI; - t mfw = 6; - t for (iv = 0; iv < NV; iv++) t { - i = vallist[iv]; - t cvin2sBuf(buf, (char *)&i, dt, UNNONE, mfw, f); - t printf("Plus ... [%s] %d\n", buf, Cvnchars); - t /* +#ifdef TEST /* #endif is about 250 lines down */ +t +t #include /* test input routines, in lib\ but not in library.*/ +t +t static float testf = 123.345; +t +t main () +t { +t char buf[200],buf2[200],dbuf[10]; +t #define NV 11 +t static SI vallist[NV] = {-10000,-1000,-100,-10,-1,0,1,10,100,1000,10000}; +t #define NFV 12 +t static float fvlist[NFV] = +t { -13945789,-999.99,-100.,-99.99,-1.,0.,.006,.999,1.0,9.999,10., +t 978437852312. }; +t static SI fmts[] = { FMTSQ, FMTLJ, FMTRJ, FMTLZ }; +t static char *fmtnames[] = {"sq","lj","rj","lz"}; +t SI iv, m; +t float val; +t double d; +t USI i; +t SI dt, mfw, dfw, f, ifx; +t LI ft; +t SI in; +t char *p; +t IDATE tdate; +t ITIME ttime; +t IDATETIME tdatetime; +t RC rc; +t SEC sec; +t SI un, lm; +t CHOICE *ch; +t val = 2.5; +t f = FMTRJ+2; +t while (1) +t { cvin2sBuf(buf,(char *)&val,DTFLOAT,UNLENGTH,10,f); +t } +t +t /* #define PERCENTTEST */ +t #ifdef PERCENTTEST +t dt = DTPERCENT; +t val = .30; +t cvin2sBuf(buf,(char *)&val,dt,UNNONE,7,FMTSQ+2); +t printf("\n'%s'",buf); +t cvin2sBuf(buf,(char *)&val,dt,UNNONE,10,FMTLJ+1); +t printf("\n'%s'",buf); +t cvin2sBuf(buf,(char *)&val,dt,UNNONE,10,FMTRJ+1); +t printf("\n'%s'",buf); +t cvin2sBuf(buf,(char *)&val,dt,UNNONE,1,FMTRJ+1); +t printf("\n'%s'",buf); +t #endif /* PERCENTTEST */ +t +t /* #define CHOICETEST */ +t #ifdef CHOICETEST +t dt = DTYESNO; +t un = UNNONE; +t lm = LMNONE; +t dfw = 0; +t Cp4snake = dmalloc(50,ABT); +t Dttab = (SI *) dmalloc(100,ABT); +t ch = DTCHOICEP(DTYESNO); +t ch->size = 2; +t ch->nchoices = 2; +t ch->choices[0] = C_YESNO_YES; +t ch->choices[1] = C_YESNO_NO; +t strcpy( rcHan2Tx( C_YESNO_YES), "Yes"); +t strcpy( rcHan2Tx( C_YESNO_NO), "No"); +t #endif /* CHOICETEST */ +t +t /* #define INTEST */ +t #ifdef INTEST +t dt = DTPERCENT; +t dfw = 2; +t while (strlen(getstr(">>> ",buf2)) > 0) +t { *(long *)dbuf = 0L; +t rc = cvs2in( buf2, dt, un, lm, dbuf, WRN); +t if (dbuf != Pgb) +t ??? +t cvin2sBuf(buf,Pgb,dt,un,7,FMTSQ+dfw); +t if (rc == RCOK) +t printf("Cvnchars=%d Gbsize=%d %s\n",Cvnchars,Gbsize,buf); +t } +t #endif /* INTEST */ +t +t +t /* #define TESTINT */ +t #ifdef TESTINT +t f = FMTLZ; +t dt = DTUSI; +t mfw = 6; +t for (iv = 0; iv < NV; iv++) +t { i = vallist[iv]; +t cvin2sBuf(buf,(char *)&i,dt,UNNONE,mfw,f); +t printf("Plus ... [%s] %d\n",buf,Cvnchars); +t /* t cvin2sBuf(buf,(char *)&i,dt,UNNONE,mfw,FMTPVPLUS+f); t printf("Plus ... [%s] %d ",buf,Cvnchars); t cvin2sBuf(buf,(char *)&i,dt,UNNONE,mfw,FMTPVNULL+f); @@ -1702,129 +1538,136 @@ t printf("Null ... [%s] %d ",buf,Cvnchars); t cvin2sBuf(buf,(char *)&i,dt,UNNONE,mfw,FMTPVSPACE+f); t printf("Space ... [%s] %d\n",buf,Cvnchars); t */ - t - } - t /* +t } +t /* t i = 100; t cvin2sBuf(buf,(char *)&i,dt,UNNONE,mfw,FMTPVNULL+FMTLJ); t printf("Left just [%s] %d\n",buf,Cvnchars); t cvin2sBuf(buf,(char *)&i,dt,UNNONE,mfw,FMTPVNULL+FMTRJ); t printf("Right just [%s] %d\n",buf,Cvnchars); t */ - t #endif /* TESTINT */ - t t #ifdef TESTVAR t f = FMTRJ; - t mfw = VARWIDTH * Vardisp; - t cvin2sBuf(buf, (char *)vallist, DTVAR, UNNONE, mfw, FMTPVNULL + f); - t printf("[%s] %d\n", buf, Cvnchars); - t #endif /* TESTVAR */ - t t /* #define TESTSTR */ - t #ifdef TESTSTR t dt = DTSTRING; - t cvin2sBuf(buf, "Hello test", dt, UNNONE, 7, 0); - t printf("[%s] %d\n", buf, Cvnchars); - t cvin2sBuf(buf, "Hello test", dt, UNNONE, 11, 0); - t printf("[%s] %d\n", buf, Cvnchars); - t cvin2sBuf(buf, "Hello test", dt, UNNONE, 15, 0); - t printf("[%s] %d\n", buf, Cvnchars); - t cvin2sBuf(buf, "Hello test", dt, UNNONE, 15, FMTRJ); - t printf("[%s] %d\n", buf, Cvnchars); - t cvin2sBuf(buf, "Hello test", dt, UNNONE, 256, FMTSQ); - t printf("[%s] %d\n", buf, Cvnchars); - t #endif /* TESTSTR */ - t t #undef TESTDATE t #ifdef TESTDATE t /* - t dt = DTDOY; - t mfw = 10; - t i = 1; - t p = (char *)&i; - t */ - t /* - t mfw=30; - t dt = DTIDATETIME; - t tdatetime.year = 86; - t tdatetime.month = 11; - t tdatetime.mday = 24; - t tdatetime.wday = 4; - t tdatetime.hour = 12; - t tdatetime.min = 11; - t tdatetime.sec = 34; - t p = (char *)&tdatetime; - t */ - t /* - t mfw=15; - t dt = DTITIME; - t ttime.hour = 12; - t ttime.min = 4; - t ttime.sec = -1; - t p = (char *)&ttime; - t */ - t mfw = 10; - t dt = DTMONTH; - t i = 4; - t p = (char *)&i; - t t cvin2sBuf(buf, p, dt, UNNONE, mfw, FMTLJ); - t printf("[%s] %d\n", buf, Cvnchars); - t cvin2sBuf(buf, p, dt, UNNONE, mfw, FMTRJ); - t printf("[%s] %d\n", buf, Cvnchars); - t cvin2sBuf(buf, p, dt, UNNONE, mfw, FMTSQ); - t printf("[%s] %d\n", buf, Cvnchars); - t #endif /* TESTDATE */ - t t /* #define TESTFL */ - t #ifdef TESTFL t dt = DTFLOAT; - t for (iv = 0; iv < NFV; iv++) t { - val = fvlist[iv]; - t mfw = 10; - t dfw = 2; - t f = FMTLJ; - t printf("%12.3f ", val); - t cvin2sBuf(buf, (char *)&val, dt, UNNONE, mfw, FMTPVPLUS + dfw + f); - t printf("Plus [%s] %d\t", buf, Cvnchars); - t cvin2sBuf(buf, (char *)&val, dt, UNNONE, mfw, FMTPVNULL + dfw + f); - t printf("Null [%s] %d\t", buf, Cvnchars); - t cvin2sBuf(buf, (char *)&val, dt, UNNONE, mfw, FMTPVSPACE + dfw + f); - t printf("Space [%s] %d\n", buf, Cvnchars); - t - } - t #endif /* TESTFL */ - t t #ifdef TESTFTIN t val = 100.9; - t mfw = 6; - t dt = DTFLOAT; - t *buf = '\0'; - t for (iv = 0; iv < 2; iv++) t { - for (ifx = 0; ifx < 4; ifx++) - t { - f = fmts[ifx]; - t printf("\n%s ... ", fmtnames[ifx]); - t cvin2sBuf(buf, (char *)&val, dt, UNLENGTH, mfw, FMTPVPLUS + f); - t printf("Plus %2d [%s]\t", Cvnchars, buf); - t cvin2sBuf(buf, (char *)&val, dt, UNLENGTH, mfw, FMTPVNULL + f); - t printf("Null %2d [%s]\t", Cvnchars, buf); - t cvin2sBuf(buf, (char *)&val, dt, UNLENGTH, mfw, FMTPVSPACE + f); - t printf("Space %2d [%s]", Cvnchars, buf); - t - } - t val = -val; - t - } - t #endif /* TESTFTIN */ - t t /* #define TESTCF */ - t #ifdef TESTCF t #define GETFDDT(r, f) - DTFLOAT t /* #define GETFDP(r,f) (&testf) */ - t #define GETFDUN(r, f) - UNENERGY t #define GETUNTAG(u)("kBtuh") - t #define RCRT(r)(0) t m = 30; - t for (mfw = 5; mfw < 9; mfw++) t { - cvcf2init(m, mfw, m + 20, mfw + 1); - t for (dfw = 0; dfw < 3; dfw++) t { - p = cvcf2(buf, "Dog", &testf, 1, FMTRJ + FMTUNITS + dfw, t & testf, 1, - FMTRJ + FMTUNITS + FMTNODATA + dfw); - t printf("[%s] Len=%d\n", p, strlen(p)); - t - } - t - } - t #endif /* TESTCF */ - t -} -#endif /* TEST */ +t #endif /* TESTINT */ +t +t #ifdef TESTVAR +t f = FMTRJ; +t mfw = VARWIDTH*Vardisp; +t cvin2sBuf(buf,(char *)vallist,DTVAR,UNNONE,mfw,FMTPVNULL+f); +t printf("[%s] %d\n",buf,Cvnchars); +t #endif /* TESTVAR */ +t +t /* #define TESTSTR */ +t #ifdef TESTSTR +t dt = DTSTRING; +t cvin2sBuf(buf,"Hello test",dt,UNNONE,7,0); +t printf("[%s] %d\n",buf,Cvnchars); +t cvin2sBuf(buf,"Hello test",dt,UNNONE,11,0); +t printf("[%s] %d\n",buf,Cvnchars); +t cvin2sBuf(buf,"Hello test",dt,UNNONE,15,0); +t printf("[%s] %d\n",buf,Cvnchars); +t cvin2sBuf(buf,"Hello test",dt,UNNONE,15,FMTRJ); +t printf("[%s] %d\n",buf,Cvnchars); +t cvin2sBuf(buf,"Hello test",dt,UNNONE,256,FMTSQ); +t printf("[%s] %d\n",buf,Cvnchars); +t #endif /* TESTSTR */ +t +t #undef TESTDATE +t #ifdef TESTDATE +t /* +t dt = DTDOY; +t mfw = 10; +t i = 1; +t p = (char *)&i; +t */ +t /* +t mfw=30; +t dt = DTIDATETIME; +t tdatetime.year = 86; +t tdatetime.month = 11; +t tdatetime.mday = 24; +t tdatetime.wday = 4; +t tdatetime.hour = 12; +t tdatetime.min = 11; +t tdatetime.sec = 34; +t p = (char *)&tdatetime; +t */ +t /* +t mfw=15; +t dt = DTITIME; +t ttime.hour = 12; +t ttime.min = 4; +t ttime.sec = -1; +t p = (char *)&ttime; +t */ +t mfw=10; +t dt = DTMONTH; +t i = 4; +t p = (char *)&i; +t +t cvin2sBuf(buf,p,dt,UNNONE,mfw,FMTLJ); +t printf("[%s] %d\n",buf,Cvnchars); +t cvin2sBuf(buf,p,dt,UNNONE,mfw,FMTRJ); +t printf("[%s] %d\n",buf,Cvnchars); +t cvin2sBuf(buf,p,dt,UNNONE,mfw,FMTSQ); +t printf("[%s] %d\n",buf,Cvnchars); +t #endif /* TESTDATE */ +t +t /* #define TESTFL */ +t #ifdef TESTFL +t dt = DTFLOAT; +t for (iv = 0; iv < NFV; iv++) +t { val = fvlist[iv]; +t mfw = 10; +t dfw = 2; +t f = FMTLJ; +t printf("%12.3f ",val); +t cvin2sBuf(buf,(char *)&val,dt,UNNONE,mfw,FMTPVPLUS+dfw+f); +t printf("Plus [%s] %d\t",buf,Cvnchars); +t cvin2sBuf(buf,(char *)&val,dt,UNNONE,mfw,FMTPVNULL+dfw+f); +t printf("Null [%s] %d\t",buf,Cvnchars); +t cvin2sBuf(buf,(char *)&val,dt,UNNONE,mfw,FMTPVSPACE+dfw+f); +t printf("Space [%s] %d\n",buf,Cvnchars); +t } +t #endif /* TESTFL */ +t +t #ifdef TESTFTIN +t val = 100.9; +t mfw = 6; +t dt = DTFLOAT; +t *buf = '\0'; +t for (iv = 0; iv < 2; iv++) +t { for (ifx = 0; ifx < 4; ifx++) +t { f = fmts[ifx]; +t printf("\n%s ... ",fmtnames[ifx]); +t cvin2sBuf(buf,(char *)&val,dt,UNLENGTH,mfw,FMTPVPLUS+f); +t printf("Plus %2d [%s]\t",Cvnchars,buf); +t cvin2sBuf(buf,(char *)&val,dt,UNLENGTH,mfw,FMTPVNULL+f); +t printf("Null %2d [%s]\t",Cvnchars,buf); +t cvin2sBuf(buf,(char *)&val,dt,UNLENGTH,mfw,FMTPVSPACE+f); +t printf("Space %2d [%s]",Cvnchars,buf); +t } +t val = -val; +t } +t #endif /* TESTFTIN */ +t +t /* #define TESTCF */ +t #ifdef TESTCF +t #define GETFDDT(r,f) DTFLOAT +t /* #define GETFDP(r,f) (&testf) */ +t #define GETFDUN(r,f) UNENERGY +t #define GETUNTAG(u) ("kBtuh") +t #define RCRT(r) (0) +t m = 30; +t for (mfw = 5; mfw < 9; mfw++) +t { cvcf2init( m, mfw, m+20, mfw+1); +t for (dfw = 0; dfw < 3; dfw++) +t { p = cvcf2( buf, "Dog", &testf, 1, FMTRJ+FMTUNITS+dfw, +t &testf, 1, FMTRJ+FMTUNITS+FMTNODATA+dfw ); +t printf("[%s] Len=%d\n",p,strlen(p)); +t } +t } +t #endif /* TESTCF */ +t } +#endif /* TEST */ /////////////////////////////////////////////////////////////////////////////// // basic string-binary conversion functions including @@ -1835,69 +1678,64 @@ t */ // be about 3 times slower than cvatof() and introduces MSC dependency. /*=============================== TEST CODE ================================*/ #ifdef TEST -t t main() t { - t RC rc; - t LI li; - t SI i, test, loop, loopLimit, printFlg; - t double d; - t char *s; - t static struct /* test driver table */ - t { - char *s; /* string to convert, NULL to term table */ - t SI test; /* 0: cvatol, hexoct = 0 +t +t main() +t{ +t RC rc; +t LI li; +t SI i, test, loop, loopLimit, printFlg; +t double d; +t char *s; +t static struct /* test driver table */ +t { char *s; /* string to convert, NULL to term table */ +t SI test; /* 0: cvatol, hexoct = 0 t 1: cvatol, hexoct = 1 t 2: cvatof, percent = 0 t 3: cvatof, percent = 1 */ - t - } tab[] = t{" 3", - 2, - t " 3.33e+4", - 2, - t " 3.e+4", - 2, - t " 3e-10", - 2, - t "5.34952945293", - 2, - t " 3.33%e17", - 3, - t " 3.33 %", - 3, - t ".", - 2, - t "0000.0", - 2, - t ".0", - 2, - t NULL t}; - t t loopLimit = 1; - t for (loop = 0; ++loop <= loopLimit;) t { - t printFlg = loop == loopLimit; - t i = -1; - t while ((s = tab[++i].s) != NULL) t { - test = tab[i].test; - t if (printFlg) t printf("s=|%s| ", s); - t if (test <= 1) t { - t li = 0L; - t rc = cvatol(s, &li, test); - t if (printFlg) t printf("li = %ld", li); - t - } - t else t { - t d = 0.; - t rc = cvatof(s, &d, test - 2); - t if (printFlg) t printf("d = %f", d); - t - } - t if (printFlg) t printf(" rc = %d\n", rc); - t - } - t - } - t return 0; /* keep compiler happy */ - t -} /* main */ -#endif /* TEST */ +t } tab[] = +t { " 3", 2, +t " 3.33e+4", 2, +t " 3.e+4", 2, +t " 3e-10", 2, +t "5.34952945293",2, +t " 3.33%e17", 3, +t " 3.33 %", 3, +t ".", 2, +t "0000.0", 2, +t ".0", 2, +t NULL +t }; +t +t loopLimit = 1; +t for (loop = 0; ++loop <= loopLimit; ) +t { +t printFlg = loop == loopLimit; +t i = -1; +t while ( (s = tab[++i].s) != NULL) +t { test = tab[i].test; +t if (printFlg) +t printf("s=|%s| ",s); +t if (test <= 1) +t { +t li = 0L; +t rc = cvatol( s, &li, test ); +t if (printFlg) +t printf("li = %ld", li ); +t } +t else +t { +t d = 0.; +t rc = cvatof( s, &d, test-2 ); +t if (printFlg) +t printf("d = %f", d ); +t } +t if (printFlg) +t printf(" rc = %d\n", rc ); +t } +t } +t return 0; /* keep compiler happy */ +t} /* main */ +#endif /* TEST */ //=========================================================================== #if 0 @@ -1981,15 +1819,15 @@ RC FC cvatol( /* convert string to (signed) long integer */ return RCOK; /* good return */ } /* cvatol */ -#endif // unused +#endif // unused //======================================================================== -RC FC cvatof( // Convert a string to a double value +RC FC cvatof( // Convert a string to a double value - const char *_str, // String, leading and trailing ws OK - double *vp, // Pointer to double to receive value. - bool percent) // true: trailing '%' is acceptable (and ignored) - // false '%' treated as error + const char* _str, // String, leading and trailing ws OK + double* vp, // Pointer to double to receive value. + bool percent ) // true: trailing '%' is acceptable (and ignored) + // false '%' treated as error /* valid format: [ws][+|-][ws][digits][.[digits]][e|E|d|D[+|-][digits]][ws][*] @@ -2002,186 +1840,176 @@ RC FC cvatof( // Convert a string to a double value so values over about 10**15 must be input with e format. Changes 9-11-89: - 1. accepts D/d in addition to E/e for exponents - 2. new arg percent eliminates use of cvpak global. % now handled - correctly for e format ( .34e01 % ); exponents no longer go - through cvatol. - 3. combined action and state tables into single table, made entries - symbolic w/ #defines for ez reading. */ + 1. accepts D/d in addition to E/e for exponents + 2. new arg percent eliminates use of cvpak global. % now handled + correctly for e format ( .34e01 % ); exponents no longer go + through cvatol. + 3. combined action and state tables into single table, made entries + symbolic w/ #defines for ez reading. */ /* Returns RCOK if conversion succeeded, otherwise informative RC. No value is stored unless conversion succeeds. */ { - double v; - SI negVal, negExp, nd, id, idd, expo, irdd; - char c; - char d[100], dd[100]; /* characters left / right of decimal point */ - SI s, ct; - - /* String decode portion of function is implemented with finite state scheme. - The STATE records the current decoding situation; each input char is - retrieved and a state-dependent ACTION is taken. The table actState - drives the process. */ - /* States */ -#define lws 0 /* scanning over leading white space */ -#define ld 1 /* scanning for leading digit (sign has been seen) */ -#define gd 2 /* getting digits (left of dec point) */ -#define gdd 3 /* getting digits (right of dec point) */ -#define xsn 4 /* expecting exponent sign (or digit) */ -#define xld 5 /* expecting exponent leading digit */ -#define xd 6 /* expecting exponent digit (or term. non-digit) */ -#define tc 7 /* scanning over trailing chars (% or ws) */ -#define tws 8 /* scanning over trailing white space */ - /* Actions */ -#define SK 0x00 /* discard character */ -#define GSN 0x10 /* note sign */ -#define GD 0x20 /* get digit (left of dec. point) */ -#define GDD 0x30 /* get digit (right of dec. point) */ -#define GXS 0x40 /* note exponent sign */ -#define GX 0x50 /* get exponent digit */ -#define DONE 0x60 /* \0 found, go determine value */ -#define ERR 0xF0 /* syntax error, return "Invalid number" */ - static UCH actState[9][7] = { - /* ------------------ Character - ------------------------- */ - /* ws + or - 0-9 . Ee/Dd - % '\0' */ - /* ------ ------ ------ ------ ------ - ------ ------ */ - /* lws: leading ws */ {SK + lws, GSN + ld, GD + gd, SK + gdd, ERR, ERR, - ERR}, - /* ld: leading dgt */ {SK + ld, ERR, GD + gd, SK + gdd, ERR, ERR, ERR}, - /* gd: get digits */ - {SK + tc, ERR, GD + gd, SK + gdd, SK + xsn, SK + tws, DONE}, - /* gdd: get dec dgts */ - {SK + tc, ERR, GDD + gdd, ERR, SK + xsn, SK + tws, DONE}, - /* xsn: get expo sign*/ {ERR, GXS + xld, GX + xd, ERR, ERR, ERR, ERR}, - /* xld: expo ldng dgt*/ {ERR, ERR, GX + xd, ERR, ERR, ERR, ERR}, - /* xd: expo digit */ {SK + tc, ERR, GX + xd, ERR, ERR, SK + tws, DONE}, - /* tc: trlng chars */ {SK + tc, ERR, ERR, ERR, ERR, SK + tws, DONE}, - /* tws: trailing ws */ {SK + tws, ERR, ERR, ERR, ERR, ERR, DONE}}; - - negVal = negExp = nd = id = idd = irdd = expo = 0; - s = lws; /* initial state: scanning over ws */ - - for (;;) /* loop over char string */ - { - /* get and categorize character */ - c = tolower(*_str); - if (isdigitW(c)) - ct = 2; - else if (isspaceW(c)) - ct = 0; - else if (c == '.') - ct = 3; - else if (c == '\0') - ct = 6; - else if (c == '-' || c == '+') - ct = 1; - else if (c == 'e' || c == 'd') - ct = 4; - else if (c == '%' && percent) - ct = 5; /* cond'l recog of '%' */ - else - return MH_V0010; // "Invalid number": unrec char - // or bad format - - switch (actState[s][ct] & 0xF0) /* perform actions */ - { - case SK: - break; /* skip character */ - - case GSN: - negVal = (c == '-'); /* get sign: note if '-' */ - break; - - case GD: - nd++; /* total digits w/ ldng 0s */ - if (id > 0 || c != '0') /* get digit, skp ldng 0s */ - d[id++] = c - '0'; - break; - - case GDD: - dd[idd++] = c - '0'; /* get dec digit */ - if (c != '0') /* if non-0 */ - { - irdd = idd; /* note rightmost dec. dig. */ - if (id == 0) - id = -idd; /* and most sig. digit */ - } - break; - - case GXS: - negExp = (c == '-'); /* note negative exponent */ - break; - - case GX: - expo = 10 * expo + c - '0'; /* accum exponent value */ - if (expo > 1000) - return MH_V0013; // quit b4 expo overflows - // "Value must be between 10e-38 and 10e38" - break; - - case DONE: - if (negExp) - expo = -expo; - if (id < 0) /* if only digits right of dp */ - id++; /* set most sig pos to final value */ - goto makeval; - - case ERR: - return MH_V0010; // "invalid number" - - default:; - } - s = actState[s][ct] & 0x0F; /* new state */ - _str++; /* next char */ - } - - /* Upon arrival here, things are set up as follows: - id: Position of most significant digit. For instance, 1000.012 - would produce 4, 0.1 would produce 0, 0.001 would produce -2. - nd: Total digits to left of decimal point including leading 0s - idd: Total digits to right of decimal point including 0s - irdd: Position of least sig. non-0 digit to right of decimal point. - 1000. would produce 0, 1000.01200 would produce 3. - expo: Exponent. Truly ridiculous values have been eliminated above. - */ + double v; + SI negVal, negExp, nd, id, idd, expo, irdd; + char c; + char d[100], dd[100]; /* characters left / right of decimal point */ + SI s, ct; + + /* String decode portion of function is implemented with finite state scheme. + The STATE records the current decoding situation; each input char is + retrieved and a state-dependent ACTION is taken. The table actState + drives the process. */ + /* States */ +#define lws 0 /* scanning over leading white space */ +#define ld 1 /* scanning for leading digit (sign has been seen) */ +#define gd 2 /* getting digits (left of dec point) */ +#define gdd 3 /* getting digits (right of dec point) */ +#define xsn 4 /* expecting exponent sign (or digit) */ +#define xld 5 /* expecting exponent leading digit */ +#define xd 6 /* expecting exponent digit (or term. non-digit) */ +#define tc 7 /* scanning over trailing chars (% or ws) */ +#define tws 8 /* scanning over trailing white space */ + /* Actions */ +#define SK 0x00 /* discard character */ +#define GSN 0x10 /* note sign */ +#define GD 0x20 /* get digit (left of dec. point) */ +#define GDD 0x30 /* get digit (right of dec. point) */ +#define GXS 0x40 /* note exponent sign */ +#define GX 0x50 /* get exponent digit */ +#define DONE 0x60 /* \0 found, go determine value */ +#define ERR 0xF0 /* syntax error, return "Invalid number" */ + static UCH actState[9][7] = + { + /* ------------------ Character ------------------------- */ + /* ws + or - 0-9 . Ee/Dd % '\0' */ + /* ------ ------ ------ ------ ------ ------ ------ */ + /* lws: leading ws */ { SK+lws, GSN+ld, GD+gd, SK+gdd, ERR, ERR, ERR }, + /* ld: leading dgt */ { SK+ld, ERR, GD+gd, SK+gdd, ERR, ERR, ERR }, + /* gd: get digits */ { SK+tc, ERR, GD+gd, SK+gdd, SK+xsn, SK+tws, DONE }, + /* gdd: get dec dgts */ { SK+tc, ERR, GDD+gdd,ERR, SK+xsn, SK+tws, DONE }, + /* xsn: get expo sign*/ { ERR, GXS+xld,GX+xd, ERR, ERR, ERR, ERR }, + /* xld: expo ldng dgt*/ { ERR, ERR, GX+xd, ERR, ERR, ERR, ERR }, + /* xd: expo digit */ { SK+tc, ERR, GX+xd, ERR, ERR, SK+tws, DONE }, + /* tc: trlng chars */ { SK+tc, ERR, ERR, ERR, ERR, SK+tws, DONE }, + /* tws: trailing ws */ { SK+tws, ERR, ERR, ERR, ERR, ERR, DONE } + }; + + negVal = negExp = nd = id = idd = irdd = expo = 0; + s = lws; /* initial state: scanning over ws */ + + for ( ; ; ) /* loop over char string */ + { + /* get and categorize character */ + c = tolower(*_str); + if (isdigitW(c)) ct = 2; + else if (isspaceW(c)) ct = 0; + else if (c=='.') ct = 3; + else if (c=='\0') ct = 6; + else if (c=='-' || c=='+') ct = 1; + else if (c=='e' || c=='d') ct = 4; + else if (c=='%' && percent) ct = 5; /* cond'l recog of '%' */ + else return MH_V0010; // "Invalid number": unrec char + // or bad format + + + switch ( actState[s][ct] & 0xF0 ) /* perform actions */ + { + case SK: + break; /* skip character */ + + case GSN: + negVal = (c=='-'); /* get sign: note if '-' */ + break; + + case GD: + nd++; /* total digits w/ ldng 0s */ + if ( id > 0 || c != '0') /* get digit, skp ldng 0s */ + d[id++] = c - '0'; + break; + + case GDD: + dd[idd++] = c - '0'; /* get dec digit */ + if (c != '0') /* if non-0 */ + { + irdd=idd; /* note rightmost dec. dig. */ + if (id==0) + id = -idd; /* and most sig. digit */ + } + break; + + case GXS: + negExp = (c=='-'); /* note negative exponent */ + break; + + case GX: + expo = 10*expo + c - '0'; /* accum exponent value */ + if (expo > 1000) + return MH_V0013; // quit b4 expo overflows + // "Value must be between 10e-38 and 10e38" + break; + + case DONE: + if (negExp) + expo = -expo; + if (id < 0) /* if only digits right of dp */ + id++; /* set most sig pos to final value */ + goto makeval; + + case ERR: + return MH_V0010; // "invalid number" + + default: + ; + } + s = actState[s][ct] & 0x0F; /* new state */ + _str++; /* next char */ + } + + /* Upon arrival here, things are set up as follows: + id: Position of most significant digit. For instance, 1000.012 + would produce 4, 0.1 would produce 0, 0.001 would produce -2. + nd: Total digits to left of decimal point including leading 0s + idd: Total digits to right of decimal point including 0s + irdd: Position of least sig. non-0 digit to right of decimal point. + 1000. would produce 0, 1000.01200 would produce 3. + expo: Exponent. Truly ridiculous values have been eliminated above. + */ makeval: - /* various validity checks */ - if ((nd + idd) == 0) - return MH_V0010; /* "invalid number": no digits */ - if (id > PTENSIZE /* more than 16 signif dgts to left of d.p. */ - || irdd > PTENSIZE - 1) /* more than 16 signif dgts to rght of d.p. */ - return MH_V0016; /* "Too many digits. Use E format for - very large or very small values" */ - if (expo + id > FLT_MAX_10_EXP // FLT_MAX_10_EXP = 38 (float.h) - || expo + id - 1 < -FLT_MAX_10_EXP) - return MH_V0013; /* "Value must be between 10e-38 and 10e38" */ - - /* sum up value of all non-0 digits starting from least significant */ - v = 0.; - for (s = irdd; --s >= 0;) - if (dd[s]) /* if non-0 digit right of dp */ - v += (double)dd[s] / Pten[s + 1]; - for (s = id; --s >= 0;) - if (d[s]) /* if non-0 digit left of dp */ - v += (double)d[s] * Pten[id - s - 1]; - - /* scale by 10**expo */ - if (expo) /* if there's a non-0 exponent */ - { - if (expo > 0 && expo < PTENSIZE) - v *= Pten[expo]; /* pos expo: mult by pwr of 10 */ - else if (expo < 0 && -expo < PTENSIZE) - v /= Pten[-expo]; /* neg expo: div by pwr of 10 */ - else - v *= pow(10., (double)expo); /* outside Pten[] range, use pow */ - } - *vp = negVal ? -v : v; - return RCOK; -} /* cvatof */ + /* various validity checks */ + if ( (nd + idd) == 0) + return MH_V0010; /* "invalid number": no digits */ + if ( id > PTENSIZE /* more than 16 signif dgts to left of d.p. */ + || irdd > PTENSIZE-1) /* more than 16 signif dgts to rght of d.p. */ + return MH_V0016; /* "Too many digits. Use E format for + very large or very small values" */ + if (expo+id > FLT_MAX_10_EXP // FLT_MAX_10_EXP = 38 (float.h) + || expo+id-1 < -FLT_MAX_10_EXP) + return MH_V0013; /* "Value must be between 10e-38 and 10e38" */ + + /* sum up value of all non-0 digits starting from least significant */ + v = 0.; + for (s = irdd; --s >= 0; ) + if ( dd[s] ) /* if non-0 digit right of dp */ + v += (double)dd[s]/Pten[s+1]; + for (s = id; --s >= 0; ) + if ( d[s] ) /* if non-0 digit left of dp */ + v += (double)d[s]*Pten[id-s-1]; + + /* scale by 10**expo */ + if (expo) /* if there's a non-0 exponent */ + { + if (expo > 0 && expo < PTENSIZE) + v *= Pten[expo]; /* pos expo: mult by pwr of 10 */ + else if (expo < 0 && -expo < PTENSIZE) + v /= Pten[-expo]; /* neg expo: div by pwr of 10 */ + else + v *= pow( 10., (double)expo); /* outside Pten[] range, use pow */ + } + *vp = negVal ? -v : v; + return RCOK; +} /* cvatof */ // end of cvpak.cpp From 3c5074412fd4fbc6a184ae678341a4719ba65dfb Mon Sep 17 00:00:00 2001 From: Phil Ahrenkiel Date: Tue, 2 Apr 2024 09:06:51 -0600 Subject: [PATCH 03/21] Simple fix. --- src/cnloads.cpp | 11075 +++++++++++++++++++++++----------------------- 1 file changed, 5505 insertions(+), 5570 deletions(-) diff --git a/src/cnloads.cpp b/src/cnloads.cpp index 3779fb8b3..840ba5bdd 100644 --- a/src/cnloads.cpp +++ b/src/cnloads.cpp @@ -2,25 +2,26 @@ // Use of this source code is governed by a BSD-style license // that can be found in the LICENSE file. -// cnloads.cpp -- Hourly simulation loads modelling calculations for a single hour for CSE +// cnloads.cpp -- Hourly simulation loads modelling calculations for a single +// hour for CSE /*------------------------------- INCLUDES --------------------------------*/ -#include "cnglob.h" // global defines +#include "cnglob.h" // global defines -#include "srd.h" -#include "ancrec.h" // record: base class for rccn.h classes -#include "rccn.h" // IZXRAT SGRAT ZNR +#include "ancrec.h" // record: base class for rccn.h classes #include "irats.h" +#include "rccn.h" // IZXRAT SGRAT ZNR +#include "srd.h" #include "timer.h" -#include "hvac.h" // CoolingSHR and Heat Pump 95/47 sizing -#include "psychro.h" // psyHCondWtr -#include "cgwthr.h" // Wthr.nZ .zD +#include "cgwthr.h" // Wthr.nZ .zD +#include "hvac.h" // CoolingSHR and Heat Pump 95/47 sizing +#include "psychro.h" // psyHCondWtr +#include "cnguts.h" +#include "cse.h" #include "cueval.h" #include "exman.h" -#include "cse.h" -#include "cnguts.h" #include "mspak.h" #include "nummeth.h" @@ -31,26 +32,25 @@ #include "comfort/comfort.h" #endif -#include "ashwface.h" // #includes xmodule.h +#include "ashwface.h" // #includes xmodule.h /*-------------------------------- OPTIONS --------------------------------*/ // 7-92 MARG1 and MARG2 2.0 made NO DIFFERENCE even in # itertions (q2, q3, q4) // suspect means humidity is limiting condition. -#define MARG1 1. // tolerance multiplier for some tests in loadsHourBeg. -#define MARG2 1. // tolerance multiplier for some tests in loadsSubhr. -#undef ZNHVACFCONV // define to activate consideration of - // HVAC heat conv/radiant ratio (incomplete) - // Disabled 1-2021 pending elaboration. +#define MARG1 1. // tolerance multiplier for some tests in loadsHourBeg. +#define MARG2 1. // tolerance multiplier for some tests in loadsSubhr. +#undef ZNHVACFCONV // define to activate consideration of + // HVAC heat conv/radiant ratio (incomplete) + // Disabled 1-2021 pending elaboration. /*-------------------------------- DEFINES --------------------------------*/ /*----------------------- LOCAL FUNCTION DECLARATIONS ---------------------*/ static RC loadsIzxSh1(); static RC loadsIzxSh2(); -static RC loadsSurfaces( BOO subhrly); +static RC loadsSurfaces(BOO subhrly); static RC loadsXFans(); - /*------------------------ The MAIN EQUATION story ------------------------*/ // rob 12-89 prelim /* @@ -58,13 +58,13 @@ Start with the first law of thermodynamics: the change in enthalpy (temperature (Tz) times heat capacity hc) of an object (such as the air in a zone) is equal to the sum of the heat flows (powers) Qj into it: - dTz - --- * hc = sigma(Qj) - dt + dTz + --- * hc = sigma(Qj) + dt Change the derivative to a delta for numerical approx purposes, and write: - (Tz - Tzold) * hc / t = sigma(Qj) + (Tz - Tzold) * hc / t = sigma(Qj) where Tzold is the prior cycle temperature and t is the time interval. @@ -83,25 +83,25 @@ Solve for Tz: Tz*hc/t + Tz*sigma(UAi) = sigma(UAi*Ti) + Tzold*hc/t + Qheat + sigma(Qj) Tz * (hc/t + sigma(UAi)) = ditto - sigma(UAi*Ti) + Tzold*hc/t + Qheat + sigma(Qj) + sigma(UAi*Ti) + Tzold*hc/t + Qheat + sigma(Qj) Tz = ---------------------------------------------- sigma(UAi) + hc/t Define "a" as the numerator less Qheat, and "b" as the denominator. The MAIN EQUATION can then be written in the following two forms: - a + Qheat - Tz = --------- Qheat = Tz *z * b - a - b + a + Qheat + Tz = --------- Qheat = Tz *z * b - a + b where a = sigma(UAi*Ti) + sigma(Qj) + Tzold*hc/t - The sum of the heat flows thru conductances plus solar - and other heat flows plus the old temperature * hc/t. + The sum of the heat flows thru conductances plus solar + and other heat flows plus the old temperature * hc/t. and b = sigma(UAi) + hc/t - The sum of the conductances to other objects plus hc/t. + The sum of the conductances to other objects plus hc/t. Application to ZONES: The above formulas are used to simulate zone (air) @@ -109,10 +109,10 @@ temperatures. When Tz is fixed, Qheat is computed; when Qheat is 0 (Tz floating between heat and cool thermostat settings, or in an unconditioned space), Tz is computed. -Zone temperatures are computed in cnloads.cpp/cnhvac.cpp, using various subexpressions -(members of the ZNR struct) precomputed in cnguts.cpp. They are computed -several times an hour; t is Top.tp_subhrDur. For "hc" the CAIR is used -- -the "air heat capacity" which has all the not-very-massive heat capacity of +Zone temperatures are computed in cnloads.cpp/cnhvac.cpp, using various +subexpressions (members of the ZNR struct) precomputed in cnguts.cpp. They are +computed several times an hour; t is Top.tp_subhrDur. For "hc" the CAIR is used +-- the "air heat capacity" which has all the not-very-massive heat capacity of the space (walls, furniture) lumped into it for modelling purposes. MASSes: It turns out that the model must account for the fact that the @@ -133,105 +133,111 @@ But Masses now all changed 1-95. RC ZNR::zn_RddInit() // initialization common to main simulation run and each autosize design day { - // Set up initial and constant values - // some redundant (not needed each design day) but cheap - zn_md = 1; // zone hvac mode (control mode) - tz = aTz = tzls = tzlh = 80.f; // zone air temps, incl ah working copy - zn_tr = zn_trls = zn_trlh = 80.f; // zone radiant temps - wz = aWz = wzls = Top.tp_refW; // zone humidity ratios 5-25-92 - zn_rho0ls = zn_Rho0(); // zone air density at tz, wz, pz0 - zn_bcon // init constant part of main equation denom: - = zn_ua // sum uval*area of zone ambient lite surfs - + zn_uaSpecT; // sum uval*area of zn specT lite surfs (hsu) - i.znCAirSh = i.znCAir / Top.tp_subhrDur; // commonly used in subhr code - - // Mass->ha's done in ms_RddInit - // aMassHr = aMassSh = 0.; object is pre-0'd but may be re-used in autoSizing - // but believe these don't need init for start-interval masses - haMass = 0.; // +='d in ms_rddInit. pre-0'd object may be re-used in autoSizing. - znXLGain = znXLGainLs = 0; // no condensation heat leftover from prior iteration, rob 6-11-97 - zn_ebErrCount = 0; // count of short-interval energy balance errors - zn_pz0WarnCount[0] = zn_pz0WarnCount[1] = 0; - - // HVAC convective delivery fraction - // needs elaboration for radiant systems - zn_fConvH = zn_fConvC = zn_fConv = 1.f; // used iff ZNHVACFCONV defined - - return RCOK; -} // ZNR::zn_RddInit() + // Set up initial and constant values + // some redundant (not needed each design day) but cheap + zn_md = 1; // zone hvac mode (control mode) + tz = aTz = tzls = tzlh = 80.f; // zone air temps, incl ah working copy + zn_tr = zn_trls = zn_trlh = 80.f; // zone radiant temps + wz = aWz = wzls = Top.tp_refW; // zone humidity ratios 5-25-92 + zn_rho0ls = zn_Rho0(); // zone air density at tz, wz, pz0 + zn_bcon // init constant part of main equation denom: + = zn_ua // sum uval*area of zone ambient lite surfs + + zn_uaSpecT; // sum uval*area of zn specT lite surfs (hsu) + i.znCAirSh = i.znCAir / Top.tp_subhrDur; // commonly used in subhr code + + // Mass->ha's done in ms_RddInit + // aMassHr = aMassSh = 0.; object is pre-0'd but may be re-used in + // autoSizing + // but believe these don't + //need init for start-interval masses + haMass = + 0.; // +='d in ms_rddInit. pre-0'd object may be re-used in autoSizing. + znXLGain = znXLGainLs = + 0; // no condensation heat leftover from prior iteration, rob 6-11-97 + zn_ebErrCount = 0; // count of short-interval energy balance errors + zn_pz0WarnCount[0] = zn_pz0WarnCount[1] = 0; + + // HVAC convective delivery fraction + // needs elaboration for radiant systems + zn_fConvH = zn_fConvC = zn_fConv = 1.f; // used iff ZNHVACFCONV defined + + return RCOK; +} // ZNR::zn_RddInit() //----------------------------------------------------------------------------- -RC ZNR::zn_RddDone( // called at end of simulation and each autosize design day - bool isAusz) // true: autosize - // false: simulation +RC ZNR::zn_RddDone( // called at end of simulation and each autosize design day + bool isAusz) // true: autosize + // false: simulation // duplicate calls harmless // NOTE: clears zn_pz0WarnCount[] -// +// // returns RCOK iff all OK { - RC rc = RCOK; - - // report count of AirNet pressure warnings - // see AIRNET_SOLVER::an_CheckResults() and ZNR::zn_CheckAirNetPressure() - if (zn_pz0WarnCount[0] > 0 || zn_pz0WarnCount[1] > 0) - { - warn("Zone '%s': %s unreasonable pressure warning counts --" - "\n mode 0 = %d / mode 1 = %d", - Name(), - isAusz ? strtprintf("%s autosizing", Top.tp_AuszDoing()) : "Simulation", - zn_pz0WarnCount[0], zn_pz0WarnCount[1]); - zn_pz0WarnCount[0] = zn_pz0WarnCount[1] = 0; // prevent duplicate msg - } - - return rc; -} // ZNR::zn_RddDone + RC rc = RCOK; + + // report count of AirNet pressure warnings + // see AIRNET_SOLVER::an_CheckResults() and ZNR::zn_CheckAirNetPressure() + if (zn_pz0WarnCount[0] > 0 || zn_pz0WarnCount[1] > 0) { + warn("Zone '%s': %s unreasonable pressure warning counts --" + "\n mode 0 = %d / mode 1 = %d", + Name(), + isAusz ? strtprintf("%s autosizing", Top.tp_AuszDoing()) + : "Simulation", + zn_pz0WarnCount[0], zn_pz0WarnCount[1]); + zn_pz0WarnCount[0] = zn_pz0WarnCount[1] = 0; // prevent duplicate msg + } + + return rc; +} // ZNR::zn_RddDone //==================================================================== -RC FC loadsHourBeg() // start of hour loads stuff: solar gains, hourly masses, zones init, . +RC FC loadsHourBeg() // start of hour loads stuff: solar gains, hourly masses, + // zones init, . // non-RCOK return means terminate run. Message already issued. { - /* CALCULATE HOURLY SOLAR GAINS (most are subhourly, done elsewhere) - Set "targets" in [zones and] masses to their solar gains for hour, calculated by combining - hour's weather data and control variable values (window shade positions) with the - precalculated (cgsolar.cpp) factors in the entries in the (currently selected) Solar Gain Rat. - The factors already reflect the size, orientation, and absorptivity of each XSURF and the - sun's position this month. cnrecs.def/rccn.h structures. */ - SGRAT* sge; - RLUP( SgR, sge) - { if (!sge->sg_isSubhrly) - sge->sg_ToTarg( Top.radBeamHrAv, Top.radDiffHrAv); - } - - // ducts - DUCTSEG* ds; - RLUP( DsR, ds) - ds->ds_BegHour(); - - IZXRAT* ize; - RLUP( IzxR, ize) - ize->iz_BegHour(); // start-hour zone transfer / airnet (ignore return) - -#if defined( ZONE_XFAN) - // SIMULATE zone exhaust fans (xfans) - // xfan flow controlled hourly by schedule - // do before masses (may alter convective xfer (future)) - // do before airnet (so flows are known) - loadsXFans(); -#endif - - // SIMULATE HOURLY MASSES Updates their surface temperatures. - // (All light ("quick") are subhourly.) - // Uses hourly mass solar gains (set in loop above) - // sets zone aMassHr's (used in loop below). - loadsSurfaces( FALSE); // below. FALSE to do hourly masses. - - // zones - ZNR* zp; - RLUP( ZrB, zp) - zp->zn_BegHour2(); - - RSYS* rs; - RLUP( RsR, rs) - rs->rs_BegHour(); + /* CALCULATE HOURLY SOLAR GAINS (most are subhourly, done elsewhere) + Set "targets" in [zones and] masses to their solar gains for hour, + calculated by combining hour's weather data and control variable values + (window shade positions) with the precalculated (cgsolar.cpp) factors in the + entries in the (currently selected) Solar Gain Rat. The factors already + reflect the size, orientation, and absorptivity of each XSURF and the sun's + position this month. cnrecs.def/rccn.h structures. */ + SGRAT *sge; + RLUP(SgR, sge) { + if (!sge->sg_isSubhrly) + sge->sg_ToTarg(Top.radBeamHrAv, Top.radDiffHrAv); + } + + // ducts + DUCTSEG *ds; + RLUP(DsR, ds) + ds->ds_BegHour(); + + IZXRAT *ize; + RLUP(IzxR, ize) + ize->iz_BegHour(); // start-hour zone transfer / airnet (ignore return) + +#if defined(ZONE_XFAN) + // SIMULATE zone exhaust fans (xfans) + // xfan flow controlled hourly by schedule + // do before masses (may alter convective xfer (future)) + // do before airnet (so flows are known) + loadsXFans(); +#endif + + // SIMULATE HOURLY MASSES Updates their surface temperatures. + // (All light ("quick") are subhourly.) + // Uses hourly mass solar gains (set in loop above) + // sets zone aMassHr's (used in loop below). + loadsSurfaces(FALSE); // below. FALSE to do hourly masses. + + // zones + ZNR *zp; + RLUP(ZrB, zp) + zp->zn_BegHour2(); + + RSYS *rs; + RLUP(RsR, rs) + rs->rs_BegHour(); #if 0 // !defined( SHINTERP) else done subhrly below o// MASSES LOOP: @@ -246,357 +252,404 @@ o ZrB.p[ mse->outside.zi ].qMsSg += mse->outside.sg; // add surf's outside sol o } #endif - return RCOK; // error return above -} // loadsHourBeg + return RCOK; // error return above +} // loadsHourBeg //----------------------------------------------------------------------------- -void SGRAT::sg_ToTarg( // apply solar gain to target - float bmRad, // beam radiation on normal surface, Btuh/ft2 - float dfRad) // diffuse radiation on horizontal surface, Btuh/ft2 +void SGRAT::sg_ToTarg( // apply solar gain to target + float bmRad, // beam radiation on normal surface, Btuh/ft2 + float dfRad) // diffuse radiation on horizontal surface, Btuh/ft2 // can be called hourly or subhourly, args must appropriately correspond to time { - if (bmRad <= 0. && dfRad <= 0.) // if no insolation this hour - { sg_pTarg->st_bm = 0.; // just zero all targets. Unnecessary to test sge->addIt. - sg_pTarg->st_df = 0.; - } - else - { double bmGain = bmRad * sg_bmXBmF[ Top.iHrST]; - double dfGain = dfRad * sg_dfXDfF[ Top.iHrST] + bmRad * sg_dfXBmF[ Top.iHrST]; - if (sg_pControl) // if this SGRAT has a control - { bmGain *= *sg_pControl; // multiply gain by it. eg zone shades-closed fraction. - dfGain *= *sg_pControl; - } - if (sg_addIt==0) // if 1st for target - { sg_pTarg->st_bm = bmGain; // store gain, initializing target value - sg_pTarg->st_df = dfGain; - } - else // if additional for target - { sg_pTarg->st_bm += bmGain; // add gain to previous - sg_pTarg->st_df += dfGain; - } - // note: addIt != 0 probably now 3-90 corresponds to control != NULL - // but we don't depend on that assumption. - } - sg_pTarg->st_tot = sg_pTarg->st_bm + sg_pTarg->st_df; // total gain -} // SGRAT::sg_ToTarg + if (bmRad <= 0. && dfRad <= 0.) // if no insolation this hour + { + sg_pTarg->st_bm = + 0.; // just zero all targets. Unnecessary to test sge->addIt. + sg_pTarg->st_df = 0.; + } else { + double bmGain = bmRad * sg_bmXBmF[Top.iHrST]; + double dfGain = dfRad * sg_dfXDfF[Top.iHrST] + bmRad * sg_dfXBmF[Top.iHrST]; + if (sg_pControl) // if this SGRAT has a control + { + bmGain *= + *sg_pControl; // multiply gain by it. eg zone shades-closed fraction. + dfGain *= *sg_pControl; + } + if (sg_addIt == 0) // if 1st for target + { + sg_pTarg->st_bm = bmGain; // store gain, initializing target value + sg_pTarg->st_df = dfGain; + } else // if additional for target + { + sg_pTarg->st_bm += bmGain; // add gain to previous + sg_pTarg->st_df += dfGain; + } + // note: addIt != 0 probably now 3-90 corresponds to control != NULL + // but we don't depend on that assumption. + } + sg_pTarg->st_tot = sg_pTarg->st_bm + sg_pTarg->st_df; // total gain +} // SGRAT::sg_ToTarg //----------------------------------------------------------------------------- -void SGRAT::sg_DbDump() const -{ - DbPrintf( "\nSGDIST '%s': isSubhrly=%d addIt=%d\n%s targ=%p control=%p", - Name(), sg_isSubhrly, sg_addIt, - Top.tp_RepTestPfx(), sg_pTarg, sg_pControl); - for (int iH=0; iH<24; iH++) - { if (iH%4 == 0) - DbPrintf( "\n"); - DbPrintf("%8.2d %6.2f %6.2f %6.2f", - iH, sg_dfXDfF[ iH], sg_dfXBmF[ iH], sg_bmXBmF[ iH]); - } -} // sg_DbDump +void SGRAT::sg_DbDump() const { + DbPrintf("\nSGDIST '%s': isSubhrly=%d addIt=%d\n%s targ=%p control=%p", + Name(), sg_isSubhrly, sg_addIt, Top.tp_RepTestPfx(), sg_pTarg, + sg_pControl); + for (int iH = 0; iH < 24; iH++) { + if (iH % 4 == 0) + DbPrintf("\n"); + DbPrintf("%8.2d %6.2f %6.2f %6.2f", iH, sg_dfXDfF[iH], sg_dfXBmF[iH], + sg_bmXBmF[iH]); + } +} // sg_DbDump //----------------------------------------------------------------------------- -RC ZNR::zn_BegHour1() // "early" hourly initializations +RC ZNR::zn_BegHour1() // "early" hourly initializations // 0s gains and other values // done *after* expressions, before dependent inits { - qrIgTot = qrIgTotO = qrIgTotIz = qrIgAir = 0.f; - znSGain = znLGain = znLitDmd = znLitEu = 0.; - zn_anVentEffect = 0; // # of IZXRATs that *could* impact this - // zone when operated in vent mode. - // see IZXRAT::iz_HasVentEffect() - - if (Top.isBegRun) - { // prior hour setpoints - zn_tzspHlh = i.znTH; - zn_tzspDlh = i.znTD; - zn_tzspClh = i.znTC; - } - return RCOK; -} // ZNR::zn_BegHour1 + qrIgTot = qrIgTotO = qrIgTotIz = qrIgAir = 0.f; + znSGain = znLGain = znLitDmd = znLitEu = 0.; + zn_anVentEffect = 0; // # of IZXRATs that *could* impact this + // zone when operated in vent mode. + // see IZXRAT::iz_HasVentEffect() + + if (Top.isBegRun) { // prior hour setpoints + zn_tzspHlh = i.znTH; + zn_tzspDlh = i.znTD; + zn_tzspClh = i.znTC; + } + return RCOK; +} // ZNR::zn_BegHour1 //----------------------------------------------------------------------------- -RC ZNR::zn_BegHour2() // beginning-of-hour calcs for zone -{ - zn_SetAirRadXArea(); // update air temp/relHum dependent radiant exchange values - // for convective/radiant zone model; - // small impact, not worth substep cost - - // hour's sensible internal gain: scheduled value, if any. - // Used for znaqLdHr, zp->qsIg, and to apportion to subhr results. - qsIgHr = znSGain; // sensible gain totalled from GAIN records - // also: qrIgAir: NOT included here0 - - // Non-hvac hour-constant portions of MAIN EQUATION "Tz = (a + q)/b" (story at start file) - - // sum ua*t for specified-temp-exposure surfaces - XSRAT* xs /*= NULL*/; - zn_uaXSpecT = 0.; - for (TI xi = xsSpecT1; xi != 0; xi = xs->nxXsSpecT) // these xsurfs have own chain. loop over them. - { xs = XsB.p + xi; // access XSRAT record xi - zn_uaXSpecT += xs->x.xs_uval * xs->x.xs_area * xs->x.sfExT; // u * a * outside temp (hourly vbl) - /*>>>> there are 2 hourly and 1 subhourly use of uval*area --> otta precompute it, - maybe also use in cnguts re ua -SpecT. FLOAT gud enuf for the latter? rob 3-91. */ - } - - // zn_aqLdHr: hour-constant non-hvac a's and q's for numerator. a = sigma(UAi*Ti) + Tzold*hc/t. - zn_aqLdHr = - aMassHr // hourly masses UAT (from loadsSurfaces call just above) - + qsIgHr // q's: sched sens internal gain from GAINS (znSGAIN) & wthr file. - + qrIgAir // q's: radiant internal gain to zone CAir (light surfaces) 11-95. - + zn_uaXSpecT; // uaT total from just above - // all other components (infil etc) done subhourly - - // zn_bLdHr: hour-constant non-hvac b's for denominator. b = sigma(UA) + hc/t. - zn_bLdHr = zn_bcon; // just the run-const UAs - - /* zn_xqHr: q = b * t - a using only hourly components, for change tests (just below) only: - Intended to be appropriately sensitive to changes when b*a almost equal to a, - eg when aMassHr settling & all else constant, - cuz terminal loads are of form b * t - a (with added subhrly components). - Fixed problems with bug39a.inp run (1 mass surf, const temps). 12-5-94. */ - zn_xqHr = zn_bLdHr * tz - zn_aqLdHr; // member only cuz saved to -Pr in loadsSubhr. - - // check/warn alternate model values - // However: don't correct value -- consumers use max(), min() as approp. - // (Changed value can persist due to expression eval optimization.) - if (i.znQMxH < -0.01f) - orWarn( "znQMxH (%0.f) taken as 0 (s/b >= 0)", i.znQMxH); - if (i.znQMxC > 0.01f) - orWarn( "znQMxC (%0.f) taken as 0 (s/b <= 0)", i.znQMxC); - - /* hourly-only load change checks: - zn_xqHr: Hourly parts of b * t - a, just above. - znLGain: latent gain rate (set per GAINs by cnguts.cpp:doHourgains, used in cnZtu.cpp:ZNR::znW). - Note: for subsequent subhrs, wz change sets ztuCf in loadsSubhr. - -Pr's used here are updated in loadsSubhr, every time ztuCf is on. */ - // zn_aqLdHr and zn_bLdHr are covered by zn_xqHr check & no longer checked separately 12-94. - // Removing aq- and zn_bLdHr checks made NO difference in test suites 12-94 - if ( - // .02 found better than .05,.1,.2 in bug39a.inp run (1 mass surf, const temps) (12-94): - RELCHANGE( zn_xqHr, zn_xqHrPr) > .02*Top.relTol*MARG1 // if hourly part of difference b*t-aq (for loads) changed - || ABSCHANGE( znLGain, znLGainPr) // if zone moisture GAIN rate (Btuh) changed: - > PsyHCondWtr*Top.absHumTol ) // test abs lb/hr change after conversion - { - ztuCf++; // say zone load changed: must do ztuCompute. - // ztuCf causes -Pr's to be updated. - } - return RCOK; -} // ZNR::zn_BegHour2 +RC ZNR::zn_BegHour2() // beginning-of-hour calcs for zone +{ + zn_SetAirRadXArea(); // update air temp/relHum dependent radiant exchange + // values + // for convective/radiant zone model; + // small impact, not worth substep cost + + // hour's sensible internal gain: scheduled value, if any. + // Used for znaqLdHr, zp->qsIg, and to apportion to subhr results. + qsIgHr = znSGain; // sensible gain totalled from GAIN records + // also: qrIgAir: NOT included here0 + + // Non-hvac hour-constant portions of MAIN EQUATION "Tz = (a + q)/b" (story at + // start file) + + // sum ua*t for specified-temp-exposure surfaces + XSRAT *xs /*= NULL*/; + zn_uaXSpecT = 0.; + for (TI xi = xsSpecT1; xi != 0; + xi = xs->nxXsSpecT) // these xsurfs have own chain. loop over them. + { + xs = XsB.p + xi; // access XSRAT record xi + zn_uaXSpecT += xs->x.xs_uval * xs->x.xs_area * + xs->x.sfExT; // u * a * outside temp (hourly vbl) + /*>>>> there are 2 hourly and 1 subhourly use of uval*area --> otta + precompute it, maybe also use in cnguts re ua -SpecT. FLOAT gud enuf for + the latter? rob 3-91. */ + } + + // zn_aqLdHr: hour-constant non-hvac a's and q's for numerator. a = + // sigma(UAi*Ti) + Tzold*hc/t. + zn_aqLdHr = + aMassHr // hourly masses UAT (from loadsSurfaces call just above) + + + qsIgHr // q's: sched sens internal gain from GAINS (znSGAIN) & wthr file. + + + qrIgAir // q's: radiant internal gain to zone CAir (light surfaces) 11-95. + + zn_uaXSpecT; // uaT total from just above + // all other components (infil etc) done subhourly + + // zn_bLdHr: hour-constant non-hvac b's for denominator. b = sigma(UA) + + // hc/t. + zn_bLdHr = zn_bcon; // just the run-const UAs + + /* zn_xqHr: q = b * t - a using only hourly components, for change tests + (just below) only: Intended to be appropriately sensitive to changes when + b*a almost equal to a, eg when aMassHr settling & all else constant, cuz + terminal loads are of form b * t - a (with added subhrly components). + Fixed problems with bug39a.inp run (1 mass surf, const temps). + 12-5-94. */ + zn_xqHr = + zn_bLdHr * tz - zn_aqLdHr; // member only cuz saved to -Pr in loadsSubhr. + + // check/warn alternate model values + // However: don't correct value -- consumers use max(), min() as approp. + // (Changed value can persist due to expression eval optimization.) + if (i.znQMxH < -0.01f) + orWarn("znQMxH (%0.f) taken as 0 (s/b >= 0)", i.znQMxH); + if (i.znQMxC > 0.01f) + orWarn("znQMxC (%0.f) taken as 0 (s/b <= 0)", i.znQMxC); + + /* hourly-only load change checks: + zn_xqHr: Hourly parts of b * t - a, just above. + znLGain: latent gain rate (set per GAINs by cnguts.cpp:doHourgains, used + in cnZtu.cpp:ZNR::znW). Note: for subsequent subhrs, wz change sets ztuCf + in loadsSubhr. -Pr's used here are updated in loadsSubhr, every time ztuCf + is on. */ + // zn_aqLdHr and zn_bLdHr are covered by zn_xqHr check & no longer checked + // separately 12-94. + // Removing aq- and zn_bLdHr checks made NO difference in test suites 12-94 + if ( + // .02 found better than .05,.1,.2 in bug39a.inp run (1 mass surf, const + // temps) (12-94): + RELCHANGE(zn_xqHr, zn_xqHrPr) > + .02 * Top.relTol * + MARG1 // if hourly part of difference b*t-aq (for loads) changed + || ABSCHANGE(znLGain, + znLGainPr) // if zone moisture GAIN rate (Btuh) changed: + > PsyHCondWtr * + Top.absHumTol) // test abs lb/hr change after conversion + { + ztuCf++; // say zone load changed: must do ztuCompute. + // ztuCf causes -Pr's to be updated. + } + return RCOK; +} // ZNR::zn_BegHour2 //----------------------------------------------------------------------------- -RC ZNR::zn_BegSubhr1() // zone start of subhour, part 1 +RC ZNR::zn_BegSubhr1() // zone start of subhour, part 1 // call *before* airnet and possible others needing prior-step (lagged) values { - memset( &ZnresB.p[ss].curr.S, 0, sizeof( ZNRES_IVL_SUB) ); // access subhr results struct in ZNRES via subscr in ZrB & 0 it. - ZnresB.p[ss].curr.S.nSubhr = 1L; // count subhours by setting to 1 at subhr level & accumulating + memset(&ZnresB.p[ss].curr.S, 0, + sizeof(ZNRES_IVL_SUB)); // access subhr results struct in ZNRES via + // subscr in ZrB & 0 it. + ZnresB.p[ss].curr.S.nSubhr = + 1L; // count subhours by setting to 1 at subhr level & accumulating - // zone-specific wind velocity pressure (re AirNet) - // zn_windFLkg defaults from zn_eaveZ and zn_infShld - // else is input (subhour variability) - zn_windPresV = Top.tp_WindPresV( i.zn_windFLkg * Top.windSpeedSh); + // zone-specific wind velocity pressure (re AirNet) + // zn_windFLkg defaults from zn_eaveZ and zn_infShld + // else is input (subhour variability) + zn_windPresV = Top.tp_WindPresV(i.zn_windFLkg * Top.windSpeedSh); - if (!nMd) // if zone has no modes (eg startup with no terminals) - spCf++; // tell ztuCompute to build zone mode sequence + if (!nMd) // if zone has no modes (eg startup with no terminals) + spCf++; // tell ztuCompute to build zone mode sequence - // initialize DHW heat transfer totals - // TODO: could be done in DHW code iff needed - // Win? -- generally less DHWHEATERs than ZONEs - // OTOH, this is foolproof and simple, 2-16 - zn_qDHWLoss = zn_qDHWLossAir = zn_qDHWLossRad = zn_qHPWH = zn_hpwhAirX = 0.; + // initialize DHW heat transfer totals + // TODO: could be done in DHW code iff needed + // Win? -- generally less DHWHEATERs than ZONEs + // OTOH, this is foolproof and simple, 2-16 + zn_qDHWLoss = zn_qDHWLossAir = zn_qDHWLossRad = zn_qHPWH = zn_hpwhAirX = 0.; - return RCOK; -} // ZNR::zn_BegSubhr1 + return RCOK; +} // ZNR::zn_BegSubhr1 //----------------------------------------------------------------------------- -RC ZNR::zn_BegSubhr2() // zone start of subhour, part 2 +RC ZNR::zn_BegSubhr2() // zone start of subhour, part 2 // call *after* lagged values used { - // forced convection coeff, Btuh/ft2-F; re C_CONVMODELCH_UNIFIED - // based on lagged value of zone total air change rate - // combined with surface-specific sb_hcNat, see SBC::sb_SetCoeffs() - // must be done *after* DHW calcs re HPWH air motion - zn_hcFrc = Top.tp_hConvF * i.zn_hcFrcF * pow( max( zn_hcAirXls + zn_hpwhAirX, 0.f), 0.8f); - zn_airNetI[ 0].af_Init(); - zn_airNetI[ 1].af_Init(); - - zn_qIzXAnSh = 0.; // subhour non-airnet xfers - zn_qIzSh = 0.; // total - - qMsSg = 0.f; // zone's mass solar and radiant internal gains. Accum'd during mass calcs - qrIgMs = 0.f; - - znXLGain = 0.; // sensible gain due to excess latent "condensation" - - // heat balance terms for convective/radiant model - // later accum additional from mass, airnet, etc - zn_nAirSh = zn_qDuctCondAir // duct conduction: lagged value from end of prior step - + zn_qDHWLossAir // DHW losses into zone: current step - + zn_qHPWH // DHW heat pump water heater extraction: current step - + zn_sysDepAirIls.af_AmfCpT(); // prior step system-dependent - // air flow into zone (duct leaks, OAV relief,) - zn_dAirSh = zn_sysDepAirIls.af_AmfCp(); - - zn_nRadSh = zn_qDuctCondRad // duct conduction: lagged value - + zn_qDHWLossRad; // DHW losses into zone - zn_dRadSh = 0.; - zn_cxSh = 0.; - - // duct conduction - // save total for ZNRES balance - // init air and rad re cur step accum if system runs - zn_qDuctCond = zn_qDuctCondAir + zn_qDuctCondRad; - zn_qDuctCondAir = zn_qDuctCondRad = 0.; - - zn_ductLkI.af_Init(); // air flows in / out - zn_ductLkO.af_Init(); - zn_sysAirI.af_Init(); - zn_sysAirO.af_Init(); - zn_OAVRlfO.af_Init(); - zn_rsAmfSup = zn_rsAmfRet = 0.; - - // NO! CNE zone model can skip step calc and use prior result - // initialization done later for CZM zone model - // zn_qsHvac = 0.; - // zn_qlHvac = 0.; - - zn_fVentPrf = zn_fVent = 0.f; // vent fraction - // = actual / possible vent flow - - return RCOK; -} // ZNR::zn_BegSubhr2 + // forced convection coeff, Btuh/ft2-F; re C_CONVMODELCH_UNIFIED + // based on lagged value of zone total air change rate + // combined with surface-specific sb_hcNat, see SBC::sb_SetCoeffs() + // must be done *after* DHW calcs re HPWH air motion + zn_hcFrc = Top.tp_hConvF * i.zn_hcFrcF * + pow(max(zn_hcAirXls + zn_hpwhAirX, 0.f), 0.8f); + zn_airNetI[0].af_Init(); + zn_airNetI[1].af_Init(); + + zn_qIzXAnSh = 0.; // subhour non-airnet xfers + zn_qIzSh = 0.; // total + + qMsSg = 0.f; // zone's mass solar and radiant internal gains. Accum'd during + // mass calcs + qrIgMs = 0.f; + + znXLGain = 0.; // sensible gain due to excess latent "condensation" + + // heat balance terms for convective/radiant model + // later accum additional from mass, airnet, etc + zn_nAirSh = + zn_qDuctCondAir // duct conduction: lagged value from end of prior step + + zn_qDHWLossAir // DHW losses into zone: current step + + zn_qHPWH // DHW heat pump water heater extraction: current step + + zn_sysDepAirIls + .af_AmfCpT(); // prior step system-dependent + // air flow into zone (duct leaks, OAV relief,) + zn_dAirSh = zn_sysDepAirIls.af_AmfCp(); + + zn_nRadSh = zn_qDuctCondRad // duct conduction: lagged value + + zn_qDHWLossRad; // DHW losses into zone + zn_dRadSh = 0.; + zn_cxSh = 0.; + + // duct conduction + // save total for ZNRES balance + // init air and rad re cur step accum if system runs + zn_qDuctCond = zn_qDuctCondAir + zn_qDuctCondRad; + zn_qDuctCondAir = zn_qDuctCondRad = 0.; + + zn_ductLkI.af_Init(); // air flows in / out + zn_ductLkO.af_Init(); + zn_sysAirI.af_Init(); + zn_sysAirO.af_Init(); + zn_OAVRlfO.af_Init(); + zn_rsAmfSup = zn_rsAmfRet = 0.; + + // NO! CNE zone model can skip step calc and use prior result + // initialization done later for CZM zone model + // zn_qsHvac = 0.; + // zn_qlHvac = 0.; + + zn_fVentPrf = zn_fVent = 0.f; // vent fraction + // = actual / possible vent flow + + return RCOK; +} // ZNR::zn_BegSubhr2 //============================================================================ -RC FC loadsSubhr() // loads stuff for all zones for subhour. Call BEFORE hvac code. +RC FC loadsSubhr() // loads stuff for all zones for subhour. Call BEFORE hvac + // code. // Assumes zone subhour results pre-0'd: done in zn_BegSubhr { - RC rc = RCOK; - ZNR *zp; - - /* CALCULATE SUBHOURLY SOLAR GAINS - Set subhourly "targets" in zones and masses to their solar gains for subhour, calculated by combining - subhour's weather data and control variable values (window shade positions) with the - precalculated (cgsolar.cpp) factors in the entries in the (currently selected) Solar Gain Rat. - The factors already reflect the size, orientation, and absorptivity of each XSURF and the - sun's position this month. cnrecs.def/rccn.h structures. */ - SGRAT* sge; + RC rc = RCOK; + ZNR *zp; + + /* CALCULATE SUBHOURLY SOLAR GAINS + Set subhourly "targets" in zones and masses to their solar gains for + subhour, calculated by combining subhour's weather data and control variable + values (window shade positions) with the precalculated (cgsolar.cpp) factors + in the entries in the (currently selected) Solar Gain Rat. The factors + already reflect the size, orientation, and absorptivity of each XSURF and + the sun's position this month. cnrecs.def/rccn.h structures. */ + SGRAT *sge; #if 1 - RLUP( SgR, sge) // loop SgR records, setting sge to point at each - { if (sge->sg_isSubhrly) // hourly sg's done elsewhere -- in loadsHourBeg - sge->sg_ToTarg( Top.radBeamShAv, Top.radDiffShAv); - } + RLUP(SgR, sge) // loop SgR records, setting sge to point at each + { + if (sge->sg_isSubhrly) // hourly sg's done elsewhere -- in loadsHourBeg + sge->sg_ToTarg(Top.radBeamShAv, Top.radDiffShAv); + } #else -x SI sunup = Top.radDiffShAv > 0.F || Top.radBeamShAv > 0.F; // 0 if no sun this subhour -x RLUP( SgR, sge) // loop SgR records, setting sge to point at each -x { if (!sge->sg_isSubhrly) // hourly sg's done elsewhere -- in loadsHourBeg -x continue; -x if (!sunup) // if no sun this hour, just 0 all subhourly gains -x *sge->sg_targ = 0.f; // zero the target. testing sge->addIt unnecessary. -x else -x { -x#ifdef SOLAVNEND // undef in cndefns.h (via cnglob.h) 1-18-94: only if computing & using end-ivl as well as ivl avg solar values -xo float gain = // gain consists of diffuse & hour's beam values -xo sge->isEndIvl // TRUE to use end-subhr not subhr-avg, 1-95 -xo ? Top.radDiffSh * sge->diff[Top.iHrST] // combine weather data -xo + Top.radBeamSh * sge->beam[Top.iHrST] // with this sge's gain factors -xo : Top.radDiffShAv * sge->diff[Top.iHrST] -xo + Top.radBeamShAv * sge->beam[Top.iHrST]; -x#else -x#if 1 -x float gain = Top.radDiffShAv * sge->sg_diff[Top.iHrST] // gain consists of subhour average weather data values -x + Top.radBeamShAv * sge->sg_beam[Top.iHrST]; // ... combined with hour's gain factors in sge -x#else // temp backtrack 2-95, undone -xx float gain = Top.radDiffShAv * sge->diff[Top.iHr] // gain consists of subhour average weather data values -xx + Top.radBeamShAv * sge->beam[Top.iHr]; // ... combined with hour's gain factors in sge -x#endif -x#endif -x // (gain fixed for diff[24], standard time 2-95) -x if (sge->sg_control) // if sge has pointer to control factor -x gain *= *sge->sg_control; // apply control. eg fraction zone shades closed. -x if (sge->sg_addIt==0) // if first sge for target -x *sge->sg_targ = gain; // initialize by storing not adding gain -x else -x *sge->sg_targ += gain; // accumulate additional gains into target by adding -x -x /* *sge->targ is ZrB.p[i].qSgAir or .qSgTotSh, or subhrly MsR.p[i].outside.sg or .inside.sg, -x as targeted in cgsolar.cpp:sgrGet(). */ -x // note: addIt != 0 probably now 3-90 corresponds to control != NULL, but we don't depend on that assumption. -x } -x } -#endif - -// SIMULATE SUBHOURLY SURFACES -// Must follow (any future) subhouly solar gain determination, precede zone loop. Uses Top.tp_subhrDur. - loadsSurfaces( TRUE); // below. TRUE = do subhourly (not hourly masses) - - TMRSTART( TMR_ZONE); - -// COMPUTE INTERZONE TRANSFERS and base (infil only) airnet - CSE_E( loadsIzxSh1() ) - - bool bDbPrint = DbDo( dbdZM); -#if defined( _DEBUG) - [[maybe_unused]] const ZNR* zp1 = ZrB.GetAtSafe( 1); + x SI sunup = Top.radDiffShAv > 0.F || + Top.radBeamShAv > 0.F; // 0 if no sun this subhour + x RLUP(SgR, sge) // loop SgR records, setting sge to point at each + x { + if (!sge->sg_isSubhrly) // hourly sg's done elsewhere -- in loadsHourBeg + x continue; + x if (!sunup) // if no sun this hour, just 0 all subhourly gains + x *sge->sg_targ = + 0.f; // zero the target. testing sge->addIt unnecessary. + x else x { + x #ifdef SOLAVNEND // undef in cndefns.h (via cnglob.h) 1-18-94: only if + // computing & using end-ivl as well as ivl avg solar + // values + xo float gain = // gain consists of diffuse & + // hour's beam values + xo sge->isEndIvl // TRUE to use end-subhr not subhr-avg, 1-95 + xo + ? Top.radDiffSh * sge->diff[Top.iHrST] // combine weather data + xo + + Top.radBeamSh * + sge->beam[Top.iHrST] // with this sge's gain factors + xo + : Top.radDiffShAv * sge->diff[Top.iHrST] xo + + Top.radBeamShAv * sge->beam[Top.iHrST]; + x #else x #if 1 x float gain = + Top.radDiffShAv * + sge->sg_diff[Top.iHrST] // gain consists of subhour average + // weather data values + x + + Top.radBeamShAv * sge->sg_beam[Top.iHrST]; // ... combined with hour's + // gain factors in sge + x #else // temp backtrack 2-95, undone + xx float gain = + Top.radDiffShAv * + sge->diff[Top.iHr] // gain consists of subhour average weather + // data values + xx + + Top.radBeamShAv * sge->beam[Top.iHr]; // ... combined with hour's + // gain factors in sge + x #endif x #endif x // (gain fixed for diff[24], standard time 2-95) + x if (sge->sg_control) // if sge has pointer to control factor + x gain *= + *sge->sg_control; // apply control. eg fraction zone shades closed. + x if (sge->sg_addIt == 0) // if first sge for target + x *sge->sg_targ = gain; // initialize by storing not adding gain + x else x *sge->sg_targ += + gain; // accumulate additional gains into target by adding + x x /* *sge->targ is ZrB.p[i].qSgAir or .qSgTotSh, or subhrly + MsR.p[i].outside.sg or .inside.sg, x as targeted in + cgsolar.cpp:sgrGet(). */ + x // note: addIt != 0 probably now 3-90 corresponds to control != + // NULL, but we don't depend on that assumption. + x + } + x + } +#endif + + // SIMULATE SUBHOURLY SURFACES + // Must follow (any future) subhouly solar gain determination, precede zone + // loop. Uses Top.tp_subhrDur. + loadsSurfaces(TRUE); // below. TRUE = do subhourly (not hourly masses) + + TMRSTART(TMR_ZONE); + + // COMPUTE INTERZONE TRANSFERS and base (infil only) airnet + CSE_E(loadsIzxSh1()) + + bool bDbPrint = DbDo(dbdZM); +#if defined(_DEBUG) + [[maybe_unused]] const ZNR *zp1 = ZrB.GetAtSafe(1); #endif - // GLOBAL LOAD CHANGE CHECK: outdoor humidity - // change changes all zone loads, but does not show in a,b. - BOO azCf = Top.tp_wOShChange; + // GLOBAL LOAD CHANGE CHECK: outdoor humidity + // change changes all zone loads, but does not show in a,b. + BOO azCf = Top.tp_wOShChange; - RLUP(ZrB, zp) - { - rc |= zp->zn_InitSubhr(); // final zone init for loads calc - // sets values for all models -#if defined( CRTERMAH) - rc |= zp->zn_SetZtuCfIf(azCf); + RLUP(ZrB, zp) { + rc |= zp->zn_InitSubhr(); // final zone init for loads calc + // sets values for all models +#if defined(CRTERMAH) + rc |= zp->zn_SetZtuCfIf(azCf); #endif + } - } - - if (!Top.tp_bAllCR) - { // all CNE zones (all CR or all CNE enforced) -#if !defined( CRTERMAH) - RLUP( ZrB, zp) - rc |= zp->zn_LoadsSubhr( azCf); // non-CR stuff - // change-check subhour zone loads, etc. + if (!Top.tp_bAllCR) { // all CNE zones (all CR or all CNE enforced) +#if !defined(CRTERMAH) + RLUP(ZrB, zp) + rc |= zp->zn_LoadsSubhr(azCf); // non-CR stuff + // change-check subhour zone loads, etc. #else - // may extrapolate tz if load unchanged - // subhour terminal/airhandler hvac - rc |= hvacIterSubhr(); - RLUP(ZrB, zp) - { - zp->zn_MapTerminalResults(); - zp->zn_AirXMoistureBal(true); - } + // may extrapolate tz if load unchanged + // subhour terminal/airhandler hvac + rc |= hvacIterSubhr(); + RLUP(ZrB, zp) { + zp->zn_MapTerminalResults(); + zp->zn_AirXMoistureBal(true); + } #endif - return rc; - } + return rc; + } - // current floating temp of non-terminal zones - RLUPC( ZrB, zp, !zp->zn_HasTerminal()) - zp->zn_TAirFloatCR(); + // current floating temp of non-terminal zones + RLUPC(ZrB, zp, !zp->zn_HasTerminal()) + zp->zn_TAirFloatCR(); - // convective-radiant model multizone vent control + // convective-radiant model multizone vent control - // TODO ideas - // * Overall vent available flag Top.? (allows scheduling vent) - // * Better estimate of available vent supply temp. - // Use prior step? (run vent at substep 0 even if not needed?) + // TODO ideas + // * Overall vent available flag Top.? (allows scheduling vent) + // * Better estimate of available vent supply temp. + // Use prior step? (run vent at substep 0 even if not needed?) -#if 0 && defined( _DEBUG) +#if 0 && defined(_DEBUG) bool bReportVent = Top.jDay == 178 /* && !Top.isWarmup */; #endif - int ventAvail = Top.tp_GetVentAvail(); // overall vent availability + int ventAvail = Top.tp_GetVentAvail(); // overall vent availability - Top.tp_fVent = 0.f; // consensus whole building vent fraction (if not RSYSOAV) - // = fraction of full vent flow to use - // determined by zone that wants the least vent - // or -1 for zonal vent control + Top.tp_fVent = 0.f; // consensus whole building vent fraction (if not RSYSOAV) + // = fraction of full vent flow to use + // determined by zone that wants the least vent + // or -1 for zonal vent control - if (ventAvail == C_VENTAVAILVC_WHOLEBLDG || ventAvail == C_VENTAVAILVC_ZONAL) - { - int nVentUseful = 0; // ventilation usefulness - // >0: vent potentially useful for 1 or more zones - // =0: vent not useful - // <0: vent forbidden by conditions in 1 or more zones - // e.g. heating or cooling required - RLUPC(ZrB, zp, !zp->zn_HasTerminal()) - { - int nVU1 = zp->zn_AssessVentUtility(); + if (ventAvail == C_VENTAVAILVC_WHOLEBLDG || + ventAvail == C_VENTAVAILVC_ZONAL) { + int nVentUseful = + 0; // ventilation usefulness + // >0: vent potentially useful for 1 or more zones + // =0: vent not useful + // <0: vent forbidden by conditions in 1 or more zones + // e.g. heating or cooling required + RLUPC(ZrB, zp, !zp->zn_HasTerminal()) { + int nVU1 = zp->zn_AssessVentUtility(); #if 0 && defined(_DEBUG) if (bReportVent) { // printf("\n%s Hr=%d, Zone '%s': VU=%d", Top.dateStr.CStr(), Top.iHr, zp->Name(), nVU1); @@ -604,85 +657,76 @@ x } zp->zn_AssessVentUtility(); // call again for debug } // break NO: zn_AssessVentUtility() needed for all zones -#endif - if (nVU1 > 0 || ventAvail != C_VENTAVAILVC_ZONAL) - nVentUseful += nVU1; - } - - int bAllNoVent = 1; - if (nVentUseful <= 0) - { if (Top.tp_pAirNet) - Top.tp_pAirNet->an_ClearResults(1); - } - else - { // vent might be useful for at least 1 zone - loadsIzxSh2(); // AirNet "vents open" - Top.tp_fVent = 1.f; // try full vent and limit - RLUP(ZrB, zp) - { - if (!zp->zn_IsUZ()) - { - int vRet = zp->zn_FVentCR(); - if (ventAvail == C_VENTAVAILVC_WHOLEBLDG) - { // whole building control - // if vent bad for any zone, all off - // else vent fract = minimum preferred - if (vRet < 0) - { Top.tp_fVent = 0.f; // vent is harmful for this zone: off for all - break; - } - else if (vRet > 0) - { if (zp->zn_fVentPrf < Top.tp_fVent) - Top.tp_fVent = zp->zn_fVentPrf; - bAllNoVent = 0; - } - // else vRet == 0 - // this zone does not care, do nothing - } - else - { // zonal control: each zone gets preferred - zp->zn_fVent = zp->zn_fVentPrf; - } - } - } - } - if (bAllNoVent) - Top.tp_fVent = 0.f; // whole-building vent fraction +#endif + if (nVU1 > 0 || ventAvail != C_VENTAVAILVC_ZONAL) + nVentUseful += nVU1; + } + + int bAllNoVent = 1; + if (nVentUseful <= 0) { + if (Top.tp_pAirNet) + Top.tp_pAirNet->an_ClearResults(1); + } else { // vent might be useful for at least 1 zone + loadsIzxSh2(); // AirNet "vents open" + Top.tp_fVent = 1.f; // try full vent and limit + RLUP(ZrB, zp) { + if (!zp->zn_IsUZ()) { + int vRet = zp->zn_FVentCR(); + if (ventAvail == C_VENTAVAILVC_WHOLEBLDG) { // whole building control + // if vent bad for any zone, all off + // else vent fract = minimum preferred + if (vRet < 0) { + Top.tp_fVent = 0.f; // vent is harmful for this zone: off for all + break; + } else if (vRet > 0) { + if (zp->zn_fVentPrf < Top.tp_fVent) + Top.tp_fVent = zp->zn_fVentPrf; + bAllNoVent = 0; + } + // else vRet == 0 + // this zone does not care, do nothing + } else { // zonal control: each zone gets preferred + zp->zn_fVent = zp->zn_fVentPrf; + } + } + } + } + if (bAllNoVent) + Top.tp_fVent = 0.f; // whole-building vent fraction -#if 0 && defined( _DEBUG) +#if 0 && defined(_DEBUG) if (bReportVent && !Top.isWarmup) printf("\n%s Hr=%d: NVU=%d ANV=%d TFvnt=%0.3f", Top.dateStr.CStr(), Top.iHr, nVentUseful, bAllNoVent, Top.tp_fVent); #endif + } - } - -#if defined( DEBUGDUMP) - if (bDbPrint) - DbPrintf( "\n"); +#if defined(DEBUGDUMP) + if (bDbPrint) + DbPrintf("\n"); #endif - RSYS* rs; - if (ventAvail == C_VENTAVAILVC_RSYSOAV) - { // attempt OAV for each system - // if successful, rs_mode = (zones)->zn_hcMode = rsmOAV (prevents other RSYS modeling) - // fVent = 0 if here - RLUP( RsR, rs) - rs->rs_OAVAttempt(); // ignore return (zn_hcMode / rs_mode retain outcome) - } + RSYS *rs; + if (ventAvail == C_VENTAVAILVC_RSYSOAV) { // attempt OAV for each system + // if successful, rs_mode = (zones)->zn_hcMode = rsmOAV (prevents other RSYS + // modeling) fVent = 0 if here + RLUP(RsR, rs) + rs->rs_OAVAttempt(); // ignore return (zn_hcMode / rs_mode retain outcome) + } - RLUP( ZrB, zp) - rc |= zp->zn_CondixCR( ventAvail); // duct leakage can add cross-zone air - // do all zones before zn_AirXMoistureBal() etc. + RLUP(ZrB, zp) + rc |= zp->zn_CondixCR( + ventAvail); // duct leakage can add cross-zone air + // do all zones before zn_AirXMoistureBal() etc. - RLUP( RsR, rs) - rc |= rs->rs_AllocateZoneAir(); // NOP if OAV + RLUP(RsR, rs) + rc |= rs->rs_AllocateZoneAir(); // NOP if OAV -#if defined( CRTERMAH) -// may extrapolate tz if load unchanged -// subhour terminal/airhandler hvac - rc |= hvacIterSubhr(); +#if defined(CRTERMAH) + // may extrapolate tz if load unchanged + // subhour terminal/airhandler hvac + rc |= hvacIterSubhr(); -#if 0 && defined( _DEBUG) +#if 0 && defined(_DEBUG) if (Top.jDay == 1) { if (zp1 && abs( zp1->zn_qsHvac) < 0.01) @@ -691,75 +735,72 @@ x } } #endif -#endif // CRTERMAH +#endif // CRTERMAH - RLUP( ZrB, zp) - rc |= zp->zn_CondixCR2(); // finalize zone for all modes (including OAV) + RLUP(ZrB, zp) + rc |= zp->zn_CondixCR2(); // finalize zone for all modes (including OAV) - RLUP( RsR, rs) - rc |= rs->rs_FinalizeSh(); + RLUP(RsR, rs) + rc |= rs->rs_FinalizeSh(); - RLUP( ZrB, zp) - { - if (zp->zn_IsConvRad()) - zp->zn_AirXMoistureBal(true); - rc |= zp->zn_ComfortCR(); -#if !defined( CRTERMAH) - rc |= zp->zn_LoadsSubhr( azCf); // insurance, may not be needed for CR + RLUP(ZrB, zp) { + if (zp->zn_IsConvRad()) + zp->zn_AirXMoistureBal(true); + rc |= zp->zn_ComfortCR(); +#if !defined(CRTERMAH) + rc |= zp->zn_LoadsSubhr(azCf); // insurance, may not be needed for CR #endif -#if defined( DEBUGDUMP) - if (bDbPrint) - zp->zn_DbDump(); +#if defined(DEBUGDUMP) + if (bDbPrint) + zp->zn_DbDump(); #endif - } + } -#if defined( DEBUGDUMP) - if (DbDo(dbdAIRNET | dbdIZ) && ZrB.GetCount() > 0) - { DbPrintf("\nZone airnet results\n" - "Zone pz0W[0] pz0W[1] pz0 qIzSh AmfCp AmfCpT\n"); - RLUP(ZrB, zp) - DbPrintf("%-20.20s %8.5f %8.5f %8.5f %10.2f %8.2f %10.1f\n", - zp->Name(), zp->zn_pz0W[0], zp->zn_pz0W[1], zp->zn_pz0, zp->zn_qIzSh, - zp->zn_AnAmfCp(0), zp->zn_AnAmfCpT(0)); - } +#if defined(DEBUGDUMP) + if (DbDo(dbdAIRNET | dbdIZ) && ZrB.GetCount() > 0) { + DbPrintf("\nZone airnet results\n" + "Zone pz0W[0] pz0W[1] pz0 qIzSh " + "AmfCp AmfCpT\n"); + RLUP(ZrB, zp) + DbPrintf("%-20.20s %8.5f %8.5f %8.5f %10.2f %8.2f %10.1f\n", zp->Name(), + zp->zn_pz0W[0], zp->zn_pz0W[1], zp->zn_pz0, zp->zn_qIzSh, + zp->zn_AnAmfCp(0), zp->zn_AnAmfCpT(0)); + } #endif - TMRSTOP( TMR_ZONE); + TMRSTOP(TMR_ZONE); - return rc; -} // loadsSubhr + return rc; +} // loadsSubhr //---------------------------------------------------------------------------- -RC ZNR::zn_InitSubhr() -{ - // derive working setpoints. - // done unconditionally altho not always used. - if (Top.tp_autoSizing) - { // avoid setpoint step changes when autosizing - // assume zn_tzspXbs changes hourly (altho they have subhourly variability) - // ramp from prior value over the hour - float f = float(Top.iSubhr + 1) / Top.tp_nSubSteps; - zn_tzspH = (1.f - f) * zn_tzspHlh + f * i.znTH; - zn_tzspD = (1.f - f) * zn_tzspDlh + f * i.znTD; - zn_tzspC = (1.f - f) * zn_tzspClh + f * i.znTC; - } - else - { // not autosizing: use setpoints as input - zn_tzspH = i.znTH; - zn_tzspD = i.znTD; - zn_tzspC = i.znTC; - } - - if (zn_UsesZoneSetpoints() && zn_tzspH >= zn_tzspC) - orer("Impossible setpoints -- znTH (%0.2f) >= znTC (%.2f)", zn_tzspH, zn_tzspC); - - // subhr's Infil UA (Btuh/F) -#if 1 // 4-17-2013 - // infiltration UA based on AMF of *zone* air - // UA (Btuh/F) = dry amf (lbm/hr) * spec ht (Btu/lbmDry-F) - // (could derive spec ht from current humrat, but effect is minor) - // (formerly based on outdoor air state, 4-17-2013) - zn_uaInfil = zn_NonAnIVAmf() * Top.tp_airSH; -#if 0 && defined( _DEBUG) +RC ZNR::zn_InitSubhr() { + // derive working setpoints. + // done unconditionally altho not always used. + if (Top.tp_autoSizing) { // avoid setpoint step changes when autosizing + // assume zn_tzspXbs changes hourly (altho they have subhourly variability) + // ramp from prior value over the hour + float f = float(Top.iSubhr + 1) / Top.tp_nSubSteps; + zn_tzspH = (1.f - f) * zn_tzspHlh + f * i.znTH; + zn_tzspD = (1.f - f) * zn_tzspDlh + f * i.znTD; + zn_tzspC = (1.f - f) * zn_tzspClh + f * i.znTC; + } else { // not autosizing: use setpoints as input + zn_tzspH = i.znTH; + zn_tzspD = i.znTD; + zn_tzspC = i.znTC; + } + + if (zn_UsesZoneSetpoints() && zn_tzspH >= zn_tzspC) + orer("Impossible setpoints -- znTH (%0.2f) >= znTC (%.2f)", zn_tzspH, + zn_tzspC); + + // subhr's Infil UA (Btuh/F) +#if 1 // 4-17-2013 + // infiltration UA based on AMF of *zone* air + // UA (Btuh/F) = dry amf (lbm/hr) * spec ht (Btu/lbmDry-F) + // (could derive spec ht from current humrat, but effect is minor) + // (formerly based on outdoor air state, 4-17-2013) + zn_uaInfil = zn_NonAnIVAmf() * Top.tp_airSH; +#if 0 && defined(_DEBUG) float uaInfilX = Top.tp_airxOSh // Btuh/cfm-F air flow heat xfer at current outdoor temp * ( i.znVol // air changes/hr part: zone volume (ft3), times... * i.infAC / 60. // user input air changes per hour, hourly variable, /60 for per minute. @@ -772,225 +813,246 @@ RC ZNR::zn_InitSubhr() printf( "uaInfil mismatch!\n"); #endif #else - zn_uaInfil = Top.tp_airxOSh // Btuh/cfm-F air flow heat xfer at current outdoor temp - * ( i.znVol // air changes/hr part: zone volume (ft3), times... - * i.infAC / 60. // user input air changes per hour, hourly variable, /60 for per minute. - + i.infELA // ELA part: user input effective leakage area (ft2), hourly variable - * sqrt( // flow is proportional to square root of - zn_stackc * fabs( tzlh - Top.tDbOSh) + // stack coeff (see ZNR.zn_InfilSetup) * delta t - zn_windc * Top.windSpeedSquaredSh ) // wind coeff (ditto) * wind speed ^ 2 - ); -#endif - - // for hvac code, compute a+q, b contributions this subhour for everything but hvac, for use in "Tz = (a + q)/b" - // a+q = sigma(UAi*Ti) + other q's + Told*hc/t - zn_qSgAir = zn_sgAirTarg.st_tot; // solar gain to air - zn_aqLdSh = zn_aqLdHr // hour-constant part: intl gain, solar gain, cond to ambient & specT's, etc. - + aMassSh // subhourly masses UAT (from loadsSurfaces) - + Top.tDbOSh * (zn_ua + zn_uaInfil) // temp * (constUA + infilUA) - + zn_qSgAir // q's: solar gain (power, Btuh) to zone (air) - + zn_qIzSh // interzone transfers to zone (q's), just set by loadsIzxSubhr - // (included zn_qIzXAnSh + zn_anAmfCp[ 0]*deltaT) - + tzls * i.znCAirSh // Told*hc/t - + znXLGainLs; // excess latent gain last subhr === heat of condensation. 5-97. - - // b = sigma(UAi) + hc/t - zn_bLdSh = zn_bLdHr // non-hvac UA's constant for hour - + zn_uaInfil // + infil UA this subhr - + i.znCAirSh; // + hc/t: .znCAirSh is hc. - // "cair" is heat capac of everything in zone not modelled as "mass": - // user const & per-ft2 parts for air, furniture, walls, etc). - -#if !defined( CRTERMAH) - /* zn_xqSh: q = b * t - a using components avail here, for change tests only: - Intended to be appropriately sensitive to changes when b*a almost equal to a, - since terminal loads are of form b * t - a. - Insurance 12-94 -- need was not proven at subhour level when all masses hourly (b4 aMassSh added). - But it did something: eliminated undercooling in hour 13 in S1LOH test run, 12-5-94, so keep. */ - zn_xqSh = zn_bLdSh * tzls - zn_aqLdSh; -#endif - - // convective-radiant zone terms - double nAirSh = - i.znCAirSh*tzls // air heat cap - + qsIgHr // convective internal gains - + zn_AnAmfCpT( 0) - + zn_qIzXAnSh // non-airnet interzone - + znXLGainLs // excess latent gain last subhr === heat of condensation - + zn_uaInfil*Top.tDbOSh; // non-airnet infiltration - zn_nAirSh += nAirSh; - - double dAirSh = // associated denominator terms - i.znCAirSh - + zn_AnAmfCp( 0) - + zn_uaInfil; - zn_dAirSh += dAirSh; - - double nRadSh = - qrIgTot; // radiant internal gains - - zn_nRadSh += nRadSh; - - double dRadSh = 0.; // nothing additional - // zn_dRadSh += dRadSh; - - zn_airCx = zn_airCxF*pow3( DegFtoR( tzls)); - zn_cxSh += zn_airCx; - if (zn_cxSh < .001) - zn_cxSh = .001; - - // useful combinations of terms - zn_dRpCx = zn_dRadSh + zn_cxSh; - zn_nRxCx = zn_nRadSh * zn_cxSh; - zn_dRxCx = zn_dRadSh * zn_cxSh; - - zn_balC1 = zn_nAirSh*zn_dRpCx + zn_nRxCx; - zn_balC2 = zn_dAirSh*zn_dRpCx + zn_dRxCx; - -#if defined( CRTERMAH) - if (zn_IsConvRad()) - { // convective/radiant: set up CNE-style zone factors - zn_aqLdSh = zn_balC1 / zn_dRpCx; - zn_bLdSh = zn_balC2 / zn_dRpCx; - } - - // zn_xqSh: q = b * t - a using components avail here, for change tests only: - // Intended to be appropriately sensitive to changes when b*a almost equal to a, - // since terminal loads are of form b * t - a. - // Insurance 12-94 -- need was not proven at subhour level when all masses hourly (b4 aMassSh added). - // But it did something: eliminated undercooling in hour 13 in S1LOH test run, 12-5-94, so keep. */ - zn_xqSh = zn_bLdSh * tzls - zn_aqLdSh; -#endif - - zn_hcMode = RSYS::rsmOFF; - -#if defined( DEBUGDUMP) - if (DbDo( dbdRCM)) - DbPrintf( "%s air qsIgHr=%0.1f qrIgTot=%0.1f uaInfil=%0.1f\n" - " nAir=%0.2f dAir=%0.2f nRad=%0.2f dRad=%0.2f CxF=%0.6g CX=%0.2f\n", - Name(), qsIgHr, qrIgTot, zn_uaInfil, - nAirSh, dAirSh, nRadSh, dRadSh, zn_airCxF, zn_airCx); -#endif - return RCOK; -} // ZNR::zn_InitSubhr + zn_uaInfil = + Top.tp_airxOSh // Btuh/cfm-F air flow heat xfer at current outdoor temp + * + (i.znVol // air changes/hr part: zone volume (ft3), times... + * i.infAC / 60. // user input air changes per hour, hourly variable, + // /60 for per minute. + + + i.infELA // ELA part: user input effective leakage area (ft2), hourly + // variable + * sqrt( // flow is proportional to square root of + zn_stackc * + fabs(tzlh - Top.tDbOSh) + // stack coeff (see + // ZNR.zn_InfilSetup) * delta t + zn_windc * Top.windSpeedSquaredSh) // wind coeff (ditto) * + // wind speed ^ 2 + ); +#endif + + // for hvac code, compute a+q, b contributions this subhour for everything but + // hvac, for use in "Tz = (a + q)/b" a+q = sigma(UAi*Ti) + other q's + + // Told*hc/t + zn_qSgAir = zn_sgAirTarg.st_tot; // solar gain to air + zn_aqLdSh = + zn_aqLdHr // hour-constant part: intl gain, solar gain, cond to ambient & + // specT's, etc. + + aMassSh // subhourly masses UAT (from loadsSurfaces) + + Top.tDbOSh * (zn_ua + zn_uaInfil) // temp * (constUA + infilUA) + + zn_qSgAir // q's: solar gain (power, Btuh) to zone (air) + + zn_qIzSh // interzone transfers to zone (q's), just set by loadsIzxSubhr + // (included zn_qIzXAnSh + zn_anAmfCp[ 0]*deltaT) + + tzls * i.znCAirSh // Told*hc/t + + znXLGainLs; // excess latent gain last subhr === heat of condensation. + // 5-97. + + // b = sigma(UAi) + hc/t + zn_bLdSh = zn_bLdHr // non-hvac UA's constant for hour + + zn_uaInfil // + infil UA this subhr + + i.znCAirSh; // + hc/t: .znCAirSh is hc. + // "cair" is heat capac of everything in zone not modelled as "mass": + // user const & per-ft2 parts for air, furniture, walls, etc). + +#if !defined(CRTERMAH) + /* zn_xqSh: q = b * t - a using components avail here, for change tests only: + Intended to be appropriately sensitive to changes when b*a almost + equal to a, since terminal loads are of form b * t - a. Insurance 12-94 -- + need was not proven at subhour level when all masses hourly (b4 aMassSh + added). But it did something: eliminated undercooling in hour 13 in S1LOH + test run, 12-5-94, so keep. */ + zn_xqSh = zn_bLdSh * tzls - zn_aqLdSh; +#endif + + // convective-radiant zone terms + double nAirSh = + i.znCAirSh * tzls // air heat cap + + qsIgHr // convective internal gains + + zn_AnAmfCpT(0) + zn_qIzXAnSh // non-airnet interzone + + znXLGainLs // excess latent gain last subhr === heat of condensation + + zn_uaInfil * Top.tDbOSh; // non-airnet infiltration + zn_nAirSh += nAirSh; + + double dAirSh = // associated denominator terms + i.znCAirSh + zn_AnAmfCp(0) + zn_uaInfil; + zn_dAirSh += dAirSh; + + double nRadSh = qrIgTot; // radiant internal gains + + zn_nRadSh += nRadSh; + + double dRadSh = 0.; // nothing additional + // zn_dRadSh += dRadSh; + + zn_airCx = zn_airCxF * pow3(DegFtoR(tzls)); + zn_cxSh += zn_airCx; + if (zn_cxSh < .001) + zn_cxSh = .001; + + // useful combinations of terms + zn_dRpCx = zn_dRadSh + zn_cxSh; + zn_nRxCx = zn_nRadSh * zn_cxSh; + zn_dRxCx = zn_dRadSh * zn_cxSh; + + zn_balC1 = zn_nAirSh * zn_dRpCx + zn_nRxCx; + zn_balC2 = zn_dAirSh * zn_dRpCx + zn_dRxCx; + +#if defined(CRTERMAH) + if (zn_IsConvRad()) { // convective/radiant: set up CNE-style zone factors + zn_aqLdSh = zn_balC1 / zn_dRpCx; + zn_bLdSh = zn_balC2 / zn_dRpCx; + } + + // zn_xqSh: q = b * t - a using components avail here, for change tests only: + // Intended to be appropriately sensitive to changes when b*a almost equal to + // a, since terminal loads are of form b * t - a. Insurance 12-94 -- need + // was not proven at subhour level when all masses hourly (b4 aMassSh added). + // But it did something: eliminated undercooling in hour 13 in S1LOH test + // run, 12-5-94, so keep. */ + zn_xqSh = zn_bLdSh * tzls - zn_aqLdSh; +#endif + + zn_hcMode = RSYS::rsmOFF; + +#if defined(DEBUGDUMP) + if (DbDo(dbdRCM)) + DbPrintf("%s air qsIgHr=%0.1f qrIgTot=%0.1f uaInfil=%0.1f\n" + " nAir=%0.2f dAir=%0.2f nRad=%0.2f dRad=%0.2f CxF=%0.6g " + "CX=%0.2f\n", + Name(), qsIgHr, qrIgTot, zn_uaInfil, nAirSh, dAirSh, nRadSh, + dRadSh, zn_airCxF, zn_airCx); +#endif + return RCOK; +} // ZNR::zn_InitSubhr //----------------------------------------------------------------------------- RC ZNR::zn_SetZtuCfIf( - BOO azCf) // TRUE iff "all-zones change" e.g. outdoor w changed -{ - - // set change flag for terminal module if zone loads changed. Cleared in ZNR::ztuCompute. - - // monitor subhourly-changing loads inputs to zone heat and moisture balances except subhrDur: - // heat balance: aqLdSh: tzls, zn_qIzSh; zn_aqLdHr is hourly; znCair is constant. zn_bLdSh: changes only per subhrDur. - // zn_xqSh - // moisture balance: wzls. znLGain is hourly. - // other: md - - if (ztuCf // if a flag already set, skip tests, update priors to avoid unnec flagging later. - || azCf // if all-zones change detected (outdoor w, above) - || spCf // spCf implies ztuCf (and more), 6-92. - || RELCHANGE(zn_xqSh, zn_xqShPr) > .02 * Top.relTol * MARG2 // b*t - a as known here: difference sensitive. - // .02 copied from hour case. Probably new dominant term; - // may make some of the other checks unnec 12-5-94. - || ABSCHANGE(tzls, tzlsPr) > Top.relTol * 2. * MARG2 // Dominant term. MATCH above. use relTol as small absTol. - // historical 7-13-92 (b4 zn_bLdHr ck above): .002 - // .005 (w/o mdPr check) --> .0001 ebal errs (T12) 4-92. - // (without tz += tzlsDelta below, < .001 required here) - || ABSCHANGE(wzls, wzlsPr) > .2 * Top.absHumTol // MATCH tolerance above. dflt .000006, or as changed. - // .00002 (vs what??) adds iterations; - // .00001 adds even more; otherwise unexplored. - || zn_md != zn_mdPr // if zn hvac mode changed last subhr, don't skip tu's now: - // eg be sure tz right on change into a setpoint mode. - || RELCHANGE(zn_qIzSh, qIzShPr) > .00005) - { - ztuCf++; // say zone's load changed. - qIzShPr = zn_qIzSh; // reset comparators (Prior values), whenever (and only when) flag set. - tzlsPr = tzls; - wzlsPr = wzls; - zn_mdPr = zn_md; - zn_xqHrPr = zn_xqHr; // ditto; hourly part of b*t - a 12-5-94. - zn_xqShPr = zn_xqSh; // ditto; as much of b*t - a as known here 12-5-94 - znLGainPr = znLGain; // ditto - } - else // load not changed - { - // extrapolate zone temp in case ztuCompute not invoked elsewhere: - // this is zone/terminal item that changes most when not zone changing much; - // other items, such as zn_qsHvac, remain approx same for same tz rate of change. 4-92 - aTz = // ah working copy of tz: init same as tz - tz += tzlsDelta; // extrapolate zone temp - - // Also extraploate zone w cuz znLGain and qlMech continue. - // Tried removing in 6-92, restoring seemed to reduce # subhours & # iterations in test runs. - aWz = // ah working copy - wz += wzlsDelta; // also extrap zone humidity ratio - } - - return RCOK; - -} // ZNR::zn_SetZtuCfIf + BOO azCf) // TRUE iff "all-zones change" e.g. outdoor w changed +{ + + // set change flag for terminal module if zone loads changed. Cleared in + // ZNR::ztuCompute. + + // monitor subhourly-changing loads inputs to zone heat and moisture balances + // except subhrDur: + // heat balance: aqLdSh: tzls, zn_qIzSh; zn_aqLdHr is hourly; znCair is + // constant. zn_bLdSh: changes only per subhrDur. + // zn_xqSh + // moisture balance: wzls. znLGain is hourly. + // other: md + + if (ztuCf // if a flag already set, skip tests, update priors to avoid unnec + // flagging later. + || azCf // if all-zones change detected (outdoor w, above) + || spCf // spCf implies ztuCf (and more), 6-92. + || RELCHANGE(zn_xqSh, zn_xqShPr) > + .02 * Top.relTol * + MARG2 // b*t - a as known here: difference sensitive. + // .02 copied from hour case. Probably new dominant term; + // may make some of the other checks unnec 12-5-94. + || + ABSCHANGE(tzls, tzlsPr) > + Top.relTol * 2. * + MARG2 // Dominant term. MATCH above. use relTol as small absTol. + // historical 7-13-92 (b4 zn_bLdHr ck above): .002 + // .005 (w/o mdPr check) --> .0001 ebal errs (T12) 4-92. + // (without tz += tzlsDelta below, < .001 required here) + || ABSCHANGE(wzls, wzlsPr) > + .2 * Top.absHumTol // MATCH tolerance above. dflt .000006, or as + // changed. .00002 (vs what??) adds iterations; + // .00001 adds even more; otherwise unexplored. + || zn_md != + zn_mdPr // if zn hvac mode changed last subhr, don't skip tu's now: + // eg be sure tz right on change into a setpoint mode. + || RELCHANGE(zn_qIzSh, qIzShPr) > .00005) { + ztuCf++; // say zone's load changed. + qIzShPr = zn_qIzSh; // reset comparators (Prior values), whenever (and only + // when) flag set. + tzlsPr = tzls; + wzlsPr = wzls; + zn_mdPr = zn_md; + zn_xqHrPr = zn_xqHr; // ditto; hourly part of b*t - a 12-5-94. + zn_xqShPr = zn_xqSh; // ditto; as much of b*t - a as known here 12-5-94 + znLGainPr = znLGain; // ditto + } else // load not changed + { + // extrapolate zone temp in case ztuCompute not invoked elsewhere: + // this is zone/terminal item that changes most when not zone changing much; + // other items, such as zn_qsHvac, remain approx same for same tz rate of + // change. 4-92 + aTz = // ah working copy of tz: init same as tz + tz += tzlsDelta; // extrapolate zone temp + + // Also extraploate zone w cuz znLGain and qlMech continue. + // Tried removing in 6-92, restoring seemed to reduce # subhours & # + // iterations in test runs. + aWz = // ah working copy + wz += wzlsDelta; // also extrap zone humidity ratio + } + + return RCOK; + +} // ZNR::zn_SetZtuCfIf //----------------------------------------------------------------------------- void ZNR::zn_CoupleDHWLossSubhr( - double qLoss, // loss rate from DHWHEATER, DHWTANK, etc., Btuh - float fR /*=0.5f*/) // fraction radiant + double qLoss, // loss rate from DHWHEATER, DHWTANK, etc., Btuh + float fR /*=0.5f*/) // fraction radiant // incorporates zone heat gain (calc'd by DHW models) into heat balance terms { - // TODO: 50/50 conc/rad split is approx at best - zn_qDHWLossRad += qLoss * fR; - zn_qDHWLossAir += qLoss * (1.f - fR); - - zn_qDHWLoss = zn_qDHWLossRad + zn_qDHWLossAir; + // TODO: 50/50 conc/rad split is approx at best + zn_qDHWLossRad += qLoss * fR; + zn_qDHWLossAir += qLoss * (1.f - fR); + + zn_qDHWLoss = zn_qDHWLossRad + zn_qDHWLossAir; -} // ZNR::zn_CoupleDHWLossSubhr +} // ZNR::zn_CoupleDHWLossSubhr //----------------------------------------------------------------------------- -void ZNR::zn_DbDump() const -{ - // int nhour = (Top.jDay-1)*24 + Top.iHr; - // int stepno = Top.iSubhr+1; - DbPrintf("%s %s: anMCp/T[ 0]=%.2f/%.1f anMCp/T[ 1]=%.2f/%.1f ventUt=%d\n", - Name(), zn_IsUZ() ? "UZ" : "CZ", - zn_AnAmfCp( 0), zn_AnAmfCpT( 0), zn_AnAmfCp( 1), zn_AnAmfCpT( 1), - zn_ventUt); - DbPrintf(" Nair=%.2f Dair=%.2f Nrad=%.2f Drad=%.2f CX=%.2f airX=%.3f\n", - zn_nAirSh, zn_dAirSh, zn_nRadSh, zn_dRadSh, zn_cxSh, i.zn_hcAirX); - if (!zn_IsUZ()) - { DbPrintf(" TH=%.2f TD=%.2f TC=%.2f", - zn_tzspH, zn_tzspD, zn_tzspC); - if (!zn_HasRSYS()) - DbPrintf(" qhCap=%.f qcCap=%.f\n", i.znQMxH, i.znQMxC); - else - DbPrintf(" tSP=%.2f md=%d amfReq=%.f amfSup=%.f tSup=%.2f\n", - zn_tzsp, zn_hcMode, zn_rsAmfSysReq[ 0], zn_sysAirI.af_amf, zn_sysAirI.as_tdb); - } - DbPrintf( " ta=%.2f tr=%.2f qIzSh=%.f fvent=%.3f pz0=%.4f qsHvac=%.f\n", - tz, zn_tr, zn_qIzSh, zn_fVent, zn_pz0, zn_qsHvac); -} // zn_DbDump +void ZNR::zn_DbDump() const { + // int nhour = (Top.jDay-1)*24 + Top.iHr; + // int stepno = Top.iSubhr+1; + DbPrintf("%s %s: anMCp/T[ 0]=%.2f/%.1f anMCp/T[ 1]=%.2f/%.1f ventUt=%d\n", + Name(), zn_IsUZ() ? "UZ" : "CZ", zn_AnAmfCp(0), zn_AnAmfCpT(0), + zn_AnAmfCp(1), zn_AnAmfCpT(1), zn_ventUt); + DbPrintf( + " Nair=%.2f Dair=%.2f Nrad=%.2f Drad=%.2f CX=%.2f airX=%.3f\n", + zn_nAirSh, zn_dAirSh, zn_nRadSh, zn_dRadSh, zn_cxSh, i.zn_hcAirX); + if (!zn_IsUZ()) { + DbPrintf(" TH=%.2f TD=%.2f TC=%.2f", zn_tzspH, zn_tzspD, zn_tzspC); + if (!zn_HasRSYS()) + DbPrintf(" qhCap=%.f qcCap=%.f\n", i.znQMxH, i.znQMxC); + else + DbPrintf(" tSP=%.2f md=%d amfReq=%.f amfSup=%.f tSup=%.2f\n", + zn_tzsp, zn_hcMode, zn_rsAmfSysReq[0], zn_sysAirI.af_amf, + zn_sysAirI.as_tdb); + } + DbPrintf( + " ta=%.2f tr=%.2f qIzSh=%.f fvent=%.3f pz0=%.4f qsHvac=%.f\n", + tz, zn_tr, zn_qIzSh, zn_fVent, zn_pz0, zn_qsHvac); +} // zn_DbDump //----------------------------------------------------------------------------- -RSYS* ZNR::zn_GetRSYS() -{ - RSYS* rs = RsR.GetAtSafe( i.zn_rsi); - return rs; -} // ZNR::zn_GetRSYS +RSYS *ZNR::zn_GetRSYS() { + RSYS *rs = RsR.GetAtSafe(i.zn_rsi); + return rs; +} // ZNR::zn_GetRSYS //----------------------------------------------------------------------------- -const RSYS* ZNR::zn_GetRSYS() const -{ - const RSYS* rs = RsR.GetAtSafe( i.zn_rsi); - return rs; -} // ZNR::zn_GetRSYS +const RSYS *ZNR::zn_GetRSYS() const { + const RSYS *rs = RsR.GetAtSafe(i.zn_rsi); + return rs; +} // ZNR::zn_GetRSYS //----------------------------------------------------------------------------- -int ZNR::zn_IsHCAvail( // determine system availability - [[maybe_unused]] int rsMode) const // capability required: rsmHEAT, rsmCOOL +int ZNR::zn_IsHCAvail( // determine system availability + [[maybe_unused]] int rsMode) const // capability required: rsmHEAT, rsmCOOL // returns 1 if specified capability is available -{ return !zn_IsUZ(); // TODO: schedule availability? -} // ZNR::zn_HCAvail +{ + return !zn_IsUZ(); // TODO: schedule availability? +} // ZNR::zn_HCAvail //----------------------------------------------------------------------------- -float ZNR::zn_VentTSup() // vent supply temp under current condtions +float ZNR::zn_VentTSup() // vent supply temp under current condtions // returns vent supply temp (at zone), F { - return Top.tDbOSh; // TODO not quite right (fan heat?) -} // ZNR::zn_VentTSup + return Top.tDbOSh; // TODO not quite right (fan heat?) +} // ZNR::zn_VentTSup //---------------------------------------------------------------------------- -int ZNR::zn_AssessVentUtility() // assess vent utility +int ZNR::zn_AssessVentUtility() // assess vent utility // tz assumed set to current floating zone temp @@ -1002,278 +1064,260 @@ int ZNR::zn_AssessVentUtility() // assess vent utility // always returns 0 for unconditioned zones (even if vent off) // 1: might be helpful { -const int vForbid = -9999; - int ventUt = 0; // init to "don't care" - if (!zn_IsUZ() && zn_anVentEffect > 0) - { - if (zn_tzspD < 0.f) - { - orer("znTD is needed re vent control but has not been set."); - ventUt = vForbid; - } - else if (tz < zn_tzspD) - ventUt = vForbid; // vent off or zone temp below TD (any vent would hurt) - else if (tz > zn_tzspD) - { // zone temp is above TD - // vent might be useful - float tvSup = zn_VentTSup(); - if (tvSup > tz) - ventUt = vForbid; // venting would add heat, forbid - else if (tvSup > zn_tzspC && zn_IsHCAvail( RSYS::rsmCOOL)) - ventUt = vForbid; // venting cannot prevent cooling - else - ventUt = 1; // venting might help get to TD - } - // else don't care - } - zn_ventUt = max(ventUt, -1); - return ventUt; -} // ZNR::zn_AssessVentUtility + const int vForbid = -9999; + int ventUt = 0; // init to "don't care" + if (!zn_IsUZ() && zn_anVentEffect > 0) { + if (zn_tzspD < 0.f) { + orer("znTD is needed re vent control but has not been set."); + ventUt = vForbid; + } else if (tz < zn_tzspD) + ventUt = vForbid; // vent off or zone temp below TD (any vent would hurt) + else if (tz > zn_tzspD) { // zone temp is above TD + // vent might be useful + float tvSup = zn_VentTSup(); + if (tvSup > tz) + ventUt = vForbid; // venting would add heat, forbid + else if (tvSup > zn_tzspC && zn_IsHCAvail(RSYS::rsmCOOL)) + ventUt = vForbid; // venting cannot prevent cooling + else + ventUt = 1; // venting might help get to TD + } + // else don't care + } + zn_ventUt = max(ventUt, -1); + return ventUt; +} // ZNR::zn_AssessVentUtility //----------------------------------------------------------------------------- -int ZNR::zn_FVentCR() // find zone's preferred vent fraction +int ZNR::zn_FVentCR() // find zone's preferred vent fraction // assume tz is at floating temp // sets zn_fVentPrf = best vent fraction for this zone in isolation (0 - 1) // returns -1: vent is harmful // 0: no effect / don't care // 1: useful { -#if 0 && defined( _DEBUG) +#if 0 && defined(_DEBUG) if (Top.jDay == 178) printf("\nzn_FVentCR '%s'", Name()); #endif - int ret = 0; - zn_fVentPrf = 0.f; - - double anAmfCpT = zn_AnAmfCpT( 1) - zn_AnAmfCpT( 0); - if (fabs( anAmfCpT) < .001) - { // zn_fVentPrf = 0.f; // other zones free to use max - zn_tzVent = 0.f; // vent air temp not known - ret = 0; // vent has no effect on this zone - } - else - { double anAmfCp = zn_AnAmfCp(1) - zn_AnAmfCp(0); - zn_tzVent = zn_TAirCR( anAmfCpT, anAmfCp); // zone air temp at full vent (do not set tz) - if (zn_tzVent > zn_tzspC && zn_IsHCAvail( RSYS::rsmCOOL)) - ret = -1; // zn_fVentPrf = 0 - else if (zn_tzVent > zn_tzspD) - { zn_fVentPrf = 1.f; - ret = 1; - } - else - { double amfDT = anAmfCpT - anAmfCp * zn_tzspD; - if (amfDT != 0.) - { double qVent = zn_QAirCR( zn_tzspD); // q required to keep tz at zn_tzspD - zn_fVentPrf = qVent / amfDT; - if (zn_fVentPrf < 0.f) - { -#if 0 && defined( _DEBUG) + int ret = 0; + zn_fVentPrf = 0.f; + + double anAmfCpT = zn_AnAmfCpT(1) - zn_AnAmfCpT(0); + if (fabs(anAmfCpT) < + .001) { // zn_fVentPrf = 0.f; // other zones free to use max + zn_tzVent = 0.f; // vent air temp not known + ret = 0; // vent has no effect on this zone + } else { + double anAmfCp = zn_AnAmfCp(1) - zn_AnAmfCp(0); + zn_tzVent = zn_TAirCR( + anAmfCpT, anAmfCp); // zone air temp at full vent (do not set tz) + if (zn_tzVent > zn_tzspC && zn_IsHCAvail(RSYS::rsmCOOL)) + ret = -1; // zn_fVentPrf = 0 + else if (zn_tzVent > zn_tzspD) { + zn_fVentPrf = 1.f; + ret = 1; + } else { + double amfDT = anAmfCpT - anAmfCp * zn_tzspD; + if (amfDT != 0.) { + double qVent = zn_QAirCR(zn_tzspD); // q required to keep tz at zn_tzspD + zn_fVentPrf = qVent / amfDT; + if (zn_fVentPrf < 0.f) { +#if 0 && defined(_DEBUG) orWarn("fVent < 0\n"); #endif - zn_fVentPrf = 0.f; - } - else if (zn_fVentPrf > 1.f) - zn_fVentPrf = 1.f; - } - ret = 1; - } - } - return ret; -} // ZNR::zn_FVentCR + zn_fVentPrf = 0.f; + } else if (zn_fVentPrf > 1.f) + zn_fVentPrf = 1.f; + } + ret = 1; + } + } + return ret; +} // ZNR::zn_FVentCR //----------------------------------------------------------------------------- -RC ZNR::zn_CondixCR( // zone conditions part 1, convective/radiant model - int ventAvail) // vent availability - // C_VENTAVAILVC_WHOLEBLDG, C_VENTAVAILVC_ZONAL +RC ZNR::zn_CondixCR( // zone conditions part 1, convective/radiant model + int ventAvail) // vent availability + // C_VENTAVAILVC_WHOLEBLDG, C_VENTAVAILVC_ZONAL // determines tz, tr, zn_qsHvac, zn_qIzSh // returns RCOK or ... { - RC rc = RCOK; + RC rc = RCOK; -#if 0 && defined( _DEBUG) +#if 0 && defined(_DEBUG) x if (!Top.isWarmup) x { if (Top.jDay==31 && strMatch( Name(), "SDuctZone")) x printf( "Hit\n"); x } #endif -#if defined( CRTERMAH) - zn_rsAmfSysReq[ 0] = zn_rsAmfSysReq[ 1] = 0.; // RSYS air requests +#if defined(CRTERMAH) + zn_rsAmfSysReq[0] = zn_rsAmfSysReq[1] = 0.; // RSYS air requests - zn_anAmfCpVent = 0.; // full vent heat rate, Btuh/F - zn_anAmfCpTVent = 0.; // full vent heat addition, Btuh + zn_anAmfCpVent = 0.; // full vent heat rate, Btuh/F + zn_anAmfCpTVent = 0.; // full vent heat addition, Btuh - if (zn_HasTerminal()) - { - zn_fVent = 0.f; // no vent - zn_pz0 = zn_pz0W[0]; + if (zn_HasTerminal()) { + zn_fVent = 0.f; // no vent + zn_pz0 = zn_pz0W[0]; - return rc; - } + return rc; + } - // zn_fVent = fVent -- don't change until end (zone value used herein) - zn_qsHvac = 0.; - zn_qlHvac = 0.; -#if defined( ZNHVACFCONV) - zn_fConv = 1.f; // HVAC convective fraction + // zn_fVent = fVent -- don't change until end (zone value used herein) + zn_qsHvac = 0.; + zn_qlHvac = 0.; +#if defined(ZNHVACFCONV) + zn_fConv = 1.f; // HVAC convective fraction #endif #else - // zn_fVent = fVent -- don't change until end (zone value used herein) - zn_qsHvac = 0.; - zn_qlHvac = 0.; -#if defined( ZNHVACFCONV) - zn_fConv = 1.f; // HVAC convective fraction -#endif - zn_rsAmfSysReq[0] = zn_rsAmfSysReq[1] = 0.; // RSYS air requests - - zn_anAmfCpVent = 0.; // full vent heat rate, Btuh/F - zn_anAmfCpTVent = 0.; // full vent heat addition, Btuh -#endif - - bool bUZ = zn_IsUZ(); - float znfVent = ventAvail == C_VENTAVAILVC_ZONAL ? zn_fVentPrf : Top.tp_fVent; - if (bUZ || znfVent > 0.) - { // float temp - // * unconditioned: HVAC not possible - // * conditioned: HVAC is off if vent is on - zn_tzsp = 0.f; - if (znfVent > 0.f) - { - zn_anAmfCpVent = znfVent*(zn_AnAmfCp( 1) - zn_AnAmfCp( 0)); - zn_anAmfCpTVent = znfVent*(zn_AnAmfCpT( 1) - zn_AnAmfCpT( 0)); - - zn_pz0 = znfVent*zn_pz0W[ 1] + (1.f-znfVent)*zn_pz0W[ 0]; // zone pressure - - tz = zn_TAirCR( zn_anAmfCpTVent, zn_anAmfCpVent); - - if (!bUZ && znfVent < 1.f && znfVent == zn_fVentPrf) - { // equal fVents: current zone is "control" - // tz s/b exactly TD, fix if very close - // else error - if (fabs( zn_tzspD - tz) < .001) - tz = zn_tzspD; -#if defined( _DEBUG) - else - printf( "Zone '%s': control zone vent mismatch\n", Name()); -#endif - } - } - else - { // tz already known - zn_pz0 = zn_pz0W[ 0]; // no venting -#if defined( _DEBUG) - float tzx = zn_TAirCR( 0., 0.); - if (fabs( tz - tzx) > .0001) - printf( "Zone '%s': floating temp mismatch\n", Name()); + // zn_fVent = fVent -- don't change until end (zone value used herein) + zn_qsHvac = 0.; + zn_qlHvac = 0.; +#if defined(ZNHVACFCONV) + zn_fConv = 1.f; // HVAC convective fraction +#endif + zn_rsAmfSysReq[0] = zn_rsAmfSysReq[1] = 0.; // RSYS air requests + + zn_anAmfCpVent = 0.; // full vent heat rate, Btuh/F + zn_anAmfCpTVent = 0.; // full vent heat addition, Btuh +#endif + + bool bUZ = zn_IsUZ(); + float znfVent = ventAvail == C_VENTAVAILVC_ZONAL ? zn_fVentPrf : Top.tp_fVent; + if (bUZ || znfVent > 0.) { // float temp + // * unconditioned: HVAC not possible + // * conditioned: HVAC is off if vent is on + zn_tzsp = 0.f; + if (znfVent > 0.f) { + zn_anAmfCpVent = znfVent * (zn_AnAmfCp(1) - zn_AnAmfCp(0)); + zn_anAmfCpTVent = znfVent * (zn_AnAmfCpT(1) - zn_AnAmfCpT(0)); + + zn_pz0 = + znfVent * zn_pz0W[1] + (1.f - znfVent) * zn_pz0W[0]; // zone pressure + + tz = zn_TAirCR(zn_anAmfCpTVent, zn_anAmfCpVent); + + if (!bUZ && znfVent < 1.f && + znfVent == zn_fVentPrf) { // equal fVents: current zone is "control" + // tz s/b exactly TD, fix if very close + // else error + if (fabs(zn_tzspD - tz) < .001) + tz = zn_tzspD; +#if defined(_DEBUG) + else + printf("Zone '%s': control zone vent mismatch\n", Name()); #endif - } - -#if defined( _DEBUG) - if (!bUZ && zn_anVentEffect > 0) - { // check conditioned zone outcome - // don't complain about zones with no vents - if (tz < zn_tzspD - .001f) - { if (znfVent > .001 && zn_anAmfCpTVent > .001) - orWarn("vent tz (%.1f F) below TD (%.1f F)", tz, zn_tzspD); - if (tz < zn_tzspH) - orWarn("floating tz (%.1f F) below TH (%.1f F)", tz, zn_tzspH); - } - else if (tz > zn_tzspC + .1f) - orWarn("floating tz (%.1f F) above TC (%.1f F)", tz, zn_tzspC); - } + } + } else { // tz already known + zn_pz0 = zn_pz0W[0]; // no venting +#if defined(_DEBUG) + float tzx = zn_TAirCR(0., 0.); + if (fabs(tz - tzx) > .0001) + printf("Zone '%s': floating temp mismatch\n", Name()); #endif - zn_fVent = znfVent; // final vent fraction - } + } - else - { // HVAC may be required - - zn_fVent = 0.f; // no vent - zn_pz0 = zn_pz0W[ 0]; - - RSYS* rs = zn_GetRSYS(); - if (rs) - { // this zone is served by an RSYS - if (zn_hcMode == RSYS::rsmOFF) // if not already handled (e.g. rsmOAV) - { zn_tzsp = 0.f; - if (tz > zn_tzspC) - { zn_tzsp = zn_tzspC; - zn_hcMode = RSYS::rsmCOOL; -#if defined( ZNHVACFCONV) - zn_fConv = zn_fConvC; -#endif - } - else if (tz < zn_tzspH) - { zn_tzsp = zn_tzspH; - zn_hcMode = RSYS::rsmHEAT; -#if defined( ZNHVACFCONV) - zn_fConv = zn_fConvH; -#endif - } - if (zn_hcMode != RSYS::rsmOFF) - { -#if 0 && defined( _DEBUG) +#if defined(_DEBUG) + if (!bUZ && zn_anVentEffect > 0) { // check conditioned zone outcome + // don't complain about zones with no vents + if (tz < zn_tzspD - .001f) { + if (znfVent > .001 && zn_anAmfCpTVent > .001) + orWarn("vent tz (%.1f F) below TD (%.1f F)", tz, zn_tzspD); + if (tz < zn_tzspH) + orWarn("floating tz (%.1f F) below TH (%.1f F)", tz, zn_tzspH); + } else if (tz > zn_tzspC + .1f) + orWarn("floating tz (%.1f F) above TC (%.1f F)", tz, zn_tzspC); + } +#endif + zn_fVent = znfVent; // final vent fraction + } + + else { // HVAC may be required + + zn_fVent = 0.f; // no vent + zn_pz0 = zn_pz0W[0]; + + RSYS *rs = zn_GetRSYS(); + if (rs) { // this zone is served by an RSYS + if (zn_hcMode == RSYS::rsmOFF) // if not already handled (e.g. rsmOAV) + { + zn_tzsp = 0.f; + if (tz > zn_tzspC) { + zn_tzsp = zn_tzspC; + zn_hcMode = RSYS::rsmCOOL; +#if defined(ZNHVACFCONV) + zn_fConv = zn_fConvC; +#endif + } else if (tz < zn_tzspH) { + zn_tzsp = zn_tzspH; + zn_hcMode = RSYS::rsmHEAT; +#if defined(ZNHVACFCONV) + zn_fConv = zn_fConvH; +#endif + } + if (zn_hcMode != RSYS::rsmOFF) { +#if 0 && defined(_DEBUG) x if (Top.tp_pass1B) x printf( "1B\n"); #endif - int rsAvail = rs->rs_SupplyAirState( zn_hcMode); - - if (rsAvail >= 2) - { // mode is possible - rc |= zn_AirRequest( rs, 1); // determine air requirement given rs_asSup - // 1: msg if bad supply temp - // zone temp calc'd in zn_CondixCR2() - } - else if (rsAvail == 1) - { // autosizing but not requested mode - // hold temp at set point, ignore HVAC air (TODO?) - tz = zn_tzsp; -#if defined( ZNHVACFCONV) - zn_QsHvacCR( tz, zn_fConv); + int rsAvail = rs->rs_SupplyAirState(zn_hcMode); + + if (rsAvail >= 2) { // mode is possible + rc |= zn_AirRequest(rs, + 1); // determine air requirement given rs_asSup + // 1: msg if bad supply temp + // zone temp calc'd in zn_CondixCR2() + } else if (rsAvail == 1) { // autosizing but not requested mode + // hold temp at set point, ignore HVAC air (TODO?) + tz = zn_tzsp; +#if defined(ZNHVACFCONV) + zn_QsHvacCR(tz, zn_fConv); #else - zn_QAirCR(tz); -#endif - } - // else system not available - // leave tz = floating and zn_rsAmfSysReq[] = 0 - } - } - } - else - { // non-RSYS zone ("magic" heating and cooling) - zn_IdealHVAC(); - } - } + zn_QAirCR(tz); +#endif + } + // else system not available + // leave tz = floating and zn_rsAmfSysReq[] = 0 + } + } + } else { // non-RSYS zone ("magic" heating and cooling) + zn_IdealHVAC(); + } + } -#if defined( _DEBUG) - if (tz < -100.f || tz > 200.f) - warn( "Zone '%s': unreasonable air temp %0.2f\n", Name(), tz); +#if defined(_DEBUG) + if (tz < -100.f || tz > 200.f) + warn("Zone '%s': unreasonable air temp %0.2f\n", Name(), tz); #endif - return rc; -} // ZNR::zn_CondixCR + return rc; +} // ZNR::zn_CondixCR //----------------------------------------------------------------------------- -RC ZNR::zn_AirRequest( // determine air requirement given rs_asSup - RSYS* rs, // zone's RSYS - [[maybe_unused]] int options /*=0*/) // option bits - // 1: report "flipped" supply temps +RC ZNR::zn_AirRequest( // determine air requirement given rs_asSup + RSYS *rs, // zone's RSYS + [[maybe_unused]] int options /*=0*/) // option bits + // 1: report "flipped" supply temps // Prereq: rs->rs_SupplyAirState() at current speed // returns RCOK iff success { - RC rc = RCOK; - double tSup0 = rs->rs_asSup.as_tdb; -#if 1 && defined( _DEBUG) - if ((options & 1) && !Top.isWarmup && rs->rs_speedF > 0.99f - && ( (zn_hcMode == RSYS::rsmCOOL && tSup0 >= zn_tzsp) - || (zn_hcMode == RSYS::rsmHEAT && tSup0 <= zn_tzsp && rs->rs_effHt > 0.f))) - { - orWarn("Flipped tSup RSYS='%s', tPln=%0.3f, tSup=%0.3f, tSP=%0.1f, tZn=%0.3f\n", - rs->Name(), rs->rs_asOut.as_tdb, tSup0, zn_tzsp, tz); - } -#endif - - double amfSup0 = zn_AmfHvacCR(zn_tzsp, tSup0); // dry air mass flow rate required to hold + RC rc = RCOK; + double tSup0 = rs->rs_asSup.as_tdb; +#if 1 && defined(_DEBUG) + if ((options & 1) && !Top.isWarmup && rs->rs_speedF > 0.99f && + ((zn_hcMode == RSYS::rsmCOOL && tSup0 >= zn_tzsp) || + (zn_hcMode == RSYS::rsmHEAT && tSup0 <= zn_tzsp && + rs->rs_effHt > 0.f))) { + orWarn("Flipped tSup RSYS='%s', tPln=%0.3f, tSup=%0.3f, tSP=%0.1f, " + "tZn=%0.3f\n", + rs->Name(), rs->rs_asOut.as_tdb, tSup0, zn_tzsp, tz); + } +#endif + + double amfSup0 = + zn_AmfHvacCR(zn_tzsp, tSup0); // dry air mass flow rate required to hold #if 0 && defined(_DEBUG) double qLoad = zn_QAirCR(zn_tzsp); // load at system, Btuh @@ -1282,534 +1326,530 @@ RC ZNR::zn_AirRequest( // determine air requirement given rs_asSup printf("\nzn_AirRequest mismatch"); #endif - zn_rsAmfSysReq[0] = rs->rs_ZoneAirRequest(amfSup0, 0); // notify system of requirement - CHECKFP(zn_rsAmfSysReq[0]); // check for NaN etc (debug only) + zn_rsAmfSysReq[0] = + rs->rs_ZoneAirRequest(amfSup0, 0); // notify system of requirement + CHECKFP(zn_rsAmfSysReq[0]); // check for NaN etc (debug only) - if (zn_hcMode == RSYS::rsmHEAT && rs->rs_CanHaveAuxHeat() && rs->rs_speedF==1.f) - { // HP heating full speed: repeat calc with full aux - double tSup1 = rs->rs_asSupAux.as_tdb; - double amfSup1 = zn_AmfHvacCR(zn_tzsp, tSup1); - zn_rsAmfSysReq[1] = rs->rs_ZoneAirRequest(amfSup1, 1); -#if defined( _DEBUG) - double qHt0 = amfSup0 * (tSup0 - zn_tzsp); - double qHt1 = amfSup1 * (tSup1 - zn_tzsp); - // zn_AmfHvacCR can return DBL_MAX - if (qHt0 > 0. && qHt0 < 1.e10 && qHt1 > 0. && qHt1 < 1.e10 - && frDiff(qHt0, qHt1) > .001) - printf("\nqHt mismatch"); -#endif - } - return rc; -} // ZNR::zn_AirRequest + if (zn_hcMode == RSYS::rsmHEAT && rs->rs_CanHaveAuxHeat() && + rs->rs_speedF == + 1.f) { // HP heating full speed: repeat calc with full aux + double tSup1 = rs->rs_asSupAux.as_tdb; + double amfSup1 = zn_AmfHvacCR(zn_tzsp, tSup1); + zn_rsAmfSysReq[1] = rs->rs_ZoneAirRequest(amfSup1, 1); +#if defined(_DEBUG) + double qHt0 = amfSup0 * (tSup0 - zn_tzsp); + double qHt1 = amfSup1 * (tSup1 - zn_tzsp); + // zn_AmfHvacCR can return DBL_MAX + if (qHt0 > 0. && qHt0 < 1.e10 && qHt1 > 0. && qHt1 < 1.e10 && + frDiff(qHt0, qHt1) > .001) + printf("\nqHt mismatch"); +#endif + } + return rc; +} // ZNR::zn_AirRequest //----------------------------------------------------------------------------- -void ZNR::zn_SetRSYSAmf( // set RSYS air flow - float fSize, // fraction of zone request that system can meet - int iAux) // [ 0]=main, [ 1]=main+aux +void ZNR::zn_SetRSYSAmf( // set RSYS air flow + float fSize, // fraction of zone request that system can meet + int iAux) // [ 0]=main, [ 1]=main+aux { - RSYS* rs = zn_GetRSYS(); + RSYS *rs = zn_GetRSYS(); - // supply / return adjusted for duct leakage - int iHC = rs->rs_DsHC(); // select duct config (0=htg, 1=clg) - zn_rsAmfSup = fSize * zn_rsAmfSysReq[ iAux] * rs->rs_ducts[ iHC].ductLkXF[ 0]; - zn_rsAmfRet = fSize * zn_rsAmfSysReq[ iAux] * rs->rs_ducts[ iHC].ductLkXF[ 1]; + // supply / return adjusted for duct leakage + int iHC = rs->rs_DsHC(); // select duct config (0=htg, 1=clg) + zn_rsAmfSup = fSize * zn_rsAmfSysReq[iAux] * rs->rs_ducts[iHC].ductLkXF[0]; + zn_rsAmfRet = fSize * zn_rsAmfSysReq[iAux] * rs->rs_ducts[iHC].ductLkXF[1]; - zn_rsFSize = fSize; + zn_rsFSize = fSize; -} // ZNR::zn_SetRSYSAmf +} // ZNR::zn_SetRSYSAmf //---------------------------------------------------------------------------- -void ZNR::zn_SetRSYSAmfFromTSup() // set RSYS-to-zone air flow given tSup +void ZNR::zn_SetRSYSAmfFromTSup() // set RSYS-to-zone air flow given tSup // supply temp = rs_asSup.as_tdb // call ONLY when RSYS capacity sufficient { - if (zn_hcMode != RSYS::rsmOFF) - { RSYS* rs = zn_GetRSYS(); - int iHC = rs->rs_DsHC(); - zn_rsAmfSup = zn_AmfHvacCR( zn_tzsp, rs->rs_asSup.as_tdb); - zn_rsAmfRet = zn_rsAmfSup * rs->rs_ducts[ iHC].ductLkXF[ 1] / rs->rs_ducts[ iHC].ductLkXF[ 0]; - zn_rsFSize = 1.f; - } - // else zn_rsAmfxxx = 0 -} // ZNR::zn_SetRSYSAmfFromTSup + if (zn_hcMode != RSYS::rsmOFF) { + RSYS *rs = zn_GetRSYS(); + int iHC = rs->rs_DsHC(); + zn_rsAmfSup = zn_AmfHvacCR(zn_tzsp, rs->rs_asSup.as_tdb); + zn_rsAmfRet = zn_rsAmfSup * rs->rs_ducts[iHC].ductLkXF[1] / + rs->rs_ducts[iHC].ductLkXF[0]; + zn_rsFSize = 1.f; + } + // else zn_rsAmfxxx = 0 +} // ZNR::zn_SetRSYSAmfFromTSup //----------------------------------------------------------------------------- -void ZNR::zn_IdealHVAC() // idealized space conditioning +void ZNR::zn_IdealHVAC() // idealized space conditioning // applies "magic" heating or cooling { - if (tz > zn_tzspC && i.znQMxC < 0.f) - { - zn_hcMode = RSYS::rsmCOOL; - tz = zn_tzsp = zn_tzspC; - #if defined( ZNHVACFCONV) - zn_fConv = zn_fConvC; - zn_qsHvac = zn_QsHvacCR(tz, zn_fConv); - #else - zn_qsHvac = zn_QAirCR(tz); - #endif - if (fabs(zn_qsHvac) > fabs(i.znQMxC)) - { zn_qsHvac = i.znQMxC; - #if defined( ZNHVACFCONV) - tz = zn_TAirCR(zn_qsHvac * zn_fConv, 0., zn_qsHvac * (1.f - zn_fConv)); - #else - tz = zn_TAirCR(zn_qsHvac, 0.); - #endif - } - // else capacity sufficient - } - else if (tz < zn_tzspH && i.znQMxH > 0.f) - { // tz < TH: air needs heating - zn_hcMode = RSYS::rsmHEAT; - tz = zn_tzsp = zn_tzspH; -#if defined( ZNHVACFCONV) - zn_fConv = zn_fConvH; - zn_qsHvac = zn_QsHvacCR(tz, zn_fConv); + if (tz > zn_tzspC && i.znQMxC < 0.f) { + zn_hcMode = RSYS::rsmCOOL; + tz = zn_tzsp = zn_tzspC; +#if defined(ZNHVACFCONV) + zn_fConv = zn_fConvC; + zn_qsHvac = zn_QsHvacCR(tz, zn_fConv); #else - zn_qsHvac = zn_QAirCR(tz); + zn_qsHvac = zn_QAirCR(tz); #endif - if (zn_qsHvac > i.znQMxH) - { // heat at heating capacity, find resulting tz. - zn_qsHvac = i.znQMxH; -#if defined( ZNHVACFCONV) - tz = zn_TAirCR(zn_qsHvac * zn_fConv, 0., zn_qsHvac * (1.f - zn_fConv)); + if (fabs(zn_qsHvac) > fabs(i.znQMxC)) { + zn_qsHvac = i.znQMxC; +#if defined(ZNHVACFCONV) + tz = zn_TAirCR(zn_qsHvac * zn_fConv, 0., zn_qsHvac * (1.f - zn_fConv)); #else - tz = zn_TAirCR(zn_qsHvac, 0.); + tz = zn_TAirCR(zn_qsHvac, 0.); +#endif + } + // else capacity sufficient + } else if (tz < zn_tzspH && i.znQMxH > 0.f) { // tz < TH: air needs heating + zn_hcMode = RSYS::rsmHEAT; + tz = zn_tzsp = zn_tzspH; +#if defined(ZNHVACFCONV) + zn_fConv = zn_fConvH; + zn_qsHvac = zn_QsHvacCR(tz, zn_fConv); +#else + zn_qsHvac = zn_QAirCR(tz); #endif - } - // else capacity sufficient - } -} // ZNR::zn_IdealHVAC + if (zn_qsHvac > i.znQMxH) { // heat at heating capacity, find resulting tz. + zn_qsHvac = i.znQMxH; +#if defined(ZNHVACFCONV) + tz = zn_TAirCR(zn_qsHvac * zn_fConv, 0., zn_qsHvac * (1.f - zn_fConv)); +#else + tz = zn_TAirCR(zn_qsHvac, 0.); +#endif + } + // else capacity sufficient + } +} // ZNR::zn_IdealHVAC //----------------------------------------------------------------------------- -void ZNR::zn_MapTerminalResults() // transfer terminal model outcomes to working mbrs +void ZNR::zn_MapTerminalResults() // transfer terminal model outcomes to working + // mbrs { - double cSup = 0.; - double cRet = 0.; - double tSup = 0.; - double wSup = 0.; + double cSup = 0.; + double cRet = 0.; + double tSup = 0.; + double wSup = 0.; - for (TU* tu = nullptr; nxTu(tu); ) - { - const AH* ah = AhB.GetAtSafe(tu->ai); - if (!ah) - { // no terminal air flow w/o AH - continue; - } + for (TU *tu = nullptr; nxTu(tu);) { + const AH *ah = AhB.GetAtSafe(tu->ai); + if (!ah) { // no terminal air flow w/o AH + continue; + } - if (ah->ahcc.q < 0.) - zn_hcMode = RSYS::rsmCOOL; - else if (ah->ahhc.q > 0.) - zn_hcMode = RSYS::rsmHEAT; + if (ah->ahcc.q < 0.) + zn_hcMode = RSYS::rsmCOOL; + else if (ah->ahhc.q > 0.) + zn_hcMode = RSYS::rsmHEAT; -#if defined( _DEBUG) - double aCz = tu->aCz(); - if (frDiff(tu->cz, aCz) > 0.001) - printf("\nFlow mismatch -- tu->cz=%.2f tu->aCz()=%.2f", - tu->cz, aCz); +#if defined(_DEBUG) + double aCz = tu->aCz(); + if (frDiff(tu->cz, aCz) > 0.001) + printf("\nFlow mismatch -- tu->cz=%.2f tu->aCz()=%.2f", tu->cz, aCz); #endif - if (tu->cz > 0.) - { // accumulate supply flow and state - cSup+= tu->cz; // supply flow (at zone), Btuh/F - cRet += tu->cz * (1.-ah->oaZoneLeakF * ah->po); // return flow (at zone), Btuh/F - tSup += tu->cz * ah->ah_tSup; - wSup += tu->cz * ah->ah_wSup; - } - } + if (tu->cz > 0.) { // accumulate supply flow and state + cSup += tu->cz; // supply flow (at zone), Btuh/F + cRet += tu->cz * + (1. - ah->oaZoneLeakF * ah->po); // return flow (at zone), Btuh/F + tSup += tu->cz * ah->ah_tSup; + wSup += tu->cz * ah->ah_wSup; + } + } -#if defined( _DEBUG) - if (frDiff( cSup, double( cSum)) > 0.001) - printf("\nTotal flow mismatch"); +#if defined(_DEBUG) + if (frDiff(cSup, double(cSum)) > 0.001) + printf("\nTotal flow mismatch"); #endif - if (cSup > 0.) - { - tSup /= cSup; - wSup /= cSup; + if (cSup > 0.) { + tSup /= cSup; + wSup /= cSup; - zn_rsAmfSup = cSup / Top.tp_airSH; + zn_rsAmfSup = cSup / Top.tp_airSH; - if (!Top.tp_airNetActive) - cRet = cSup; // prevent unbalanced if no airnet - // (no relief holes available) + if (!Top.tp_airNetActive) + cRet = cSup; // prevent unbalanced if no airnet + // (no relief holes available) - zn_rsAmfRet = cRet / Top.tp_airSH; + zn_rsAmfRet = cRet / Top.tp_airSH; - zn_sysAirI.af_AccumDry(zn_rsAmfSup, tSup, wSup); - zn_sysAirO.af_AccumDry(-zn_rsAmfRet, tz, wzls); // w finalized in zn_AirXMoistureBal - } + zn_sysAirI.af_AccumDry(zn_rsAmfSup, tSup, wSup); + zn_sysAirO.af_AccumDry(-zn_rsAmfRet, tz, + wzls); // w finalized in zn_AirXMoistureBal + } -} // ZNR::zn_MapTerminalResults +} // ZNR::zn_MapTerminalResults //----------------------------------------------------------------------------- -RC ZNR::zn_CondixCR2() // zone conditions, part 2 -{ - RSYS* rs = zn_GetRSYS(); - if (rs) - { if (zn_rsAmfSup > 0.) - { // this zone is served by RSYS and receives air - // RSYS has determined amf available in rs_AllocateZoneAir() - // zn_rsAmfSup, zn_rsAmfRet, and zn_rsFSize are set -#if defined( _DEBUG) - if (!Top.isWarmup && rs->rs_mode != zn_hcMode) - // zone mode should match RSYS mode - // don't check during warmup (including autosize) - printf( "Zone '%s': Mode mismatch\n", Name()); -#endif - double mCp = zn_rsAmfSup*Top.tp_airSH; - double tSup = rs->rs_asSup.as_tdb; - tz = zn_TAirCR( mCp*tSup, mCp); - zn_qsHvac = mCp*(tSup - tz); - // zn_qsHvac = zn_QAirCR(tzls); experiment, same results 11-21 - zn_sysAirI.af_AccumDry( zn_rsAmfSup, rs->rs_asSup); - if (zn_hcMode == RSYS::rsmOAV) - zn_OAVRlfO.af_AccumDry( -zn_rsAmfSup, tz, wzls); // w not used - else - zn_sysAirO.af_AccumDry( -zn_rsAmfRet, tz, wzls); // w finalized in zn_AirXMoistureBal -#if defined( _DEBUG) - if (tz < -100.f || tz > 200.f) - warn( "Zone '%s': unreasonable air temp %0.2f\n", Name(), tz); -#endif - } - -#if defined( _DEBUG) - // test against setpoint (with a little tolerance) - if (!Top.isWarmup) // skip if warmup (including autosize) - { if (zn_hcMode == RSYS::rsmHEAT) - { if (tz > zn_tzspH + .001f) - printf( "\nOverheat"); - } - else if (zn_hcMode == RSYS::rsmCOOL) - { if (tz < zn_tzspC - .001f) - printf( "\nOvercool"); - } - } +RC ZNR::zn_CondixCR2() // zone conditions, part 2 +{ + RSYS *rs = zn_GetRSYS(); + if (rs) { + if (zn_rsAmfSup > + 0.) { // this zone is served by RSYS and receives air + // RSYS has determined amf available in rs_AllocateZoneAir() + // zn_rsAmfSup, zn_rsAmfRet, and zn_rsFSize are set +#if defined(_DEBUG) + if (!Top.isWarmup && rs->rs_mode != zn_hcMode) + // zone mode should match RSYS mode + // don't check during warmup (including autosize) + printf("Zone '%s': Mode mismatch\n", Name()); +#endif + double mCp = zn_rsAmfSup * Top.tp_airSH; + double tSup = rs->rs_asSup.as_tdb; + tz = zn_TAirCR(mCp * tSup, mCp); + zn_qsHvac = mCp * (tSup - tz); + // zn_qsHvac = zn_QAirCR(tzls); experiment, same results 11-21 + zn_sysAirI.af_AccumDry(zn_rsAmfSup, rs->rs_asSup); + if (zn_hcMode == RSYS::rsmOAV) + zn_OAVRlfO.af_AccumDry(-zn_rsAmfSup, tz, wzls); // w not used + else + zn_sysAirO.af_AccumDry(-zn_rsAmfRet, tz, + wzls); // w finalized in zn_AirXMoistureBal +#if defined(_DEBUG) + if (tz < -100.f || tz > 200.f) + warn("Zone '%s': unreasonable air temp %0.2f\n", Name(), tz); #endif - } -#if defined( CRTERMAH) - else if (zn_HasTerminal()) - { - zn_MapTerminalResults(); - + } - } -#endif - // else all known -#if defined( ZNHVACFCONV) - zn_tr = zn_TRadCR( tz, zn_qsHvac*(1.-zn_fConv)); +#if defined(_DEBUG) + // test against setpoint (with a little tolerance) + if (!Top.isWarmup) // skip if warmup (including autosize) + { + if (zn_hcMode == RSYS::rsmHEAT) { + if (tz > zn_tzspH + .001f) + printf("\nOverheat"); + } else if (zn_hcMode == RSYS::rsmCOOL) { + if (tz < zn_tzspC - .001f) + printf("\nOvercool"); + } + } +#endif + } +#if defined(CRTERMAH) + else if (zn_HasTerminal()) { + zn_MapTerminalResults(); + } +#endif + // else all known +#if defined(ZNHVACFCONV) + zn_tr = zn_TRadCR(tz, zn_qsHvac * (1. - zn_fConv)); #else - zn_tr = zn_TRadCR( tz); + zn_tr = zn_TRadCR(tz); #endif - zn_qIzSh = zn_anAmfCpTVent + zn_AnAmfCpT( 0) + zn_sysDepAirIls.af_AmfCpT() - - (zn_anAmfCpVent + zn_AnAmfCp( 0) + zn_sysDepAirIls.af_AmfCp())*tz - + zn_qIzXAnSh; + zn_qIzSh = + zn_anAmfCpTVent + zn_AnAmfCpT(0) + zn_sysDepAirIls.af_AmfCpT() - + (zn_anAmfCpVent + zn_AnAmfCp(0) + zn_sysDepAirIls.af_AmfCp()) * tz + + zn_qIzXAnSh; - return RCOK; + return RCOK; -} // ZNR::zn_CondixCR2 +} // ZNR::zn_CondixCR2 //---------------------------------------------------------------------------- #if defined(_DEBUG) -RC ZNR::zn_AirFlowVsTsup() -{ - RC rc = RCOK; - if (!Top.isEndHour) - return rc; // skip all except last substep - - RSYS* rs = zn_GetRSYS(); - if (!rs) - return RCBAD; +RC ZNR::zn_AirFlowVsTsup() { + RC rc = RCOK; + if (!Top.isEndHour) + return rc; // skip all except last substep - static FILE* f = NULL; - if (!f) - { - f = fopen(strtprintf("AirCurves.csv"), "wt"); - if (!f) - return RCBAD; // can't open file - fprintf(f, "%s\n", rs->rs_desc.CStrIfNotBlank( "Curves")); - - fprintf(f, "Mon,Day,Hr,Sh,fAmf,amf,tReg,amfNeeded\n"); - - } + RSYS *rs = zn_GetRSYS(); + if (!rs) + return RCBAD; - // RSYS rsSave( rs); // save for restore at exit - // (we alter mbrs here) + static FILE *f = NULL; + if (!f) { + f = fopen(strtprintf("AirCurves.csv"), "wt"); + if (!f) + return RCBAD; // can't open file + fprintf(f, "%s\n", rs->rs_desc.CStrIfNotBlank("Curves")); + fprintf(f, "Mon,Day,Hr,Sh,fAmf,amf,tReg,amfNeeded\n"); + } - AIRSTATE asSup; // air state at register - for (int iAf = 0; iAf < 10; iAf++) - { - float fAmf = 1.f - float(iAf) / 10.f; + // RSYS rsSave( rs); // save for restore at exit + // (we alter mbrs here) - float amf = rs->rs_TSupVarFlow(fAmf, asSup); + AIRSTATE asSup; // air state at register + for (int iAf = 0; iAf < 10; iAf++) { + float fAmf = 1.f - float(iAf) / 10.f; - double amfNeed = zn_AmfHvacCR(70. /*tz*/, asSup.as_tdb); + float amf = rs->rs_TSupVarFlow(fAmf, asSup); - fprintf(f, "%d,%d,%d,%d,%.1f,%.0f,%0.1f,%.0f\n", - Top.tp_date.month, Top.tp_date.mday, Top.iHrST + 1,Top.iSubhr, - fAmf, amf, asSup.as_tdb, amfNeed); + double amfNeed = zn_AmfHvacCR(70. /*tz*/, asSup.as_tdb); - } + fprintf(f, "%d,%d,%d,%d,%.1f,%.0f,%0.1f,%.0f\n", Top.tp_date.month, + Top.tp_date.mday, Top.iHrST + 1, Top.iSubhr, fAmf, amf, + asSup.as_tdb, amfNeed); + } - return rc; -} // ZNR::zn_AirFlowVsTsup + return rc; +} // ZNR::zn_AirFlowVsTsup //----------------------------------------------------------------------------- -float RSYS::rs_TSupVarFlow( - float fAmf, - AIRSTATE& asSup) -{ +float RSYS::rs_TSupVarFlow(float fAmf, AIRSTATE &asSup) { - asSup = rs_asOut; + asSup = rs_asOut; - float amf = rs_amf * fAmf; + float amf = rs_amf * fAmf; - return rs_SupplyDSEAndDucts(asSup, amf); + return rs_SupplyDSEAndDucts(asSup, amf); -} // RSYS::rs_TSupVarFlow +} // RSYS::rs_TSupVarFlow #endif //---------------------------------------------------------------------------- -double ZNR::zn_TAirCR( // zone air temp w/ add'l radiant heat - double mCpT, // add'l air heat to zone, Btuh - // either heat added to air ("qAir") - // or mass flow*Tsup (with associated mCp) - double mCp, // add'l air heat rate, Btuh/F - double qRad) const // add'l radiant heat to zone, Btuh +double ZNR::zn_TAirCR( // zone air temp w/ add'l radiant heat + double mCpT, // add'l air heat to zone, Btuh + // either heat added to air ("qAir") + // or mass flow*Tsup (with associated mCp) + double mCp, // add'l air heat rate, Btuh/F + double qRad) const // add'l radiant heat to zone, Btuh // returns zone air temp, F { - double tza = (mCpT*zn_dRpCx + zn_balC1 + zn_cxSh*qRad) - / (mCp*zn_dRpCx + zn_balC2); - return tza; -} // ZNR::zn_TAirCR + double tza = (mCpT * zn_dRpCx + zn_balC1 + zn_cxSh * qRad) / + (mCp * zn_dRpCx + zn_balC2); + return tza; +} // ZNR::zn_TAirCR //---------------------------------------------------------------------------- -double ZNR::zn_TAirCR( // zone air temp - double mCpT, // add'l air heat to zone, Btuh - // either heat added to air ("qAir") - // or mass flow*Tsup (with associated mCp) - double mCp) const // add'l air heat rate, Btuh/F +double ZNR::zn_TAirCR( // zone air temp + double mCpT, // add'l air heat to zone, Btuh + // either heat added to air ("qAir") + // or mass flow*Tsup (with associated mCp) + double mCp) const // add'l air heat rate, Btuh/F // returns zone air temp, F { - double tza = (mCpT*zn_dRpCx + zn_balC1) - / (mCp*zn_dRpCx + zn_balC2); - return tza; -} // ZNR::zn_TAirCR + double tza = (mCpT * zn_dRpCx + zn_balC1) / (mCp * zn_dRpCx + zn_balC2); + return tza; +} // ZNR::zn_TAirCR //---------------------------------------------------------------------------- // Note float w/ no add'l gains (cnrecs.def inline) // void zn_TAirFloatCR() { tz = zn_balC1 / zn_balC2; } //---------------------------------------------------------------------------- -double ZNR::zn_TRadCR( // zone radiant temp w/ add'l radiant heat - double tza, // zone air temp, F - double qRad) const // add'l radiant heat to zone +double ZNR::zn_TRadCR( // zone radiant temp w/ add'l radiant heat + double tza, // zone air temp, F + double qRad) const // add'l radiant heat to zone // returns zone radiant temp, F { - double tzr = (zn_nRadSh + qRad + zn_cxSh*tza)/zn_dRpCx; - return tzr; -} // ZNR::zn_TRadCR + double tzr = (zn_nRadSh + qRad + zn_cxSh * tza) / zn_dRpCx; + return tzr; +} // ZNR::zn_TRadCR //---------------------------------------------------------------------------- -double ZNR::zn_TRadCR( // zone radiant temp - double tza) const // zone air temp, F +double ZNR::zn_TRadCR( // zone radiant temp + double tza) const // zone air temp, F // returns zone radiant temp, F { - double tzr = (zn_nRadSh + zn_cxSh * tza) / zn_dRpCx; - return tzr; -} // ZNR::zn_TRadCR + double tzr = (zn_nRadSh + zn_cxSh * tza) / zn_dRpCx; + return tzr; +} // ZNR::zn_TRadCR //------------------------------------------------------------------------------ -double ZNR::zn_QAirCR( // air heating/cooling requirements w/ add'l radiant heat - double tza, // zone air temp, F - double qRad) const // add'l radiant heat +double ZNR::zn_QAirCR( // air heating/cooling requirements w/ add'l radiant heat + double tza, // zone air temp, F + double qRad) const // add'l radiant heat // returns zone air heating/cooling (Btuh) required to maintain tza { - double q = (zn_balC2*tza - zn_balC1 - zn_cxSh*qRad) / zn_dRpCx; - return q; -} // ZNR::zn_QAirCR + double q = (zn_balC2 * tza - zn_balC1 - zn_cxSh * qRad) / zn_dRpCx; + return q; +} // ZNR::zn_QAirCR //------------------------------------------------------------------------------ -double ZNR::zn_QAirCR( // air heating/cooling requirements - double tza) const // zone air temp, F +double ZNR::zn_QAirCR( // air heating/cooling requirements + double tza) const // zone air temp, F // returns zone air heating/cooling (Btuh) required to maintain tza { - double q = (zn_balC2*tza - zn_balC1) / zn_dRpCx; - return q; -} // ZNR::zn_QAirCR + double q = (zn_balC2 * tza - zn_balC1) / zn_dRpCx; + return q; +} // ZNR::zn_QAirCR //------------------------------------------------------------------------------ -double ZNR::zn_QsHvacCR( // sensible hvac heat/cool requirements - // (with possible conv/radiant mix) - double tza, // zone air temp, F - float fConv) const // convective faction of available source +double ZNR::zn_QsHvacCR( // sensible hvac heat/cool requirements + // (with possible conv/radiant mix) + double tza, // zone air temp, F + float fConv) const // convective faction of available source // assume no vent (HVAC active) // does NOT change ZNR state // returns sensible power rqd to hold tza, Btuh { - double qs = (zn_balC2*tza - zn_balC1) / (fConv*zn_dRadSh + zn_cxSh); - return qs; -} // ZNR::zn_QsHvacCR + double qs = (zn_balC2 * tza - zn_balC1) / (fConv * zn_dRadSh + zn_cxSh); + return qs; +} // ZNR::zn_QsHvacCR //------------------------------------------------------------------------------ -double ZNR::zn_AmfHvacCR( // sensible hvac air requirements w/ add'l radiant heat - double tza, // zone air temp, F - double tSup, // available supply air temp, F - double qRad) const // add'l radiant heat to zone +double +ZNR::zn_AmfHvacCR( // sensible hvac air requirements w/ add'l radiant heat + double tza, // zone air temp, F + double tSup, // available supply air temp, F + double qRad) const // add'l radiant heat to zone // assume no vent (HVAC active) // does NOT change ZNR state // returns dry-air mass flow rate required to hold tza, lbm/hr { - double amf = (fabs( tza - tSup) < .00001) - ? DBL_MAX - : (zn_balC1 - zn_balC2*tza + zn_cxSh*qRad) - / (zn_dRpCx * (tza - tSup) * Top.tp_airSH); - return amf; -} // ZNR::zn_AmfHvacCR + double amf = (fabs(tza - tSup) < .00001) + ? DBL_MAX + : (zn_balC1 - zn_balC2 * tza + zn_cxSh * qRad) / + (zn_dRpCx * (tza - tSup) * Top.tp_airSH); + return amf; +} // ZNR::zn_AmfHvacCR //------------------------------------------------------------------------------ -double ZNR::zn_AmfHvacCR( // sensible hvac air requirements - double tza, // zone air temp, F - double tSup) const // available supply air temp, F +double ZNR::zn_AmfHvacCR( // sensible hvac air requirements + double tza, // zone air temp, F + double tSup) const // available supply air temp, F // assume no vent (HVAC active) // does NOT change ZNR state // returns dry-air mass flow rate required to hold tza, lbm/hr { - double amf = (fabs(tza - tSup) < .00001) - ? DBL_MAX - : (zn_balC1 - zn_balC2 * tza) - / (zn_dRpCx * (tza - tSup) * Top.tp_airSH); - return amf; -} // ZNR::zn_AmfHvacCR + double amf = (fabs(tza - tSup) < .00001) + ? DBL_MAX + : (zn_balC1 - zn_balC2 * tza) / + (zn_dRpCx * (tza - tSup) * Top.tp_airSH); + return amf; +} // ZNR::zn_AmfHvacCR //----------------------------------------------------------------------------- -double ZNR::zn_AnAmf() const // current AirNet mass flow rate -// returns dry air mass flow rate into zone (lbm/hr) due to AirNet exchanges, lbm/hr -{ - double amf = (1.f - zn_fVent) * zn_airNetI[ 0].af_amf - + zn_fVent * zn_airNetI[ 1].af_amf; - return amf; -} // ZNR::zn_AnAmf +double ZNR::zn_AnAmf() const // current AirNet mass flow rate +// returns dry air mass flow rate into zone (lbm/hr) due to AirNet exchanges, +// lbm/hr +{ + double amf = + (1.f - zn_fVent) * zn_airNetI[0].af_amf + zn_fVent * zn_airNetI[1].af_amf; + return amf; +} // ZNR::zn_AnAmf //----------------------------------------------------------------------------- -double ZNR::zn_NonAnIVAmf( // non-airnet dry air mass flow rate - double dryAirMass /*=-1.*/) const // current zone dry air mass, lbm - // default = zn_dryAirMass +double ZNR::zn_NonAnIVAmf( // non-airnet dry air mass flow rate + double dryAirMass /*=-1.*/) const // current zone dry air mass, lbm + // default = zn_dryAirMass // Note: non-airnet flows contribute to heat and moisture balance // but *NOT* pressure balance (assumed balanced) // returns dry air mass flow rate (lbm/hr) into zone due to non-AirNet sources { - if (dryAirMass < 0.) - dryAirMass = zn_dryAirMass; - double amf = i.infAC * dryAirMass; - if (i.infELA > 0.f) - { float amfELA = - Top.tp_rhoDryOSh - * 60.f - * i.infELA // user input effective leakage area (ft2), hourly variable - * sqrt( // flow is proportional to square root of - zn_stackc * fabs( tzls - Top.tDbOSh) + // stack coeff (see ZNR.zn_InfilSetup) * delta t - zn_windc * Top.windSpeedSquaredSh ); // wind coeff (ditto) * wind speed ^ 2 - - amf += amfELA; - } - return amf; -} // ZNR::zn_NonAnIVAmf + if (dryAirMass < 0.) + dryAirMass = zn_dryAirMass; + double amf = i.infAC * dryAirMass; + if (i.infELA > 0.f) { + float amfELA = + Top.tp_rhoDryOSh * 60.f * + i.infELA // user input effective leakage area (ft2), hourly variable + * + sqrt( // flow is proportional to square root of + zn_stackc * + fabs(tzls - Top.tDbOSh) + // stack coeff (see + // ZNR.zn_InfilSetup) * delta t + zn_windc * + Top.windSpeedSquaredSh); // wind coeff (ditto) * wind speed ^ 2 + + amf += amfELA; + } + return amf; +} // ZNR::zn_NonAnIVAmf //------------------------------------------------------------------------------ -double ZNR::zn_AirXMoistureBal( // air change rate and zone moisture balance - bool bFinal, // true: set ZNR members to calculated zone state - // false: use temporary vars only - double amfX /*=0.*/, // additional system flow (e.g. air handler), lbm/h - double mwX /*=0.*/, // air handler moisture flow, lbm/hr - double _tz /*=-99.*/, // alternative air temp, F; default = ZNR tz - double* pXLGain /*=nullptr*/) // if non-null, return condensation heat gain, Btu +double ZNR::zn_AirXMoistureBal( // air change rate and zone moisture balance + bool bFinal, // true: set ZNR members to calculated zone state + // false: use temporary vars only + double amfX /*=0.*/, // additional system flow (e.g. air handler), lbm/h + double mwX /*=0.*/, // air handler moisture flow, lbm/hr + double _tz /*=-99.*/, // alternative air temp, F; default = ZNR tz + double * + pXLGain /*=nullptr*/) // if non-null, return condensation heat gain, Btu // see also ZNR::znW() re CNE zone moisture balance // returns humidity ratio { - if (_tz < -98.) - _tz = tz; - - double rho = psyDenMoistAir(_tz, wzls); // moist air density, lbm/ft3 - double dryAirMass = max(i.znVol * psyDenDryAir2( rho, wzls), .1); // dry air mass, lbm - - // air changes due to infil + vent - // includes flows induced by ducts / HVAC - // but not those flows - // TODO: improve other infil models - double nonAnIVAmf = zn_NonAnIVAmf( dryAirMass); - double ivAirX = (zn_AnAmf() + nonAnIVAmf) / dryAirMass; - if (ivAirX < 0.f) - ivAirX = 0.f; - - double amfSys = zn_ductLkI.af_amf + zn_sysAirI.af_amf + amfX; - double airX = ivAirX + amfSys / dryAirMass; - - // internal gain - double mwIG = znLGain / PsyHCondWtr; // latent internal gains, lbm/hr - - // non-airnet infil, lbm/hr - double mwInf = nonAnIVAmf * Top.wOSh; - - // IZXFER (airnet) gains not including duct leakage and HVAC - double mwAN = (1.f - zn_fVent) * zn_airNetI[0].af_Wmf() - + (zn_fVent)*zn_airNetI[1].af_Wmf(); - - double mw = mwIG + mwInf + mwAN + mwX // total water vapor mass flow rate, lbm/hr - + zn_ductLkI.af_Wmf() + zn_sysAirI.af_Wmf(); - - // TODO: HPWH moisture removal? 2-16 - - [[maybe_unused]] int wCase = 0; // debug aid - // 0 = time constant OK, result OK - // 1 = short time constant (steady state sln used), result OK - // 2 = time constant OK, result limited - // 3 = short time constant (steady state sln used), result clamped - - double dryAirMassEff = dryAirMass * i.zn_HIRatio; // effective dry air mass - // may be adjusted below re short time steps - float f = airX * Top.tp_subhrDur / i.zn_HIRatio; - if (f > 1.f) - { dryAirMassEff *= f; - f = 1.f; - wCase += 1; - } - double _wz = mw * Top.tp_subhrDur / dryAirMassEff + wzls * (1. - f); - - double XLGain = 0.; // excess gain to space due to "condensation" or "evaporation", Btuh - // if predicted humidity ratio is physically impossible, assume - // magic (sensible) heat transfer - // + = into zone (= "condensation") - double wSat = psyHumRat3(_tz); // maximum w air at this temp can hold - double wzClamp = bracket(double(PsyWmin), _wz, wSat); - if (_wz != wzClamp) - { XLGain = (_wz - wzClamp) * dryAirMassEff * PsyHCondWtr / Top.tp_subhrDur; - _wz = wzClamp; - wCase += 2; // limits applied - } - if (pXLGain) - *pXLGain = XLGain; - - if (bFinal) - { // set ZNR members - zn_rho = rho; - zn_dryAirMass = dryAirMass; - zn_dryAirMassEff = dryAirMassEff; - zn_ivAirX = ivAirX; - zn_airX = airX; - - // modified airX re convection coefficients - if (!i.zn_hcAirXIsSet) // if not fixed by user input - { i.zn_hcAirX = zn_airX; // note: does NOT include zn_hpwhAirX - if (zn_IsAirHVACActive() && !zn_HasAirHVAC()) - i.zn_hcAirX += 4.8f; // additional "virtual" air flow for non-air zones - } - - wz = _wz; - znXLGain = XLGain; // zone total excess latent gain, Btuh - - // latent gains to space - zn_qlHvac = zn_sysAirI.af_QLat(wzls); - if (fabs(zn_qlHvac) < .1) - zn_qlHvac = 0.; // drop tiny values (re report aesthetics) -#if 0 && defined( _DEBUG) + if (_tz < -98.) + _tz = tz; + + double rho = psyDenMoistAir(_tz, wzls); // moist air density, lbm/ft3 + double dryAirMass = + max(i.znVol * psyDenDryAir2(rho, wzls), .1); // dry air mass, lbm + + // air changes due to infil + vent + // includes flows induced by ducts / HVAC + // but not those flows + // TODO: improve other infil models + double nonAnIVAmf = zn_NonAnIVAmf(dryAirMass); + double ivAirX = (zn_AnAmf() + nonAnIVAmf) / dryAirMass; + if (ivAirX < 0.f) + ivAirX = 0.f; + + double amfSys = zn_ductLkI.af_amf + zn_sysAirI.af_amf + amfX; + double airX = ivAirX + amfSys / dryAirMass; + + // internal gain + double mwIG = znLGain / PsyHCondWtr; // latent internal gains, lbm/hr + + // non-airnet infil, lbm/hr + double mwInf = nonAnIVAmf * Top.wOSh; + + // IZXFER (airnet) gains not including duct leakage and HVAC + double mwAN = (1.f - zn_fVent) * zn_airNetI[0].af_Wmf() + + (zn_fVent)*zn_airNetI[1].af_Wmf(); + + double mw = mwIG + mwInf + mwAN + + mwX // total water vapor mass flow rate, lbm/hr + + zn_ductLkI.af_Wmf() + zn_sysAirI.af_Wmf(); + + // TODO: HPWH moisture removal? 2-16 + + [[maybe_unused]] int wCase = + 0; // debug aid + // 0 = time constant OK, result OK + // 1 = short time constant (steady state sln used), result OK + // 2 = time constant OK, result limited + // 3 = short time constant (steady state sln used), result clamped + + double dryAirMassEff = + dryAirMass * i.zn_HIRatio; // effective dry air mass + // may be adjusted below re short time steps + float f = airX * Top.tp_subhrDur / i.zn_HIRatio; + if (f > 1.f) { + dryAirMassEff *= f; + f = 1.f; + wCase += 1; + } + double _wz = mw * Top.tp_subhrDur / dryAirMassEff + wzls * (1. - f); + + double XLGain = + 0.; // excess gain to space due to "condensation" or "evaporation", Btuh + // if predicted humidity ratio is physically impossible, assume + // magic (sensible) heat transfer + // + = into zone (= "condensation") + double wSat = psyHumRat3(_tz); // maximum w air at this temp can hold + double wzClamp = bracket(double(PsyWmin), _wz, wSat); + if (_wz != wzClamp) { + XLGain = (_wz - wzClamp) * dryAirMassEff * PsyHCondWtr / Top.tp_subhrDur; + _wz = wzClamp; + wCase += 2; // limits applied + } + if (pXLGain) + *pXLGain = XLGain; + + if (bFinal) { // set ZNR members + zn_rho = rho; + zn_dryAirMass = dryAirMass; + zn_dryAirMassEff = dryAirMassEff; + zn_ivAirX = ivAirX; + zn_airX = airX; + + // modified airX re convection coefficients + if (!i.zn_hcAirXIsSet) // if not fixed by user input + { + i.zn_hcAirX = zn_airX; // note: does NOT include zn_hpwhAirX + if (zn_IsAirHVACActive() && !zn_HasAirHVAC()) + i.zn_hcAirX += 4.8f; // additional "virtual" air flow for non-air zones + } + + wz = _wz; + znXLGain = XLGain; // zone total excess latent gain, Btuh + + // latent gains to space + zn_qlHvac = zn_sysAirI.af_QLat(wzls); + if (fabs(zn_qlHvac) < .1) + zn_qlHvac = 0.; // drop tiny values (re report aesthetics) +#if 0 && defined(_DEBUG) if (zn_qlHvac > 0.) printf("\nqlHvac > 0"); #endif - zn_qlIz = (1.f - zn_fVent) * zn_airNetI[0].af_QLat(wzls) - + (zn_fVent)*zn_airNetI[1].af_QLat(wzls) - + zn_ductLkI.af_QLat(wzls); + zn_qlIz = (1.f - zn_fVent) * zn_airNetI[0].af_QLat(wzls) + + (zn_fVent)*zn_airNetI[1].af_QLat(wzls) + zn_ductLkI.af_QLat(wzls); - zn_twb = psyTWetBulb(_tz, _wz); - zn_relHum = psyRelHum3(_wz, wSat); + zn_twb = psyTWetBulb(_tz, _wz); + zn_relHum = psyRelHum3(_wz, wSat); - // zone humidity ratio now known - // finalize return air state - zn_sysAirO.as_w = wz; + // zone humidity ratio now known + // finalize return air state + zn_sysAirO.as_w = wz; #if 0 x float relHum = psyRelHum2(tz, zn_twb); @@ -1817,7 +1857,7 @@ x if (tz > 50 && fabs(zn_relHum - relHum) > .0001) x printf("hit"); #endif -#if 0 && defined( _DEBUG) +#if 0 && defined(_DEBUG) x moisture balance experiment x generally does not balance, 5 - 10 - 2012 x double znLDelta = zn_dryAirMass * (wz - wzls) * PsyHCondWtr; @@ -1826,254 +1866,252 @@ x if (frDiff(znLDelta, znLX) > .01) x printf("Mismatch\n"); #endif -#if defined( DEBUGDUMP) - if (DbDo(dbdZM)) - { - DbPrintf("%s W: mwIG=%0.3f mwInf=%0.3f mwAN=%0.3f mwDuctLk=%0.3f mwSys=%0.3f mwSum=%0.3f\n" - " tdb=%0.2f airX=%0.3f hcAirX=%0.3f dryAirMass=%0.2f XLGain=%0.2f W=%0.6f twb=%0.2f rh=%0.4f\n", - Name(), mwIG, mwInf, mwAN, zn_ductLkI.af_Wmf(), zn_sysAirI.af_Wmf(), mw, - _tz, zn_airX, i.zn_hcAirX, zn_dryAirMass, znXLGain, wz, zn_twb, zn_relHum); - DbPrintf(" rho=%0.4f rho0ls=%0.4f wzls=%0.6f dryAirMassEff=%0.2f qlHvac=%0.2f qlIz=%0.2f\n", - zn_rho, zn_rho0ls, wzls, zn_dryAirMassEff, zn_qlHvac, zn_qlIz); +#if defined(DEBUGDUMP) + if (DbDo(dbdZM)) { + DbPrintf("%s W: mwIG=%0.3f mwInf=%0.3f mwAN=%0.3f mwDuctLk=%0.3f " + "mwSys=%0.3f mwSum=%0.3f\n" + " tdb=%0.2f airX=%0.3f hcAirX=%0.3f dryAirMass=%0.2f " + "XLGain=%0.2f W=%0.6f twb=%0.2f rh=%0.4f\n", + Name(), mwIG, mwInf, mwAN, zn_ductLkI.af_Wmf(), + zn_sysAirI.af_Wmf(), mw, _tz, zn_airX, i.zn_hcAirX, + zn_dryAirMass, znXLGain, wz, zn_twb, zn_relHum); + DbPrintf(" rho=%0.4f rho0ls=%0.4f wzls=%0.6f dryAirMassEff=%0.2f " + "qlHvac=%0.2f qlIz=%0.2f\n", + zn_rho, zn_rho0ls, wzls, zn_dryAirMassEff, zn_qlHvac, zn_qlIz); #endif + } + } // bFinal - } - } // bFinal - - return _wz; + return _wz; -} // ZNR::zn_AirXMoistureBal +} // ZNR::zn_AirXMoistureBal //---------------------------------------------------------------------------------- -#if defined( OLDHUM) -RC ZNR::zn_AirX( // total air exchanges etc - double amfX /*=0.*/, // additional air flow, lbm - double _tz /*=-99.*/) // zone air dry bulb, F - // default = current tz -{ - RC rc = RCOK; - - if (_tz < -98.) - _tz = tz; - - zn_rho = psyDenMoistAir(_tz, wzls); // moist air density - zn_dryAirMass = max(i.znVol * psyDenDryAir2(zn_rho, wzls), .1); // dry air mass, lbm - zn_dryAirMassEff = zn_dryAirMass; // effective dry air mass, lbm - // zn_AirXMoistureBal() may adjust re short time steps - - // TODO: improve other infil models? - zn_ivAirX = (zn_AnAmf() + zn_NonAnIVAmf()) / zn_dryAirMass; - // air changes due to infil + vent - // includes flows induced by ducts / HVAC - // but not those flows - if (zn_ivAirX < 0.f) - zn_ivAirX = 0.f; - - double amfSys = zn_ductLkI.af_amf + zn_sysAirI.af_amf + amfX; - zn_airX = zn_ivAirX + amfSys / zn_dryAirMass; - - // modified airX re convection coefficients - if (!i.zn_hcAirXIsSet) // if not fixed by user input - { - i.zn_hcAirX = zn_airX; // note: does NOT include zn_hpwhAirX - if (zn_IsAirHVACActive() && !zn_HasAirHVAC()) - i.zn_hcAirX += 4.8f; // additional "virtual" air flow for non-air zones - } - - return rc; -} // ZNR::zn_AirX +#if defined(OLDHUM) +RC ZNR::zn_AirX( // total air exchanges etc + double amfX /*=0.*/, // additional air flow, lbm + double _tz /*=-99.*/) // zone air dry bulb, F + // default = current tz +{ + RC rc = RCOK; + + if (_tz < -98.) + _tz = tz; + + zn_rho = psyDenMoistAir(_tz, wzls); // moist air density + zn_dryAirMass = + max(i.znVol * psyDenDryAir2(zn_rho, wzls), .1); // dry air mass, lbm + zn_dryAirMassEff = + zn_dryAirMass; // effective dry air mass, lbm + // zn_AirXMoistureBal() may adjust re short time steps + + // TODO: improve other infil models? + zn_ivAirX = (zn_AnAmf() + zn_NonAnIVAmf()) / zn_dryAirMass; + // air changes due to infil + vent + // includes flows induced by ducts / HVAC + // but not those flows + if (zn_ivAirX < 0.f) + zn_ivAirX = 0.f; + + double amfSys = zn_ductLkI.af_amf + zn_sysAirI.af_amf + amfX; + zn_airX = zn_ivAirX + amfSys / zn_dryAirMass; + + // modified airX re convection coefficients + if (!i.zn_hcAirXIsSet) // if not fixed by user input + { + i.zn_hcAirX = zn_airX; // note: does NOT include zn_hpwhAirX + if (zn_IsAirHVACActive() && !zn_HasAirHVAC()) + i.zn_hcAirX += 4.8f; // additional "virtual" air flow for non-air zones + } + + return rc; +} // ZNR::zn_AirX //----------------------------------------------------------------------------- -double ZNR::zn_HumRat( // zone moisture ratio - double amfX, // additional system flow (e.g. air handler), lbm/h - double mwX, // air handler moisture flow, lbm/hr - double _tz, - double* pXLGain /*=nullptr*/) +double ZNR::zn_HumRat( // zone moisture ratio + double amfX, // additional system flow (e.g. air handler), lbm/h + double mwX, // air handler moisture flow, lbm/hr + double _tz, double *pXLGain /*=nullptr*/) - // returns zone humidity ratio +// returns zone humidity ratio - - // see also znW() re CNE zone moisture balance +// see also znW() re CNE zone moisture balance { - if (_tz < -98.) - _tz = tz; + if (_tz < -98.) + _tz = tz; #if 1 - RC rc = zn_AirX(amfX, _tz); + RC rc = zn_AirX(amfX, _tz); #else - double rho = psyDenMoistAir(_tz, wzls); // moist air density - double dryAirMass = max(i.znVol * psyDenDryAir2(rho, wzls), .1); // dry air mass, lbm - zn_dryAirMass = dryAirMass; - zn_dryAirMassEff = dryAirMass; // effective dry air mass, lbm - // zn_AirXMoistureBal() may adjust re short time steps - - // TODO: improve other infil models? - zn_ivAirX = (zn_AnAmf() + zn_NonAnIVAmf()) / dryAirMass; - // air changes due to infil + vent - // includes flows induced by ducts / HVAC - // but not those flows - if (zn_ivAirX < 0.f) - zn_ivAirX = 0.f; - - double amfSys = zn_ductLkI.af_amf + zn_sysAirI.af_amf + amfX; - zn_airX = zn_ivAirX + amfSys / dryAirMass; - - // modified airX re convection coefficients - if (!i.zn_hcAirXIsSet) // if not fixed by user input - { - i.zn_hcAirX = zn_airX; // note: does NOT include zn_hpwhAirX - if (zn_IsAirHVACActive() && !zn_HasAirHVAC()) - i.zn_hcAirX += 4.8f; // additional "virtual" air flow for non-air zones - } -#endif - - // internal gain - double mwIG = znLGain / PsyHCondWtr; // latent internal gains, lbm/hr - - // non-airnet infil, lbm/hr - double mwInf = zn_NonAnIVAmf() * Top.wOSh; - - // IZXFER (airnet) gains not including duct leakage and HVAC - double mwAN = (1.f - zn_fVent) * zn_airNetI[0].af_Wmf() - + (zn_fVent)*zn_airNetI[1].af_Wmf(); - - double mw = mwIG + mwInf + mwAN + mwX // total water vapor mass flow rate, lbm/hr - + zn_ductLkI.af_Wmf() + zn_sysAirI.af_Wmf(); - - // TODO: HPWH moisture removal? 2-16 - - int wCase = 0; // debug aid - // 0 = time constant OK, result OK - // 1 = short time constant (steady state sln used), result OK - // 2 = time constant OK, result limited - // 3 = short time constant (steady state sln used), result clamped - - - float f = zn_airX * Top.tp_subhrDur / i.zn_HIRatio; - if (f > 1.f) - { - zn_dryAirMassEff *= f; - f = 1.f; - wCase += 1; - } - double _wz = mw * Top.tp_subhrDur / (zn_dryAirMassEff * i.zn_HIRatio) + wzls * (1. - f); - - double wSat = psyHumRat3(_tz); // maximum w air at this temp can hold - double XLGain = 0.; // excess gain to space due to "condensation" or "evaporation", Btuh - // if predicted humidity ratio is physically impossible, assume - // magic (sensible) heat transfer - // + = into zone (= "condensation") - double wzClamp = bracket(double(PsyWmin), _wz, wSat); - if (_wz != wzClamp) - { - XLGain = (_wz - wzClamp) * zn_dryAirMassEff * i.zn_HIRatio * PsyHCondWtr / Top.tp_subhrDur; - _wz = wzClamp; - wCase += 2; // limits applied - } - - if (pXLGain) - *pXLGain = XLGain; - - return _wz; - -} // ZNR::zn_HumRat + double rho = psyDenMoistAir(_tz, wzls); // moist air density + double dryAirMass = + max(i.znVol * psyDenDryAir2(rho, wzls), .1); // dry air mass, lbm + zn_dryAirMass = dryAirMass; + zn_dryAirMassEff = + dryAirMass; // effective dry air mass, lbm + // zn_AirXMoistureBal() may adjust re short time steps + + // TODO: improve other infil models? + zn_ivAirX = (zn_AnAmf() + zn_NonAnIVAmf()) / dryAirMass; + // air changes due to infil + vent + // includes flows induced by ducts / HVAC + // but not those flows + if (zn_ivAirX < 0.f) + zn_ivAirX = 0.f; + + double amfSys = zn_ductLkI.af_amf + zn_sysAirI.af_amf + amfX; + zn_airX = zn_ivAirX + amfSys / dryAirMass; + + // modified airX re convection coefficients + if (!i.zn_hcAirXIsSet) // if not fixed by user input + { + i.zn_hcAirX = zn_airX; // note: does NOT include zn_hpwhAirX + if (zn_IsAirHVACActive() && !zn_HasAirHVAC()) + i.zn_hcAirX += 4.8f; // additional "virtual" air flow for non-air zones + } +#endif + + // internal gain + double mwIG = znLGain / PsyHCondWtr; // latent internal gains, lbm/hr + + // non-airnet infil, lbm/hr + double mwInf = zn_NonAnIVAmf() * Top.wOSh; + + // IZXFER (airnet) gains not including duct leakage and HVAC + double mwAN = (1.f - zn_fVent) * zn_airNetI[0].af_Wmf() + + (zn_fVent)*zn_airNetI[1].af_Wmf(); + + double mw = mwIG + mwInf + mwAN + + mwX // total water vapor mass flow rate, lbm/hr + + zn_ductLkI.af_Wmf() + zn_sysAirI.af_Wmf(); + + // TODO: HPWH moisture removal? 2-16 + + int wCase = + 0; // debug aid + // 0 = time constant OK, result OK + // 1 = short time constant (steady state sln used), result OK + // 2 = time constant OK, result limited + // 3 = short time constant (steady state sln used), result clamped + + float f = zn_airX * Top.tp_subhrDur / i.zn_HIRatio; + if (f > 1.f) { + zn_dryAirMassEff *= f; + f = 1.f; + wCase += 1; + } + double _wz = mw * Top.tp_subhrDur / (zn_dryAirMassEff * i.zn_HIRatio) + + wzls * (1. - f); + + double wSat = psyHumRat3(_tz); // maximum w air at this temp can hold + double XLGain = + 0.; // excess gain to space due to "condensation" or "evaporation", Btuh + // if predicted humidity ratio is physically impossible, assume + // magic (sensible) heat transfer + // + = into zone (= "condensation") + double wzClamp = bracket(double(PsyWmin), _wz, wSat); + if (_wz != wzClamp) { + XLGain = (_wz - wzClamp) * zn_dryAirMassEff * i.zn_HIRatio * PsyHCondWtr / + Top.tp_subhrDur; + _wz = wzClamp; + wCase += 2; // limits applied + } + + if (pXLGain) + *pXLGain = XLGain; + + return _wz; + +} // ZNR::zn_HumRat #endif //------------------------------------------------------------------------------ -RC ZNR::zn_ComfortCR() // calculate comfort conditions, conv/radiant model +RC ZNR::zn_ComfortCR() // calculate comfort conditions, conv/radiant model // sets zn_comfPMV7730 and zn_comfPPD7730 { - RC rc = RCOK; + RC rc = RCOK; #ifdef COMFORT_MODEL - if (zn_pComf) - { - float wComf; - if (i.znComfUseZoneRH) - { wComf = wz; - i.znComfRh = zn_relHum; - } - else - wComf = psyHumRat2( tz, i.znComfRh); - - int ret = zn_pComf->CalcPMV_IP( tz, tr, - wComf, i.znComfAirV, - i.znComfMet, i.znComfClo); - if (ret==0) - { zn_comfPMV7730 = zn_pComf->GetPMV(); - zn_comfPPD7730 = zn_pComf->GetPPD() * 100.f; - } - else - rc = errCrit( WRN, "Zone '%s': Comfort calculation failure", Name()); - } -#endif - return rc; -} // ZNR::zn_ComfortCR + if (zn_pComf) { + float wComf; + if (i.znComfUseZoneRH) { + wComf = wz; + i.znComfRh = zn_relHum; + } else + wComf = psyHumRat2(tz, i.znComfRh); + + int ret = zn_pComf->CalcPMV_IP(tz, tr, wComf, i.znComfAirV, i.znComfMet, + i.znComfClo); + if (ret == 0) { + zn_comfPMV7730 = zn_pComf->GetPMV(); + zn_comfPPD7730 = zn_pComf->GetPPD() * 100.f; + } else + rc = errCrit(WRN, "Zone '%s': Comfort calculation failure", Name()); + } +#endif + return rc; +} // ZNR::zn_ComfortCR //----------------------------------------------------------------------------- -bool ZNR::zn_IsAirHVACActive() const // determine air motion +bool ZNR::zn_IsAirHVACActive() const // determine air motion // used re convective coefficient determination // see SBC::sb_HCZone case C_CONVMODELCH_ASHRAE // returns true iff air system is operating (causing air motion) // Note: relies on zn_qsHvac not initialized (prior step value persists) { - // TODO: could enhance re other air movers (e.g. whole house fans) - return zn_qsHvac != 0.; -} // ZNR::zn_IsAirMovingMech + // TODO: could enhance re other air movers (e.g. whole house fans) + return zn_qsHvac != 0.; +} // ZNR::zn_IsAirMovingMech //============================================================================= -/*virtual*/ RSYS::~RSYS() -{ - delete rs_pRgiHtg[0]; - delete rs_pRgiHtg[1]; - rs_pRgiHtg[0] = rs_pRgiHtg[1] = nullptr; - delete rs_pRgiClg; - rs_pRgiClg = nullptr; - delete rs_pCHDHW; - rs_pCHDHW = nullptr; - -} // RSYS::~RSYS -//---------------------------------------------------------------------------- -/*virtual*/ void RSYS::Copy( const record* pSrc, int options/*=0*/) -{ - rs_desc.Release(); - record::Copy( pSrc, options); - rs_desc.FixAfterCopy(); -} // RSYS::Copy +/*virtual*/ RSYS::~RSYS() { + delete rs_pRgiHtg[0]; + delete rs_pRgiHtg[1]; + rs_pRgiHtg[0] = rs_pRgiHtg[1] = nullptr; + delete rs_pRgiClg; + rs_pRgiClg = nullptr; + delete rs_pCHDHW; + rs_pCHDHW = nullptr; + +} // RSYS::~RSYS + //---------------------------------------------------------------------------- +/*virtual*/ void RSYS::Copy(const record *pSrc, int options /*=0*/) { + rs_desc.Release(); + record::Copy(pSrc, options); + rs_desc.FixAfterCopy(); +} // RSYS::Copy //------------------------------------------------------------------------------- -RC RSYS::rs_CkF() -{ - static_assert(RSYSMODES == rsmCOUNT - 1); // cndefn.h #define must be consistent - // with cnrecs.def enum - int rc = RCOK; - - rc |= rs_CkFHeating(); - - rc |= rs_CkFCooling(); - - // OAV - if (rs_OAVType == C_RSYSOAVTYCH_NONE) - rc |= disallowN( "when rsOAVType=None", - RSYS_OAVRELIEFZI, RSYS_OAVTDBINLET, RSYS_OAVTDIFF, - RSYS_OAVAVFDS, RSYS_OAVAVFMINF, RSYS_OAVFANPWR, - 0); - else - { const char* whenOAV = "when rsOAVType is given"; - rc |= requireN( whenOAV, RSYS_OAVRELIEFZI, RSYS_OAVAVFDS, 0); - if (IsVal( RSYS_OAVTDIFF) && rs_OAVTdiff < 2.f) - oWarn( "Dubious rsOAVTdiff (%0.2f F) may cause inadvertent vent heating." - "\n rsOAVTdiff is typically >= 5 F to allow for fan heat and duct gains", - rs_OAVTdiff); - // rsOAVFanPwr (W/cfm): error if >5, warn if >2 - rc |= limitCheck( RSYS_OAVFANPWR, 0., 5., 0., 2.); - } - - return rc; -} // RSYS::rs_CkF +RC RSYS::rs_CkF() { + static_assert(RSYSMODES == + rsmCOUNT - 1); // cndefn.h #define must be consistent + // with cnrecs.def enum + int rc = RCOK; + + rc |= rs_CkFHeating(); + + rc |= rs_CkFCooling(); + + // OAV + if (rs_OAVType == C_RSYSOAVTYCH_NONE) + rc |= disallowN("when rsOAVType=None", RSYS_OAVRELIEFZI, RSYS_OAVTDBINLET, + RSYS_OAVTDIFF, RSYS_OAVAVFDS, RSYS_OAVAVFMINF, + RSYS_OAVFANPWR, 0); + else { + const char *whenOAV = "when rsOAVType is given"; + rc |= requireN(whenOAV, RSYS_OAVRELIEFZI, RSYS_OAVAVFDS, 0); + if (IsVal(RSYS_OAVTDIFF) && rs_OAVTdiff < 2.f) + oWarn("Dubious rsOAVTdiff (%0.2f F) may cause inadvertent vent heating." + "\n rsOAVTdiff is typically >= 5 F to allow for fan heat and " + "duct gains", + rs_OAVTdiff); + // rsOAVFanPwr (W/cfm): error if >5, warn if >2 + rc |= limitCheck(RSYS_OAVFANPWR, 0., 5., 0., 2.); + } + + return rc; +} // RSYS::rs_CkF //----------------------------------------------------------------------------- -RC RSYS::rs_CkFHeating() -{ - RC rc = RCOK; - - rc |= rs_CkFAuxHeat(); // check aux heat inputs +RC RSYS::rs_CkFHeating() { + RC rc = RCOK; + rc |= rs_CkFAuxHeat(); // check aux heat inputs #if 0 // all heating non-ASHP fields EXCEPT RSYS_CAPH, RSYS_FANPWRH, RSYS_FXCAPHTARG @@ -2081,347 +2119,331 @@ RC RSYS::rs_CkFHeating() static constexpr int16_t htgFNs[]{ RSYS_AFUE, RSYS_CAPNOMH, RSYS_TDDESH, RSYS_FEFFH, 0 }; #endif - // ASHP heating FNs (all types) - static constexpr int16_t ASHP_HtgFNs[]{ RSYS_HSPF, RSYS_CAP47, RSYS_COP47, - RSYS_CAP35, RSYS_COP35, RSYS_CAP17, RSYS_COP17, RSYS_ASHPLOCKOUTT, - RSYS_CAPRAT1747, RSYS_CAPRAT9547, RSYS_CAPRATCH, RSYS_CDH, - RSYS_DEFROSTMODEL, 0 }; - - // ASHPVC (VCHP2) active FNs - static constexpr int16_t ASHPVC_HtgFNs[]{ RSYS_LOADFMIN47, RSYS_LOADFMIN17, RSYS_LOADFMIN05, - RSYS_COPMIN47, RSYS_COPMIN35, RSYS_COPMIN17, RSYS_COPMIN05, - RSYS_CAPRAT0547, RSYS_CAP05, RSYS_COP05, 0 }; - - - if (!rs_CanHeat()) - { - const char* whenTyNoHt = strtprintf("when rsType=%s (heating not available)", - getChoiTx(RSYS_TYPE)); - rc |= ignoreX(whenTyNoHt, RSYS_CAPH, RSYS_FANPWRH, RSYS_FXCAPHTARG, - RSYS_AFUE, RSYS_CAPNOMH, RSYS_TDDESH, RSYS_FEFFH, RSYS_CHDHWSYSI, - ASHP_HtgFNs, ASHPVC_HtgFNs); - FldSet(RSYS_CAPNOMH, 0.f); // insurance - - return rc; - } - const char* whenTy = strtprintf("when rsType=%s", getChoiTx(RSYS_TYPE)); - - if (!rs_IsCHDHW()) - { - disallow(whenTy, RSYS_CHDHWSYSI); - } - - if (rs_IsFanCoil()) - { // fancoil htg - rc |= require(whenTy, RSYS_CAPH); - rc |= ignoreX(whenTy, RSYS_AFUE, RSYS_CAPNOMH, RSYS_FEFFH, - ASHP_HtgFNs, ASHPVC_HtgFNs); - rs_AFUE = 0.f; - } - else if (rs_IsWSHP()) - { - rc |= requireN(whenTy, RSYS_TDBOUT, 0); - - rc |= disallowN(whenTy, RSYS_AFUE, 0); - - if (!IsSet(RSYS_CAPH) && !IsSet(RSYS_CAP95)) - rc |= oer("at least one of rsCapH and rsCapC must be specified %s", whenTy); - - if (IsSetCount(RSYS_CAPH, RSYS_CAP95, RSYS_CAPRATCH, 0) == 3 - && !(IsAusz(RSYS_CAPH) && IsAusz(RSYS_CAP95))) - // cannot give all 3 values unless both caps are autosized - rc |= oer("rsCapH, rsCapC, and rsCapRatCH cannot all be specfied %s", whenTy); - - rc |= ignoreX(whenTy, RSYS_HSPF, RSYS_CAP47, - RSYS_CAP35, RSYS_COP35, RSYS_CAP17, RSYS_COP17, RSYS_ASHPLOCKOUTT, - RSYS_CAPRAT1747, RSYS_CAPRAT9547, RSYS_DEFROSTMODEL, ASHPVC_HtgFNs); - } - else if (rs_IsASHP()) - { - if (!IsSet(RSYS_CAP47) && !IsSet(RSYS_CAP95)) - rc |= oer("at least one of rsCap47 and rsCapC must be specified %s", whenTy); - - if (IsSetCount(RSYS_CAP47, RSYS_CAP95, RSYS_CAPRAT9547, 0) == 3 - && !(IsAusz(RSYS_CAP47) && IsAusz(RSYS_CAP95))) - // cannot give all 3 values unless both caps are autosized - rc |= oer("rsCap47, rsCapC, and rsCapRat9547 cannot all be specfied %s", whenTy); - - if (rs_IsASHPHydronic()) - { // ASHPHydronic: air-to-water heat pump - // approximated with air-to-air model - rc |= requireN(whenTy, RSYS_COP47, RSYS_COP17, 0); - rc |= disallowN(whenTy, RSYS_HSPF, RSYS_CAPH, RSYS_AFUE, RSYS_DEFROSTMODEL, 0); - if (!IsSet(RSYS_FANPWRH)) - rs_fanPwrH = 0.f; // hydronic: no fan power - } - else - { // ASHP other than ASHPHydronic - - rc |= disallowN(whenTy, RSYS_CAPH, RSYS_AFUE, 0); - - if (rs_IsASHPPkgRoom()) - { - rc |= disallowN(whenTy, RSYS_HSPF, RSYS_CAP05, RSYS_COP05, - RSYS_CAP17, RSYS_COP17, RSYS_CAP35, RSYS_COP35, 0); - rc |= requireN(whenTy, RSYS_COP47, 0); - - if (!IsSet(RSYS_FANPWRH)) - rs_fanPwrH = 0.f; // fan power included in primary by default - - // other defaults derived in rs_TopRSys1() - } - else - { // ASHP (non-hydronic, non-pkgroom): only HSPF required - // capacities defaulted from cooling cap95 - // COPs defaulted from HSPF - // if both COP47 and COP17 are specified, HSPF is not used - - rc |= requireN(whenTy, RSYS_HSPF, 0); - - if (IsAusz(RSYS_CAP47)) - rc |= disallowN("when rsCap47 is AUTOSIZE", - RSYS_CAP05, RSYS_CAP17, RSYS_CAP35, 0); - else - { if (IsSet(RSYS_CAP17)) - rc |= disallowN("when rsCap17 is given", RSYS_CAPRAT1747, 0); - if (IsSet(RSYS_CAP05)) - rc |= disallowN("when rsCap05 is given", RSYS_CAPRAT0547, 0); - } - - if (!rs_IsASHPVC()) - { rc |= ignore("when rsType is not ASHPVC (VCHP2)", - ASHPVC_HtgFNs); - rs_loadFMin05 = rs_loadFMin17 = rs_loadFMin47 = 1.f; - } - else - { // default loadFMins - FldCopyIf(RSYS_LOADFMIN47, RSYS_LOADFMIN17); - FldCopyIf(RSYS_LOADFMIN47, RSYS_LOADFMIN05); - } - } - } - - // all air source heat pumps - if (!IsAusz(RSYS_CAP47)) - // rs_cap47 not AUTOSIZEd (altho may be expression), use it as rs_capNomH default - // see DefaultCapNomsIf() - FldCopyIf(RSYS_CAP47, RSYS_CAPNOMH); - } - else if (rs_IsCHDHW()) - { // combined heat and DHW - rc |= requireX(whenTy, RSYS_CHDHWSYSI); - rc |= disallowX(whenTy, RSYS_TDDESH); - if (IsAusz( RSYS_CAPH)) - rc |= oer("rsCapH cannot be AUTOSIZE %s", whenTy); - else - rc |= ignoreX(whenTy, RSYS_CAPH); - // rs_CdH? - rc |= disallowX("when rsType is CombinedHeatDHW or ACCombinedHeatDHW", - ASHP_HtgFNs, ASHPVC_HtgFNs); - } - else - { // not CHDHW or HP of any type - rc |= disallowX("when rsType is not ASHP, ASHPHydronic, ASHPPkgRoom, or ASHPVC (VCHP2)", - ASHP_HtgFNs, ASHPVC_HtgFNs); - - // default AFUE (CULT default = 0) - if (!IsSet(RSYS_AFUE)) - rs_AFUE = rs_IsElecHeat() ? 1.f : 0.9; - - if (!IsAusz(RSYS_CAPH)) - // rs_capH not AUTOSIZEd (altho may be expression), use it as rs_capNomH default - FldCopyIf(RSYS_CAPH, RSYS_CAPNOMH); - } - - return rc; -} // RSYS::rs_CkFHeating + // ASHP heating FNs (all types) + static constexpr int16_t ASHP_HtgFNs[]{RSYS_HSPF, RSYS_CAP47, + RSYS_COP47, RSYS_CAP35, + RSYS_COP35, RSYS_CAP17, + RSYS_COP17, RSYS_ASHPLOCKOUTT, + RSYS_CAPRAT1747, RSYS_CAPRAT9547, + RSYS_CAPRATCH, RSYS_CDH, + RSYS_DEFROSTMODEL, 0}; + + // ASHPVC (VCHP2) active FNs + static constexpr int16_t ASHPVC_HtgFNs[]{RSYS_LOADFMIN47, + RSYS_LOADFMIN17, + RSYS_LOADFMIN05, + RSYS_COPMIN47, + RSYS_COPMIN35, + RSYS_COPMIN17, + RSYS_COPMIN05, + RSYS_CAPRAT0547, + RSYS_CAP05, + RSYS_COP05, + 0}; + + if (!rs_CanHeat()) { + const char *whenTyNoHt = strtprintf( + "when rsType=%s (heating not available)", getChoiTx(RSYS_TYPE)); + rc |= ignoreX(whenTyNoHt, RSYS_CAPH, RSYS_FANPWRH, RSYS_FXCAPHTARG, + RSYS_AFUE, RSYS_CAPNOMH, RSYS_TDDESH, RSYS_FEFFH, + RSYS_CHDHWSYSI, ASHP_HtgFNs, ASHPVC_HtgFNs); + FldSet(RSYS_CAPNOMH, 0.f); // insurance + + return rc; + } + const char *whenTy = strtprintf("when rsType=%s", getChoiTx(RSYS_TYPE)); + + if (!rs_IsCHDHW()) { + disallow(whenTy, RSYS_CHDHWSYSI); + } + + if (rs_IsFanCoil()) { // fancoil htg + rc |= require(whenTy, RSYS_CAPH); + rc |= ignoreX(whenTy, RSYS_AFUE, RSYS_CAPNOMH, RSYS_FEFFH, ASHP_HtgFNs, + ASHPVC_HtgFNs); + rs_AFUE = 0.f; + } else if (rs_IsWSHP()) { + rc |= requireN(whenTy, RSYS_TDBOUT, 0); + + rc |= disallowN(whenTy, RSYS_AFUE, 0); + + if (!IsSet(RSYS_CAPH) && !IsSet(RSYS_CAP95)) + rc |= + oer("at least one of rsCapH and rsCapC must be specified %s", whenTy); + + if (IsSetCount(RSYS_CAPH, RSYS_CAP95, RSYS_CAPRATCH, 0) == 3 && + !(IsAusz(RSYS_CAPH) && IsAusz(RSYS_CAP95))) + // cannot give all 3 values unless both caps are autosized + rc |= oer("rsCapH, rsCapC, and rsCapRatCH cannot all be specfied %s", + whenTy); + + rc |= ignoreX(whenTy, RSYS_HSPF, RSYS_CAP47, RSYS_CAP35, RSYS_COP35, + RSYS_CAP17, RSYS_COP17, RSYS_ASHPLOCKOUTT, RSYS_CAPRAT1747, + RSYS_CAPRAT9547, RSYS_DEFROSTMODEL, ASHPVC_HtgFNs); + } else if (rs_IsASHP()) { + if (!IsSet(RSYS_CAP47) && !IsSet(RSYS_CAP95)) + rc |= oer("at least one of rsCap47 and rsCapC must be specified %s", + whenTy); + + if (IsSetCount(RSYS_CAP47, RSYS_CAP95, RSYS_CAPRAT9547, 0) == 3 && + !(IsAusz(RSYS_CAP47) && IsAusz(RSYS_CAP95))) + // cannot give all 3 values unless both caps are autosized + rc |= oer("rsCap47, rsCapC, and rsCapRat9547 cannot all be specfied %s", + whenTy); + + if (rs_IsASHPHydronic()) { // ASHPHydronic: air-to-water heat pump + // approximated with air-to-air model + rc |= requireN(whenTy, RSYS_COP47, RSYS_COP17, 0); + rc |= disallowN(whenTy, RSYS_HSPF, RSYS_CAPH, RSYS_AFUE, + RSYS_DEFROSTMODEL, 0); + if (!IsSet(RSYS_FANPWRH)) + rs_fanPwrH = 0.f; // hydronic: no fan power + } else { // ASHP other than ASHPHydronic + + rc |= disallowN(whenTy, RSYS_CAPH, RSYS_AFUE, 0); + + if (rs_IsASHPPkgRoom()) { + rc |= disallowN(whenTy, RSYS_HSPF, RSYS_CAP05, RSYS_COP05, RSYS_CAP17, + RSYS_COP17, RSYS_CAP35, RSYS_COP35, 0); + rc |= requireN(whenTy, RSYS_COP47, 0); + + if (!IsSet(RSYS_FANPWRH)) + rs_fanPwrH = 0.f; // fan power included in primary by default + + // other defaults derived in rs_TopRSys1() + } else { // ASHP (non-hydronic, non-pkgroom): only HSPF required + // capacities defaulted from cooling cap95 + // COPs defaulted from HSPF + // if both COP47 and COP17 are specified, HSPF is not used + + rc |= requireN(whenTy, RSYS_HSPF, 0); + + if (IsAusz(RSYS_CAP47)) + rc |= disallowN("when rsCap47 is AUTOSIZE", RSYS_CAP05, RSYS_CAP17, + RSYS_CAP35, 0); + else { + if (IsSet(RSYS_CAP17)) + rc |= disallowN("when rsCap17 is given", RSYS_CAPRAT1747, 0); + if (IsSet(RSYS_CAP05)) + rc |= disallowN("when rsCap05 is given", RSYS_CAPRAT0547, 0); + } + + if (!rs_IsASHPVC()) { + rc |= ignore("when rsType is not ASHPVC (VCHP2)", ASHPVC_HtgFNs); + rs_loadFMin05 = rs_loadFMin17 = rs_loadFMin47 = 1.f; + } else { // default loadFMins + FldCopyIf(RSYS_LOADFMIN47, RSYS_LOADFMIN17); + FldCopyIf(RSYS_LOADFMIN47, RSYS_LOADFMIN05); + } + } + } + + // all air source heat pumps + if (!IsAusz(RSYS_CAP47)) + // rs_cap47 not AUTOSIZEd (altho may be expression), use it as rs_capNomH + // default + // see DefaultCapNomsIf() + FldCopyIf(RSYS_CAP47, RSYS_CAPNOMH); + } else if (rs_IsCHDHW()) { // combined heat and DHW + rc |= requireX(whenTy, RSYS_CHDHWSYSI); + rc |= disallowX(whenTy, RSYS_TDDESH); + if (IsAusz(RSYS_CAPH)) + rc |= oer("rsCapH cannot be AUTOSIZE %s", whenTy); + else + rc |= ignoreX(whenTy, RSYS_CAPH); + // rs_CdH? + rc |= disallowX("when rsType is CombinedHeatDHW or ACCombinedHeatDHW", + ASHP_HtgFNs, ASHPVC_HtgFNs); + } else { // not CHDHW or HP of any type + rc |= disallowX( + "when rsType is not ASHP, ASHPHydronic, ASHPPkgRoom, or ASHPVC (VCHP2)", + ASHP_HtgFNs, ASHPVC_HtgFNs); + + // default AFUE (CULT default = 0) + if (!IsSet(RSYS_AFUE)) + rs_AFUE = rs_IsElecHeat() ? 1.f : 0.9; + + if (!IsAusz(RSYS_CAPH)) + // rs_capH not AUTOSIZEd (altho may be expression), use it as rs_capNomH + // default + FldCopyIf(RSYS_CAPH, RSYS_CAPNOMH); + } + + return rc; +} // RSYS::rs_CkFHeating //----------------------------------------------------------------------------- -RC RSYS::rs_CkFCooling() -{ - // compression cooling FNs - static constexpr int16_t Clg_FNs[] = { RSYS_SEER, RSYS_EER95, RSYS_COP95, - RSYS_CAP82, RSYS_COP82, RSYS_CAPRAT8295, RSYS_CAP115, RSYS_COP115, RSYS_CAPRAT11595, - RSYS_FCHG, RSYS_CDC, 0 }; - - // FNs meaningful only for variable capacity - // ignored for non-VC ASHP - // disallowed for others - static constexpr int16_t VC_ClgFNs[] = { RSYS_LOADFMIN115, RSYS_LOADFMIN95, RSYS_LOADFMIN82, - RSYS_COPMIN115, RSYS_COPMIN95, RSYS_COPMIN82, 0 }; - - int rc = RCOK; - - if (!rs_CanCool()) - { // no cooling capability - const char* whenNoCl = strtprintf("when rsType=%s (cooling not available)", - getChoiTx(RSYS_TYPE)); - rc |= ignoreX(whenNoCl, RSYS_CAP95, RSYS_FANPWRC, RSYS_CAPNOMC, RSYS_VFPERTON, - Clg_FNs, VC_ClgFNs); - FldSet(RSYS_CAPNOMC, 0.f); // insurance - return rc; - } - - const char* whenTy = strtprintf("when rsType=%s", getChoiTx(RSYS_TYPE)); - if (rs_IsPkgRoom()) - { - rc |= requireN(whenTy, RSYS_EER95, 0); - rc |= disallowN(whenTy, RSYS_SEER, RSYS_COP95, 0); - if (!IsSet(RSYS_FANPWRC)) - rs_fanPwrC = 0.f; // fan power included in primary by default - } - else if (rs_IsFanCoil()) - { // fancoil cooling - rc |= require(whenTy, RSYS_CAP95); - rc |= ignoreX(whenTy, Clg_FNs, VC_ClgFNs); - } - else if (rs_IsWSHP()) - { // note checks of RSYS_TDBOUT and RSYS_CAP95 in rs_CkFHeating() - - rc |= requireN(whenTy, RSYS_EER95, 0); - - rc |= ignoreX(whenTy, RSYS_SEER, - VC_ClgFNs); - } - else - rc |= requireN(whenTy, RSYS_SEER, 0); - - if (!IsAusz(RSYS_CAP95)) - { // rs_cap95 not AUTOSIZEd (altho may be expression), use as rs_capNomC default - FldCopyIf(RSYS_CAP95, RSYS_CAPNOMC); - - if (IsSet(RSYS_CAP82)) - rc |= disallowN("when rsCap82 is given", RSYS_CAPRAT8295, 0); - if (IsSet(RSYS_CAP115)) - rc |= disallowN("when rsCap115 is given", RSYS_CAPRAT11595, 0); - } - else - { // rsCap95 is autosize - - rc |= disallowN("when rsCap95 is AUTOSIZE", - RSYS_CAP82, RSYS_CAP115, 0); - } - - if (rs_IsASHPVC()) - { - FldCopyIf(RSYS_LOADFMIN95, RSYS_LOADFMIN82); - FldCopyIf(RSYS_LOADFMIN95, RSYS_LOADFMIN115); - } - else - { - rc |= ignoreX("when rsType is not ASHPVC (VCHP2)", - VC_ClgFNs); - rs_loadFMin82= rs_loadFMin95 = rs_loadFMin115 = 1.f; - } - - return rc; - -} // RSYS::rs_CkFCooling +RC RSYS::rs_CkFCooling() { + // compression cooling FNs + static constexpr int16_t Clg_FNs[] = { + RSYS_SEER, RSYS_EER95, RSYS_COP95, RSYS_CAP82, + RSYS_COP82, RSYS_CAPRAT8295, RSYS_CAP115, RSYS_COP115, + RSYS_CAPRAT11595, RSYS_FCHG, RSYS_CDC, 0}; + + // FNs meaningful only for variable capacity + // ignored for non-VC ASHP + // disallowed for others + static constexpr int16_t VC_ClgFNs[] = {RSYS_LOADFMIN115, + RSYS_LOADFMIN95, + RSYS_LOADFMIN82, + RSYS_COPMIN115, + RSYS_COPMIN95, + RSYS_COPMIN82, + 0}; + + int rc = RCOK; + + if (!rs_CanCool()) { // no cooling capability + const char *whenNoCl = strtprintf("when rsType=%s (cooling not available)", + getChoiTx(RSYS_TYPE)); + rc |= ignoreX(whenNoCl, RSYS_CAP95, RSYS_FANPWRC, RSYS_CAPNOMC, + RSYS_VFPERTON, Clg_FNs, VC_ClgFNs); + FldSet(RSYS_CAPNOMC, 0.f); // insurance + return rc; + } + + const char *whenTy = strtprintf("when rsType=%s", getChoiTx(RSYS_TYPE)); + if (rs_IsPkgRoom()) { + rc |= requireN(whenTy, RSYS_EER95, 0); + rc |= disallowN(whenTy, RSYS_SEER, RSYS_COP95, 0); + if (!IsSet(RSYS_FANPWRC)) + rs_fanPwrC = 0.f; // fan power included in primary by default + } else if (rs_IsFanCoil()) { // fancoil cooling + rc |= require(whenTy, RSYS_CAP95); + rc |= ignoreX(whenTy, Clg_FNs, VC_ClgFNs); + } else if (rs_IsWSHP()) { // note checks of RSYS_TDBOUT and RSYS_CAP95 in + // rs_CkFHeating() + + rc |= requireN(whenTy, RSYS_EER95, 0); + + rc |= ignoreX(whenTy, RSYS_SEER, VC_ClgFNs); + } else + rc |= requireN(whenTy, RSYS_SEER, 0); + + if (!IsAusz(RSYS_CAP95)) { // rs_cap95 not AUTOSIZEd (altho may be + // expression), use as rs_capNomC default + FldCopyIf(RSYS_CAP95, RSYS_CAPNOMC); + + if (IsSet(RSYS_CAP82)) + rc |= disallowN("when rsCap82 is given", RSYS_CAPRAT8295, 0); + if (IsSet(RSYS_CAP115)) + rc |= disallowN("when rsCap115 is given", RSYS_CAPRAT11595, 0); + } else { // rsCap95 is autosize + + rc |= disallowN("when rsCap95 is AUTOSIZE", RSYS_CAP82, RSYS_CAP115, 0); + } + + if (rs_IsASHPVC()) { + FldCopyIf(RSYS_LOADFMIN95, RSYS_LOADFMIN82); + FldCopyIf(RSYS_LOADFMIN95, RSYS_LOADFMIN115); + } else { + rc |= ignoreX("when rsType is not ASHPVC (VCHP2)", VC_ClgFNs); + rs_loadFMin82 = rs_loadFMin95 = rs_loadFMin115 = 1.f; + } + + return rc; + +} // RSYS::rs_CkFCooling //----------------------------------------------------------------------------- -RC RSYS::rs_CkFCooling2() // additional cooling checks +RC RSYS::rs_CkFCooling2() // additional cooling checks // call after all values set (including calculated defaults) // returns RCOK if all check passed // else nz value { - RC rc = RCOK; - - if (!rs_IsFanCoil() && !rs_IsWSHP() && !rs_IsVCClg()) - { // check that SEER and EER are plausible - if (rs_SEER <= rs_EER95) - rc |= oer("rsSEER (%g) must be > rsEER (%g)", rs_SEER, rs_EER95); - } - - // if (rs_VCClg()) -- no check for all rs_CanCool() types (insurance) - { // checks to prevent crazy extrapolation results - // cap82 > cap95 typically; occasional cap82 < cap95 examples seen so use 0.8 - rc |= rs_CkFRatio(RSYS_CAP82, RSYS_CAP95, RSYS_CAPRAT8295, 0.8f, 2.f); - // cap115 < cap95 always - rc |= rs_CkFRatio(RSYS_CAP115, RSYS_CAP95, RSYS_CAPRAT11595, .2f, 1.f); - } - - return rc; -} // RSYS::rs_CkFCooling2 + RC rc = RCOK; + + if (!rs_IsFanCoil() && !rs_IsWSHP() && + !rs_IsVCClg()) { // check that SEER and EER are plausible + if (rs_SEER <= rs_EER95) + rc |= oer("rsSEER (%g) must be > rsEER (%g)", rs_SEER, rs_EER95); + } + + // if (rs_VCClg()) -- no check for all rs_CanCool() types (insurance) + { // checks to prevent crazy extrapolation results + // cap82 > cap95 typically; occasional cap82 < cap95 examples seen so use + // 0.8 + rc |= rs_CkFRatio(RSYS_CAP82, RSYS_CAP95, RSYS_CAPRAT8295, 0.8f, 2.f); + // cap115 < cap95 always + rc |= rs_CkFRatio(RSYS_CAP115, RSYS_CAP95, RSYS_CAPRAT11595, .2f, 1.f); + } + + return rc; +} // RSYS::rs_CkFCooling2 //----------------------------------------------------------------------------- -RC RSYS::rs_CkFCd( // default and check Cd values - int mode) // rsmCOOL or rsmHEAT +RC RSYS::rs_CkFCd( // default and check Cd values + int mode) // rsmCOOL or rsmHEAT // returns RCOK iff rs_CdX is acceptable // else !RCOK (user input out of range) { - RC rc = RCOK; - if (mode == rsmCOOL) - { if (!IsSet(RSYS_CDC)) - rs_CdC = rs_IsVCClg() || rs_IsWSHP() ? 0.25f : 0.f; - else - rc |= limitCheckFix(RSYS_CDC, 0.f, .5f); - } - else - { // not rsmCOOL, assume rsmHEAT - if (!IsSet(RSYS_CDH)) - rs_CdH = rs_IsASHPHydronic() || rs_IsASHPPkgRoom() || rs_IsASHPVC() || rs_IsWSHP() - ? 0.25f // hydronic / pkgRoom / WSHP: no HSPF source for default - // ASHPVC: relationship to HSPF not known - : bracket(.05f, .25f - 0.2f*(rs_HSPF - 6.8f) / (10.f - 6.8f), .25f); - else - rc |= limitCheckFix(RSYS_CDH, 0.f, .5f); - } - return rc; -} // RSYS::rs_CkFCd + RC rc = RCOK; + if (mode == rsmCOOL) { + if (!IsSet(RSYS_CDC)) + rs_CdC = rs_IsVCClg() || rs_IsWSHP() ? 0.25f : 0.f; + else + rc |= limitCheckFix(RSYS_CDC, 0.f, .5f); + } else { // not rsmCOOL, assume rsmHEAT + if (!IsSet(RSYS_CDH)) + rs_CdH = + rs_IsASHPHydronic() || rs_IsASHPPkgRoom() || rs_IsASHPVC() || + rs_IsWSHP() + ? 0.25f // hydronic / pkgRoom / WSHP: no HSPF source for default + // ASHPVC: relationship to HSPF not known + : bracket(.05f, .25f - 0.2f * (rs_HSPF - 6.8f) / (10.f - 6.8f), + .25f); + else + rc |= limitCheckFix(RSYS_CDH, 0.f, .5f); + } + return rc; +} // RSYS::rs_CkFCd //----------------------------------------------------------------------------- -RC RSYS::rs_CkFAuxHeat() // check aux heat -{ - RC rc = RCOK; - rs_capAuxHInp = 0.f; - rs_effAuxH = 1.f; // insurance - if (!rs_CanHaveAuxHeat()) - { rc |= disallowN("when rsType is not ASHP, ASHPHydronic, ASHPPkgRoom, or VCHP2", - RSYS_TYPEAUXH, RSYS_CAPAUXH, RSYS_FXCAPAUXHTARG, RSYS_AFUEAUXH, - RSYS_CTRLAUXH, 0); - rs_AFUEAuxH = 0.f; // clear possibly confusing default - } - else if (rs_typeAuxH == C_AUXHEATTY_NONE) - { rc |= disallowN("when rsTypeAuxH = None", - RSYS_CTRLAUXH, RSYS_CAPAUXH, RSYS_FXCAPAUXHTARG, RSYS_AFUEAUXH, 0); - rs_capAuxH = 0.f; - } - else if (!IsAusz(RSYS_CAPAUXH) && rs_capAuxH == 0.f) - { // no auxiliary - rs_typeAuxH = C_AUXHEATTY_NONE; - } - else - { // save input value of rs_capAuxH - // WHY: rs_capAuxH changed during HP autosize - // saved value used to restore original - rs_capAuxHInp = rs_capAuxH; - if (IsAusz(RSYS_CAPAUXH)) - rs_capAuxH = 0.f; // overwrite NANDLE - if (rs_IsFuelAuxH()) - { - if (!IsSet(RSYS_AFUEAUXH)) - rs_AFUEAuxH = 0.9f; // change CULT default (1) to value - // appropriate for furnace - rs_effAuxH = rs_AFUEAuxH; - if (!IsSet(RSYS_CTRLAUXH)) - rs_ctrlAuxH = C_AUXHEATCTRL_ALT; - } - } - - return rc; -} // RSYS::rs_CkFAuxHeat +RC RSYS::rs_CkFAuxHeat() // check aux heat +{ + RC rc = RCOK; + rs_capAuxHInp = 0.f; + rs_effAuxH = 1.f; // insurance + if (!rs_CanHaveAuxHeat()) { + rc |= disallowN( + "when rsType is not ASHP, ASHPHydronic, ASHPPkgRoom, or VCHP2", + RSYS_TYPEAUXH, RSYS_CAPAUXH, RSYS_FXCAPAUXHTARG, RSYS_AFUEAUXH, + RSYS_CTRLAUXH, 0); + rs_AFUEAuxH = 0.f; // clear possibly confusing default + } else if (rs_typeAuxH == C_AUXHEATTY_NONE) { + rc |= disallowN("when rsTypeAuxH = None", RSYS_CTRLAUXH, RSYS_CAPAUXH, + RSYS_FXCAPAUXHTARG, RSYS_AFUEAUXH, 0); + rs_capAuxH = 0.f; + } else if (!IsAusz(RSYS_CAPAUXH) && rs_capAuxH == 0.f) { // no auxiliary + rs_typeAuxH = C_AUXHEATTY_NONE; + } else { // save input value of rs_capAuxH + // WHY: rs_capAuxH changed during HP autosize + // saved value used to restore original + rs_capAuxHInp = rs_capAuxH; + if (IsAusz(RSYS_CAPAUXH)) + rs_capAuxH = 0.f; // overwrite NANDLE + if (rs_IsFuelAuxH()) { + if (!IsSet(RSYS_AFUEAUXH)) + rs_AFUEAuxH = 0.9f; // change CULT default (1) to value + // appropriate for furnace + rs_effAuxH = rs_AFUEAuxH; + if (!IsSet(RSYS_CTRLAUXH)) + rs_ctrlAuxH = C_AUXHEATCTRL_ALT; + } + } + + return rc; +} // RSYS::rs_CkFAuxHeat //----------------------------------------------------------------------------- -RC RSYS::rs_CkFRatio( - int fn1, // field 1 - int fn2, // field 2 - int fnRat, // alternate: ratio field - float vMin, // min allowed value for v1/v2 or vRat - float vMax) // max allowed value for v1/v2 or vRat +RC RSYS::rs_CkFRatio(int fn1, // field 1 + int fn2, // field 2 + int fnRat, // alternate: ratio field + float vMin, // min allowed value for v1/v2 or vRat + float vMax) // max allowed value for v1/v2 or vRat // Expects either (fn1 and fn2) OR fnRat to be set (but not all 3) // Prior checking must verify that (see e.g. rs_CkF) // mutua { - RC rc = IsSet( fn1) - ? limitCheckRatio(fn1, fn2, vMin, vMax) - : limitCheck(fnRat, vMin, vMax); - return rc; -} // RSYS::rs_CkFRatio + RC rc = IsSet(fn1) ? limitCheckRatio(fn1, fn2, vMin, vMax) + : limitCheck(fnRat, vMin, vMax); + return rc; +} // RSYS::rs_CkFRatio //----------------------------------------------------------------------------- #if 0 // incomplete idea, 11-21 @@ -2441,427 +2463,403 @@ RC RSYS::rs_CheckCapAuxH() // check for sufficient aux heat capacity } // RSYS::rs_CheckCapAuxH #endif //----------------------------------------------------------------------------- -RC RSYS::rs_TopRSys1() // check RSYS, initial set up for run -{ - RC rc = RCOK; - - if (!IsSet(RSYS_TDDESH)) - rs_tdDesH = rs_IsHP() ? 30.f // lower default temp rise for ASHP - : 50.f; // (changed later for CHDHW) - - if (rs_IsASHPPkgRoom()) - { if (!IsSet(RSYS_ASHPLOCKOUTT)) - rs_ASHPLockOutT = 45.f; // pkg room ASHP: use resistance at lower temps - rc |= rs_SetupASHP(); - } - - if (rs_IsVC()) - { // VCHP default motor type is BPM (others are PSC from cult) - if (!IsSet(RSYS_FAN + FAN_MOTTY)) - rs_fan.fn_motTy = C_MOTTYCH_BPM; - } - - if (rs_CanCool()) - { // cooling model air flow correlations have limited validity range - // verify air flow 150 - 550 cfm/ton (per Proctor Engineering) - rc |= limitCheck(RSYS_VFPERTON, 150., 550.); - - // rsFanPwrC (W/cfm): error if >5, warn if >2 - rc |= limitCheck(RSYS_FANPWRC, 0., 5., 0., 2.); - - if (rs_IsFanCoil()) - { // nothing to check? - - } - else - { // compression cooling - - rc |= rs_CkFCd(rsmCOOL); - - if (rs_IsWSHP()) - { - rc |= limitCheck(RSYS_CAPRATCH, 0.3, 2.); - - } - else - { // default/harmonize 95 F COP - // inter-default rs_COP95 <-> rs_EER95 - // if both input, values can be different - // rs_COP95 used for VCHP2, rs_EER95 used for single speed - // EER can default from SEER - if (rs_IsPkgRoom()) - { // pkg: derive SEER from EER - // abram conant fit, 6-20 - rs_SEER = 1.07f * rs_EER95; - // rs_COP95 not allowed - } - else if (IsSet(RSYS_EER95)) - { - if (!IsSet(RSYS_COP95)) - rs_COP95 = rs_EER95 / BtuperWh; - } - else - { - if (IsSet(RSYS_COP95)) - rs_EER95 = rs_COP95 * BtuperWh; - else if (!rs_IsVCClg()) - { // estimate missing EER from SEER - // California ACM method - rs_EER95 = rs_SEER < 13.f ? 10.f + 0.84f * (rs_SEER - 11.5f) - : rs_SEER < 16.f ? 11.3f + 0.57f * (rs_SEER - 13.f) - : 13.f; - rs_COP95 = rs_EER95 / BtuperWh; - } - } - } - } - - // final rs_CanCool() checks: EER vs SEER and cap82 vs cap95 vs cap115 - rc |= rs_CkFCooling2(); - } - - if (IsAusz( RSYS_CAP95)) - { rs_auszC.az_active = TRUE; - rs_fxCapCAsF = 1.2f; // working oversize factor - // ensures sufficient capacity - // during autosize search - } - - if (rs_CanHeat()) - { - // rsFanPwrH (W/cfm): error if >5, warn if >2 - rc |= limitCheck(RSYS_FANPWRH, 0., 5., 0., 2.); - - if (rs_IsHP()) - { - rc |= rs_CkFCd(rsmHEAT); - - if (IsAusz(RSYS_CAPH) || IsAusz(RSYS_CAP47) || IsAusz(RSYS_CAPAUXH)) - { // rs_capH for WSHP, rs_cap47 for ASHP - rs_auszH.az_active = TRUE; // ASHP autosizes rs_capH - // capacities derived in rs_AuszFinal - rs_fxCapHAsF = 1.2f; // working oversize factor - } - - if (rs_IsASHP()) - { - rc |= rs_CkFRatio(RSYS_CAP17, RSYS_CAP47, RSYS_CAPRAT1747, .2f, 1.2f); - rc |= rs_CkFRatio(RSYS_CAP05, RSYS_CAP47, RSYS_CAPRAT0547, .1f, 1.2f); - } - } - else if (IsAusz(RSYS_CAPH)) - { // other checking? - rs_auszH.az_active = TRUE; - rs_fxCapHAsF = 1.4f; - } - } - - // loop all zones served by this RSYS - rs_areaServed = 0.; - rs_zonesServed = 0; - ZNR* zp; - RLUPC( ZrB, zp, rs_IsZoneServed( zp)) - { rs_areaServed += zp->i.znArea; - rs_zonesServed++; - } - - return rc; -} // RSYS::rs_TopRSys1 +RC RSYS::rs_TopRSys1() // check RSYS, initial set up for run +{ + RC rc = RCOK; + + if (!IsSet(RSYS_TDDESH)) + rs_tdDesH = rs_IsHP() ? 30.f // lower default temp rise for ASHP + : 50.f; // (changed later for CHDHW) + + if (rs_IsASHPPkgRoom()) { + if (!IsSet(RSYS_ASHPLOCKOUTT)) + rs_ASHPLockOutT = 45.f; // pkg room ASHP: use resistance at lower temps + rc |= rs_SetupASHP(); + } + + if (rs_IsVC()) { // VCHP default motor type is BPM (others are PSC from cult) + if (!IsSet(RSYS_FAN + FAN_MOTTY)) + rs_fan.fn_motTy = C_MOTTYCH_BPM; + } + + if (rs_CanCool()) { // cooling model air flow correlations have limited + // validity range + // verify air flow 150 - 550 cfm/ton (per Proctor Engineering) + rc |= limitCheck(RSYS_VFPERTON, 150., 550.); + + // rsFanPwrC (W/cfm): error if >5, warn if >2 + rc |= limitCheck(RSYS_FANPWRC, 0., 5., 0., 2.); + + if (rs_IsFanCoil()) { // nothing to check? + + } else { // compression cooling + + rc |= rs_CkFCd(rsmCOOL); + + if (rs_IsWSHP()) { + rc |= limitCheck(RSYS_CAPRATCH, 0.3, 2.); + + } else { // default/harmonize 95 F COP + // inter-default rs_COP95 <-> rs_EER95 + // if both input, values can be different + // rs_COP95 used for VCHP2, rs_EER95 used for single speed + // EER can default from SEER + if (rs_IsPkgRoom()) { // pkg: derive SEER from EER + // abram conant fit, 6-20 + rs_SEER = 1.07f * rs_EER95; + // rs_COP95 not allowed + } else if (IsSet(RSYS_EER95)) { + if (!IsSet(RSYS_COP95)) + rs_COP95 = rs_EER95 / BtuperWh; + } else { + if (IsSet(RSYS_COP95)) + rs_EER95 = rs_COP95 * BtuperWh; + else if (!rs_IsVCClg()) { // estimate missing EER from SEER + // California ACM method + rs_EER95 = rs_SEER < 13.f ? 10.f + 0.84f * (rs_SEER - 11.5f) + : rs_SEER < 16.f ? 11.3f + 0.57f * (rs_SEER - 13.f) + : 13.f; + rs_COP95 = rs_EER95 / BtuperWh; + } + } + } + } + + // final rs_CanCool() checks: EER vs SEER and cap82 vs cap95 vs cap115 + rc |= rs_CkFCooling2(); + } + + if (IsAusz(RSYS_CAP95)) { + rs_auszC.az_active = TRUE; + rs_fxCapCAsF = 1.2f; // working oversize factor + // ensures sufficient capacity + // during autosize search + } + + if (rs_CanHeat()) { + // rsFanPwrH (W/cfm): error if >5, warn if >2 + rc |= limitCheck(RSYS_FANPWRH, 0., 5., 0., 2.); + + if (rs_IsHP()) { + rc |= rs_CkFCd(rsmHEAT); + + if (IsAusz(RSYS_CAPH) || IsAusz(RSYS_CAP47) || + IsAusz(RSYS_CAPAUXH)) { // rs_capH for WSHP, rs_cap47 for ASHP + rs_auszH.az_active = TRUE; // ASHP autosizes rs_capH + // capacities derived in rs_AuszFinal + rs_fxCapHAsF = 1.2f; // working oversize factor + } + + if (rs_IsASHP()) { + rc |= rs_CkFRatio(RSYS_CAP17, RSYS_CAP47, RSYS_CAPRAT1747, .2f, 1.2f); + rc |= rs_CkFRatio(RSYS_CAP05, RSYS_CAP47, RSYS_CAPRAT0547, .1f, 1.2f); + } + } else if (IsAusz(RSYS_CAPH)) { // other checking? + rs_auszH.az_active = TRUE; + rs_fxCapHAsF = 1.4f; + } + } + + // loop all zones served by this RSYS + rs_areaServed = 0.; + rs_zonesServed = 0; + ZNR *zp; + RLUPC(ZrB, zp, rs_IsZoneServed(zp)) { + rs_areaServed += zp->i.znArea; + rs_zonesServed++; + } + + return rc; +} // RSYS::rs_TopRSys1 //----------------------------------------------------------------------------- -RC RSYS::rs_TopRSys2() // final set up for run -{ - RC rc = RCOK; - - rs_SetWorkingPtrs(); // MTR and other inter-object pointers - - DUCTSEG* ds; - memset( rs_ducts, 0, sizeof( rs_ducts)); - RLUP( DsR, ds) - { ds->ds_SetRunConstants(); - if (ds->ownTi == ss) - { // if duct segment part of this system - int iSR = !ds->ds_IsSupply(); // 1=return 0=supply - if (!rs_CanHaveDucts( 0) && !rs_CanHaveDucts( 1)) - { ZNR* zpx = ds->ds_GetExZone(); - if (zpx && ds->ds_exArea > 0.f) - oWarn( "DUCTSEG '%s' is unused (rsDSEH and rsDSEC both given)\n" - " but its surface area is included in ZONE '%s'", - ds->Name(), zpx->Name()); - else - oInfo( "DUCTSEG '%s' is unused (rsDSEH and rsDSEC both given)\n" - " and has no effect", - ds->Name()); - } - else for (int iHC=0; iHC<2; iHC++) - { int iDS = rs_Dsi( iSR, iHC); - if ( iDS > 0) - { rc |= oer( "DUCTSEG trouble -- more than one %s duct", - iSR ? "return" : "supply"); - break; - } - if (rs_CanHaveDucts( iHC)) - rs_ducts[ iHC].dsi[ iSR] = ds->ss; - } - } - } - - // combined heat / DHW - if (rs_IsCHDHW()) - rc |= rs_SetupCHDHW(); - - if (rc == RCOK) - rs_SetRunConstants(); - - return rc; -} // RSYS::rs_TopRSys2 +RC RSYS::rs_TopRSys2() // final set up for run +{ + RC rc = RCOK; + + rs_SetWorkingPtrs(); // MTR and other inter-object pointers + + DUCTSEG *ds; + memset(rs_ducts, 0, sizeof(rs_ducts)); + RLUP(DsR, ds) { + ds->ds_SetRunConstants(); + if (ds->ownTi == ss) { // if duct segment part of this system + int iSR = !ds->ds_IsSupply(); // 1=return 0=supply + if (!rs_CanHaveDucts(0) && !rs_CanHaveDucts(1)) { + ZNR *zpx = ds->ds_GetExZone(); + if (zpx && ds->ds_exArea > 0.f) + oWarn("DUCTSEG '%s' is unused (rsDSEH and rsDSEC both given)\n" + " but its surface area is included in ZONE '%s'", + ds->Name(), zpx->Name()); + else + oInfo("DUCTSEG '%s' is unused (rsDSEH and rsDSEC both given)\n" + " and has no effect", + ds->Name()); + } else + for (int iHC = 0; iHC < 2; iHC++) { + int iDS = rs_Dsi(iSR, iHC); + if (iDS > 0) { + rc |= oer("DUCTSEG trouble -- more than one %s duct", + iSR ? "return" : "supply"); + break; + } + if (rs_CanHaveDucts(iHC)) + rs_ducts[iHC].dsi[iSR] = ds->ss; + } + } + } + + // combined heat / DHW + if (rs_IsCHDHW()) + rc |= rs_SetupCHDHW(); + + if (rc == RCOK) + rs_SetRunConstants(); + + return rc; +} // RSYS::rs_TopRSys2 //----------------------------------------------------------------------------- -RC RSYS::rs_FazInit( // init before autosize (once) and main sim - int isAusz) // TRUE = autosize, FALSE = main simulation +RC RSYS::rs_FazInit( // init before autosize (once) and main sim + int isAusz) // TRUE = autosize, FALSE = main simulation { - RC rc = RCOK; + RC rc = RCOK; - // autosizing init - // set handy local flag indicating autosizing now underway - rs_isAuszH = rs_auszH.az_fazInit(&rs_capH, FALSE, this, RSYS_CAPH, isAusz, "RSYS[%s] capH"); - rs_isAuszC = rs_auszC.az_fazInit(&rs_cap95, FALSE, this, RSYS_CAP95, isAusz, "RSYS[%s] cap95"); + // autosizing init + // set handy local flag indicating autosizing now underway + rs_isAuszH = rs_auszH.az_fazInit(&rs_capH, FALSE, this, RSYS_CAPH, isAusz, + "RSYS[%s] capH"); + rs_isAuszC = rs_auszC.az_fazInit(&rs_cap95, FALSE, this, RSYS_CAP95, isAusz, + "RSYS[%s] cap95"); - if (!rs_isAuszH) - rc |= rs_SetupCapH(); + if (!rs_isAuszH) + rc |= rs_SetupCapH(); - if (!rs_isAuszC) - rc |= rs_SetupCapC(); + if (!rs_isAuszC) + rc |= rs_SetupCapC(); - return rc; -} // RSYS::rs_FazInit + return rc; +} // RSYS::rs_FazInit //----------------------------------------------------------------------------- -RC RSYS::rs_RddInit( int isAusz) // init before each autosize design day and main sim +RC RSYS::rs_RddInit( + int isAusz) // init before each autosize design day and main sim { - isAusz; - return RCOK; -} // RSYS::rs_RddInit + isAusz; + return RCOK; +} // RSYS::rs_RddInit //----------------------------------------------------------------------------- -void RSYS::rs_RddiInit() // init before each autosize design day ITERATION -{ -// TODO: redundant calls occur and must be harmless! - - int auszMode = rs_IsAutoSizing(); - if (auszMode == rsmHEAT) // if autosizing something - { if (Top.tp_pass1A) - { // pass1A: warmup with fixed air flow - float cfmPerFt2 = rs_IsHP() ? 0.6f : 0.4f; - rs_SetupCapH( rs_areaServed * cfmPerFt2); // rs_capH derived from AVF - } - else - { // pass1B: rs_capH has latest value - if (Top.tp_auszDsDayItr < 2) - rs_capH = rs_auszH.az_a; // 1st iteration cap may be 0 (see rs_pass1AtoB) - rs_SetupCapH(); - } - } - else if (auszMode == rsmCOOL) - { if (Top.tp_pass1A) - { // pass1A: warmup with fixed air flow - constexpr float cfmPerFt2 = 0.6f; - rs_amfC = AVFtoAMF( rs_areaServed * cfmPerFt2); - rs_SetupCapC( rs_areaServed * cfmPerFt2); // rs_cap95 derived from AVF - } - else - { // pass1B: rs_cap95 has latest value - if (Top.tp_auszDsDayItr < 2) - rs_cap95 = rs_auszC.az_a; // 1st iteration cap may be 0 (see rs_pass1AtoB) - rs_SetupCapC(); - } - } -} // RSYS::rs_RddiInit +void RSYS::rs_RddiInit() // init before each autosize design day ITERATION +{ + // TODO: redundant calls occur and must be harmless! + + int auszMode = rs_IsAutoSizing(); + if (auszMode == rsmHEAT) // if autosizing something + { + if (Top.tp_pass1A) { // pass1A: warmup with fixed air flow + float cfmPerFt2 = rs_IsHP() ? 0.6f : 0.4f; + rs_SetupCapH(rs_areaServed * cfmPerFt2); // rs_capH derived from AVF + } else { // pass1B: rs_capH has latest value + if (Top.tp_auszDsDayItr < 2) + rs_capH = + rs_auszH.az_a; // 1st iteration cap may be 0 (see rs_pass1AtoB) + rs_SetupCapH(); + } + } else if (auszMode == rsmCOOL) { + if (Top.tp_pass1A) { // pass1A: warmup with fixed air flow + constexpr float cfmPerFt2 = 0.6f; + rs_amfC = AVFtoAMF(rs_areaServed * cfmPerFt2); + rs_SetupCapC(rs_areaServed * cfmPerFt2); // rs_cap95 derived from AVF + } else { // pass1B: rs_cap95 has latest value + if (Top.tp_auszDsDayItr < 2) + rs_cap95 = + rs_auszC.az_a; // 1st iteration cap may be 0 (see rs_pass1AtoB) + rs_SetupCapC(); + } + } +} // RSYS::rs_RddiInit //----------------------------------------------------------------------------- -RC RSYS::rs_endP1DsdIter( // autosizing end of day - int auszMode) // rsmHEAT, rsmCOOL -{ - RC rc = RCOK; - if (auszMode == rsmHEAT) - { // note: rs_fxCapHDay = 20 if no load - float f = rs_fxCapHAsF / max( rs_fxCapHDay, .01f); - if (Top.tp_pass1A) - { // pass1A (warmup): system always has fixed size - // after 1st iter, change rs_capH at will (used only for convergence test) - // rs_capH re-inited in rs_RddiInit - if (Top.tp_auszDsDayItr > 1) - rs_capH *= f; - } - else - { // pass1B: rs_capH sizes system - // change in small steps (can be unstable) - rs_capH *= bracket( 0.9f, f, 1.1f); - - // never allow smaller than 1000 Btuh or any prior pass1B result - if (rs_capH < 1000.f) - rs_capH = 1000.f; - else if (rs_capH < rs_auszH.az_b) - rs_capH = rs_auszH.az_b; - } - - setToMax( rs_auszH.ldPk, rs_capH); // peak - - rs_amfH = rs_AMFForHtgCap( rs_capH); // consistent AMF - } - else if (auszMode == rsmCOOL) - { // note: rs_fxCapCDay = 20 if no load - float f = rs_fxCapCAsF / max( rs_fxCapCDay, .01f); - if (Top.tp_pass1A) - { // pass1A (warmup): system always has fixed size - // after 1st iter, change rs_cap95 at will (used only for convergence test) - // rs_cap95 re-inited in rs_RddiInit - if (Top.tp_auszDsDayItr > 1) - rs_cap95 *= f; - } - else - { // pass1B: rs_cap95 sizes system - // change in small steps (can be unstable) - // when f is close to 1, move very slowly - // err = 0.01 -> fX = .001 - // err = 0.02 -> fX = .004 - // err = 0.1 -> fX = .1 - float err = f - 1.f; - float errX = err * fabs( err) * 10.f; - float fX = bracket( .9f, errX+1.f, 1.1f); - rs_cap95 *= fX; - - float cap95min = max(rs_auszC.az_b, 1200.f); - if (rs_cap95 < cap95min) - rs_cap95 = cap95min; // never < 0.1 ton - // never < previous design day peak - } - - setToMax( rs_auszC.ldPk, rs_cap95); // peak - - rs_amfC = rs_AMFForClgCap( rs_cap95); // consistent AMF - } - return rc; - -} // RSYS::rs_endP1DsdIter +RC RSYS::rs_endP1DsdIter( // autosizing end of day + int auszMode) // rsmHEAT, rsmCOOL +{ + RC rc = RCOK; + if (auszMode == rsmHEAT) { // note: rs_fxCapHDay = 20 if no load + float f = rs_fxCapHAsF / max(rs_fxCapHDay, .01f); + if (Top.tp_pass1A) { // pass1A (warmup): system always has fixed size + // after 1st iter, change rs_capH at will (used only for convergence + // test) rs_capH re-inited in rs_RddiInit + if (Top.tp_auszDsDayItr > 1) + rs_capH *= f; + } else { // pass1B: rs_capH sizes system + // change in small steps (can be unstable) + rs_capH *= bracket(0.9f, f, 1.1f); + + // never allow smaller than 1000 Btuh or any prior pass1B result + if (rs_capH < 1000.f) + rs_capH = 1000.f; + else if (rs_capH < rs_auszH.az_b) + rs_capH = rs_auszH.az_b; + } + + setToMax(rs_auszH.ldPk, rs_capH); // peak + + rs_amfH = rs_AMFForHtgCap(rs_capH); // consistent AMF + } else if (auszMode == rsmCOOL) { // note: rs_fxCapCDay = 20 if no load + float f = rs_fxCapCAsF / max(rs_fxCapCDay, .01f); + if (Top.tp_pass1A) { // pass1A (warmup): system always has fixed size + // after 1st iter, change rs_cap95 at will (used only for convergence + // test) rs_cap95 re-inited in rs_RddiInit + if (Top.tp_auszDsDayItr > 1) + rs_cap95 *= f; + } else { // pass1B: rs_cap95 sizes system + // change in small steps (can be unstable) + // when f is close to 1, move very slowly + // err = 0.01 -> fX = .001 + // err = 0.02 -> fX = .004 + // err = 0.1 -> fX = .1 + float err = f - 1.f; + float errX = err * fabs(err) * 10.f; + float fX = bracket(.9f, errX + 1.f, 1.1f); + rs_cap95 *= fX; + + float cap95min = max(rs_auszC.az_b, 1200.f); + if (rs_cap95 < cap95min) + rs_cap95 = cap95min; // never < 0.1 ton + // never < previous design day peak + } + + setToMax(rs_auszC.ldPk, rs_cap95); // peak + + rs_amfC = rs_AMFForClgCap(rs_cap95); // consistent AMF + } + return rc; + +} // RSYS::rs_endP1DsdIter //----------------------------------------------------------------------------- -RC RSYS::rs_pass1AtoB() // call at transition from autoSize pass 1 part A to part B for each design day +RC RSYS::rs_pass1AtoB() // call at transition from autoSize pass 1 part A to + // part B for each design day -// called between iterations of a design day (between tp_SimDays()'s without reInit) -// at the change from const-temp size-finding open-ended models to the real models. +// called between iterations of a design day (between tp_SimDays()'s without +// reInit) at the change from const-temp size-finding open-ended models to the +// real models. { - // at entry, value is set to max converged part A value (.a). + // at entry, value is set to max converged part A value (.a). - // here do any optimizations to make value a better estimate of rated size. + // here do any optimizations to make value a better estimate of rated size. - // on return, caller will increase value to any larger converged part B value (.b) - // then call RSYS::begP1b, next. + // on return, caller will increase value to any larger converged part B value + // (.b) then call RSYS::begP1b, next. - int auszMode = rs_IsAutoSizing(); - if (auszMode == rsmCOOL) - rs_cap95 = 0.f; - else - rs_capH = 0.f; + int auszMode = rs_IsAutoSizing(); + if (auszMode == rsmCOOL) + rs_cap95 = 0.f; + else + rs_capH = 0.f; - return RCOK; -} // RSYS::rs_pass1AtoB + return RCOK; +} // RSYS::rs_pass1AtoB //----------------------------------------------------------------------------------------------------------------------------- -RC RSYS::rs_begP1b() // called b4 start of pass 1 part b iterations for des day, after value set to max part B value seen. +RC RSYS::rs_begP1b() // called b4 start of pass 1 part b iterations for des day, + // after value set to max part B value seen. { -// reSetup models being autoSized, cuz AUSZ stuff and/or rs_pass1AtoB above may have changed values. +// reSetup models being autoSized, cuz AUSZ stuff and/or rs_pass1AtoB above may +// have changed values. #if 0 x RC rc = rs_SetupSizes(); // just above x return rc; #else - return RCOK; + return RCOK; #endif -} // RSYS::rs_begP1b +} // RSYS::rs_begP1b //----------------------------------------------------------------------------- -RC RSYS::rs_endAutosize() // call at end successful autoSize for possible additional checks/messages +RC RSYS::rs_endAutosize() // call at end successful autoSize for possible + // additional checks/messages { - return RCOK; -} // rs_endAutosize + return RCOK; +} // rs_endAutosize //----------------------------------------------------------------------------- -void RSYS::rs_AuszFinal() // called at end of successful autosize (after all xx_endAutosize) +void RSYS::rs_AuszFinal() // called at end of successful autosize (after all + // xx_endAutosize) { - RSYS* rsi = RSiB.GetAtSafe( ss); - - // member function sets _As, _AsNov, and input record members for each AUSZ's 'x'. - - if (rs_isAuszC) - rs_cap95 = rs_fxCapCTarg * rs_auszC.ldPkAs1 / rs_fxCapCAsF; - // max of design days w/o working oversize factor - // apply user oversize factor - - if (rs_isAuszH) - { float capHBase = rs_auszH.ldPkAs1 / rs_fxCapHAsF; // max of all design days - // remove working cap factor - - rs_capH = capHBase * rs_fxCapHTarg; // apply user's oversize factor - - if (rs_IsASHP()) - { if (IsAusz( RSYS_CAP47)) - { // find cap47 that produces required output - // (re-derives dependent values) - rs_speedF = 1.f; // force full speed (used in e.g. rs_CapEffASHP()) - rs_SizeHtASHP( rs_capH, Top.heatDsTDbO); -#if defined( _DEBUG) - // capacity consistency check - float capCheck = rs_CapEffASHP(Top.heatDsTDbO); - float capExpected = Top.heatDsTDbO >= rs_ASHPLockOutT - ? rs_capH - : rs_fanHeatH; // fan heat only when lockout - if (frDiff(capCheck, capExpected, 1.f) > .001f) - oWarn("ASHP heating capacity (%.1f) autosize mismatch (expected %.1f)", - capCheck, capExpected); -#endif - } - } + RSYS *rsi = RSiB.GetAtSafe(ss); - if (IsAusz(RSYS_CAPAUXH)) - { // ASHP aux heat autosize cap = full load w/ user oversize - rs_capAuxH = capHBase * rs_fxCapAuxHTarg; - } - } - - // re rs_capAuxH NOT autosized - // restore input value - // WHY: HP autosize process modifies - if (!IsAusz(RSYS_CAPAUXH)) - rs_capAuxH = rs_capAuxHInp; // value as input - rsi->rs_capAuxH = rs_capAuxH; // copy back to input record - - if (rs_IsASHP()) - { if (rs_isAuszC) - { if (IsAusz( RSYS_CAP47)) - { // both autosized - ASHPConsistentCaps( rs_cap95, rs_cap47, IsSet(RSYS_CAPRAT9547), rs_capRat9547); - } - // else leave rs_cap95 autosized, rs_cap47 as input - } - rs_SetupASHP(); // default other params as needed - rsi->rs_cap47 = rs_cap47; // copy back to input record WHY? + // member function sets _As, _AsNov, and input record members for each AUSZ's + // 'x'. - } - else if (rs_IsWSHP()) - { if (rs_isAuszC) - { - if (IsAusz(RSYS_CAPH)) - { // both autosized - WSHPPerf.whp_ConsistentCaps(rs_cap95, rs_capH, IsSet(RSYS_CAPRATCH), rs_capRatCH); - } - // else leave rs_cap95 autosized, rs_capH as input - } + if (rs_isAuszC) + rs_cap95 = rs_fxCapCTarg * rs_auszC.ldPkAs1 / rs_fxCapCAsF; + // max of design days w/o working oversize factor + // apply user oversize factor - rs_SetupWSHP(); // default other params as needed + if (rs_isAuszH) { + float capHBase = + rs_auszH.ldPkAs1 / rs_fxCapHAsF; // max of all design days + // remove working cap factor - } + rs_capH = capHBase * rs_fxCapHTarg; // apply user's oversize factor - // all capacities now known - if (rs_isAuszC) - { float coolOver = 1.f; // no coiling oversize (rs_fxCapC included in process) - rs_auszC.az_final( this, rsi, coolOver); - } - if (rs_isAuszH) - { float heatOver = 1.f; // ditto heating - rs_auszH.az_final( this, rsi, heatOver); - } + if (rs_IsASHP()) { + if (IsAusz(RSYS_CAP47)) { // find cap47 that produces required output + // (re-derives dependent values) + rs_speedF = 1.f; // force full speed (used in e.g. rs_CapEffASHP()) + rs_SizeHtASHP(rs_capH, Top.heatDsTDbO); +#if defined(_DEBUG) + // capacity consistency check + float capCheck = rs_CapEffASHP(Top.heatDsTDbO); + float capExpected = Top.heatDsTDbO >= rs_ASHPLockOutT + ? rs_capH + : rs_fanHeatH; // fan heat only when lockout + if (frDiff(capCheck, capExpected, 1.f) > .001f) + oWarn( + "ASHP heating capacity (%.1f) autosize mismatch (expected %.1f)", + capCheck, capExpected); +#endif + } + } + + if (IsAusz(RSYS_CAPAUXH)) { // ASHP aux heat autosize cap = full load w/ + // user oversize + rs_capAuxH = capHBase * rs_fxCapAuxHTarg; + } + } + + // re rs_capAuxH NOT autosized + // restore input value + // WHY: HP autosize process modifies + if (!IsAusz(RSYS_CAPAUXH)) + rs_capAuxH = rs_capAuxHInp; // value as input + rsi->rs_capAuxH = rs_capAuxH; // copy back to input record + + if (rs_IsASHP()) { + if (rs_isAuszC) { + if (IsAusz(RSYS_CAP47)) { // both autosized + ASHPConsistentCaps(rs_cap95, rs_cap47, IsSet(RSYS_CAPRAT9547), + rs_capRat9547); + } + // else leave rs_cap95 autosized, rs_cap47 as input + } + rs_SetupASHP(); // default other params as needed + rsi->rs_cap47 = rs_cap47; // copy back to input record WHY? + + } else if (rs_IsWSHP()) { + if (rs_isAuszC) { + if (IsAusz(RSYS_CAPH)) { // both autosized + WSHPPerf.whp_ConsistentCaps(rs_cap95, rs_capH, IsSet(RSYS_CAPRATCH), + rs_capRatCH); + } + // else leave rs_cap95 autosized, rs_capH as input + } + + rs_SetupWSHP(); // default other params as needed + } + + // all capacities now known + if (rs_isAuszC) { + float coolOver = 1.f; // no coiling oversize (rs_fxCapC included in process) + rs_auszC.az_final(this, rsi, coolOver); + } + if (rs_isAuszH) { + float heatOver = 1.f; // ditto heating + rs_auszH.az_final(this, rsi, heatOver); + } #if 0 x NO -- leave to allow reporting of load at design temp @@ -2870,220 +2868,212 @@ x if (rs_IsASHP()) x rsi->rs_capH = rs_capH = 0.f; #endif - rs_DefaultCapNomsIf(); // capture possible nominal cap change(s) - // no calc effect - // insurance: no rs_auszX.final() changes expected - // (coolOver = heatOver = 1) + rs_DefaultCapNomsIf(); // capture possible nominal cap change(s) + // no calc effect + // insurance: no rs_auszX.final() changes expected + // (coolOver = heatOver = 1) -} // RSYS::rs_AuszFinal +} // RSYS::rs_AuszFinal //----------------------------------------------------------------------------- -float RSYS::rs_ClgCapNomTons( // nominal cooling capacity - float nearest /*=-1.f*/) // if >0, round to nearest specified increment - // e.g. 0.5 = round to nearest .5 ton +float RSYS::rs_ClgCapNomTons( // nominal cooling capacity + float nearest /*=-1.f*/) // if >0, round to nearest specified increment + // e.g. 0.5 = round to nearest .5 ton // returns capacity, tons (1 ton = 12000 Btuh) { - float capNomTons = fabs( rs_cap95)/12000.f; - if (nearest > 0.f) - capNomTons = nearest*floor( 0.5f + capNomTons / nearest); - return capNomTons; -} // RSYS::rs_ClgCapNomTons + float capNomTons = fabs(rs_cap95) / 12000.f; + if (nearest > 0.f) + capNomTons = nearest * floor(0.5f + capNomTons / nearest); + return capNomTons; +} // RSYS::rs_ClgCapNomTons //----------------------------------------------------------------------------- -float RSYS::rs_ClgCapForAMF( - float amf) const // air mass flow rate, lbm/hr +float RSYS::rs_ClgCapForAMF(float amf) const // air mass flow rate, lbm/hr // returns rated total capacity at 95F, Btuh -{ float avf = AMFtoAVF( amf); // vol flow, cfm std air - float cap95 = 12000.f * avf / rs_vfPerTon; - return cap95; -} // RSYS::rs_ClgCapForAMF +{ + float avf = AMFtoAVF(amf); // vol flow, cfm std air + float cap95 = 12000.f * avf / rs_vfPerTon; + return cap95; +} // RSYS::rs_ClgCapForAMF //----------------------------------------------------------------------------- float RSYS::rs_AMFForClgCap( - float cap95) const // total cooling capacity at 95F, Btuh + float cap95) const // total cooling capacity at 95F, Btuh // returns air mass flow, lbm/hr { - float avf = cap95 * rs_vfPerTon / 12000.f; - float amf = AVFtoAMF( avf); - return amf; -} // RSYS::rs_AMFForClgCap + float avf = cap95 * rs_vfPerTon / 12000.f; + float amf = AVFtoAMF(avf); + return amf; +} // RSYS::rs_AMFForClgCap //----------------------------------------------------------------------------- -float RSYS::rs_HtgCapForAMF( - float amf) const // air mass flow rate, lbm/hr +float RSYS::rs_HtgCapForAMF(float amf) const // air mass flow rate, lbm/hr // returns rated heating capacity, Btuh { - float capH = amf * rs_tdDesH * Top.tp_airSH; - return capH; -} // RSYS::rs_ClgCapForAMF + float capH = amf * rs_tdDesH * Top.tp_airSH; + return capH; +} // RSYS::rs_ClgCapForAMF //----------------------------------------------------------------------------- -float RSYS::rs_AMFForHtgCap( - float capH) const // heating capacity +float RSYS::rs_AMFForHtgCap(float capH) const // heating capacity // returns air mass flow, lbm/hr { - float amf = capH / (rs_tdDesH * Top.tp_airSH); - return amf; -} // RSYS::rs_AMFForHtgCap + float amf = capH / (rs_tdDesH * Top.tp_airSH); + return amf; +} // RSYS::rs_AMFForHtgCap //----------------------------------------------------------------------------- -int RSYS::rs_CanHaveDucts( - int iHC) const // 0=htg, 1=clg +int RSYS::rs_CanHaveDucts(int iHC) const // 0=htg, 1=clg // return nz iff { - int ret = !IsSet( iHC ? RSYS_DSEC : RSYS_DSEH); - return ret; -} // RSYS::rs_CanHaveDucts + int ret = !IsSet(iHC ? RSYS_DSEC : RSYS_DSEH); + return ret; +} // RSYS::rs_CanHaveDucts //----------------------------------------------------------------------------- -ZNR* RSYS::rs_GetOAVReliefZn() const // get relief zone for OAV +ZNR *RSYS::rs_GetOAVReliefZn() const // get relief zone for OAV // returns pointer to ZNR where this RSYS discards zone return air // NULL if RSYS cannot OAV -{ ZNR* zp = rs_CanOAV() - ? ZrB.GetAtSafe( rs_OAVReliefZi) - : NULL; - return zp; -} // RSYS::rs_GetOAVReliefZn -//----------------------------------------------------------------------------- -void RSYS::rs_OAVSetup() // 1 time setup for OAV { - if (rs_OAVType == C_RSYSOAVTYCH_NONE) - { // insurance - rs_OAVAvfDs = rs_OAVFanPwr = 0.f; - } - else - { - [[maybe_unused]] ZNR* zp = rs_GetOAVReliefZn(); - if (rs_OAVType == C_RSYSOAVTYCH_VARFLOW) - { // variable flow (aka "NightBreeze") - - // rs_OAVAvfDs is required - - if (!IsSet( RSYS_OAVFANPWR)) - { // W/cfm per DEG Eq 1 (updated 11-20-2013) - // curve is good only to 1600 cfm - float avfX = bracket( 1.f, rs_OAVAvfDs, 1600.f); - rs_OAVFanPwr = 44.616f * pow( 1.00175684f, avfX) / avfX; - } - } - else - { // fixed flow (aka "SmartVent") - - // rs_OAVAvfDs is required - - if (!IsSet( RSYS_OAVFANPWR)) - rs_OAVFanPwr = rs_fanPwrC; // same as cooling - } - } -} // RSYS::rs_OAVSetup + ZNR *zp = rs_CanOAV() ? ZrB.GetAtSafe(rs_OAVReliefZi) : NULL; + return zp; +} // RSYS::rs_GetOAVReliefZn +//----------------------------------------------------------------------------- +void RSYS::rs_OAVSetup() // 1 time setup for OAV +{ + if (rs_OAVType == C_RSYSOAVTYCH_NONE) { // insurance + rs_OAVAvfDs = rs_OAVFanPwr = 0.f; + } else { + [[maybe_unused]] ZNR *zp = rs_GetOAVReliefZn(); + if (rs_OAVType == + C_RSYSOAVTYCH_VARFLOW) { // variable flow (aka "NightBreeze") + + // rs_OAVAvfDs is required + + if (!IsSet(RSYS_OAVFANPWR)) { // W/cfm per DEG Eq 1 (updated 11-20-2013) + // curve is good only to 1600 cfm + float avfX = bracket(1.f, rs_OAVAvfDs, 1600.f); + rs_OAVFanPwr = 44.616f * pow(1.00175684f, avfX) / avfX; + } + } else { // fixed flow (aka "SmartVent") + + // rs_OAVAvfDs is required + + if (!IsSet(RSYS_OAVFANPWR)) + rs_OAVFanPwr = rs_fanPwrC; // same as cooling + } + } +} // RSYS::rs_OAVSetup //----------------------------------------------------------------------------- -void RSYS::rs_OAVAirFlow() // OAV air flow calcs +void RSYS::rs_OAVAirFlow() // OAV air flow calcs // call at beg of each day // sets rs_OAVAvfD and rs_OAVFanPwrD { - rs_avfOAV = rs_fanHeatOAV = 0.f; - - if (rs_OAVType == C_RSYSOAVTYCH_VARFLOW) - { // NightBreeze algorithm, reference = - // "Method for Developing Fan Energy Use For a Variable - // Speed Ventilation Cooling Title 24 Measure" - // Davis Energy Group - // 11/20/2013 "Updated" version - // Air flow and fan power based on prior days average tDb - // use double due to potentially huge exp( x) - float afUnlim = float( 1./pow( 1.+exp( 653.-8.152*Wthr.d.wd_taDbAvg), 0.010654)); - float af = max( rs_OAVAvfMinF, afUnlim); - rs_avfOAV = af * rs_OAVAvfDs; // cfm - rs_fanHeatOAV = rs_OAVAvfDs * rs_OAVFanPwr * pow( af, 2.767f) * 3.413f; -#if defined( _DEBUG) - // 9-29-2010 model for comparison - float tMax = Wthr.d.wd_taDbPvPk; - [[maybe_unused]] float afOld = 0.f; - if (tMax > .0000001f) - { double d = 17.91554 - 3.67538*log( tMax); - afOld = 1.f/max( 1.f, float( d)); - } - // float d = af - afOld; - if (rs_fanHeatOAV > 4000.f) - printf( "Excess OAV fan heat\n"); -#endif - } - else if (rs_OAVType == C_RSYSOAVTYCH_FIXEDFLOW) - { rs_avfOAV = rs_OAVAvfDs; - rs_fanHeatOAV = rs_avfOAV * rs_OAVFanPwr * 3.413f; // fan power, Btuh - } - rs_amfOAV = AVFtoAMF( rs_avfOAV); // mass flow - - // else rs_OAVAvfD = rs_OAVFanPwrD = 0.f from above -} // RSYS::rs_OAVAirFlow + rs_avfOAV = rs_fanHeatOAV = 0.f; + + if (rs_OAVType == + C_RSYSOAVTYCH_VARFLOW) { // NightBreeze algorithm, reference = + // "Method for Developing Fan Energy Use For a Variable + // Speed Ventilation Cooling Title 24 Measure" + // Davis Energy Group + // 11/20/2013 "Updated" version + // Air flow and fan power based on prior days average tDb + // use double due to potentially huge exp( x) + float afUnlim = + float(1. / pow(1. + exp(653. - 8.152 * Wthr.d.wd_taDbAvg), 0.010654)); + float af = max(rs_OAVAvfMinF, afUnlim); + rs_avfOAV = af * rs_OAVAvfDs; // cfm + rs_fanHeatOAV = rs_OAVAvfDs * rs_OAVFanPwr * pow(af, 2.767f) * 3.413f; +#if defined(_DEBUG) + // 9-29-2010 model for comparison + float tMax = Wthr.d.wd_taDbPvPk; + [[maybe_unused]] float afOld = 0.f; + if (tMax > .0000001f) { + double d = 17.91554 - 3.67538 * log(tMax); + afOld = 1.f / max(1.f, float(d)); + } + // float d = af - afOld; + if (rs_fanHeatOAV > 4000.f) + printf("Excess OAV fan heat\n"); +#endif + } else if (rs_OAVType == C_RSYSOAVTYCH_FIXEDFLOW) { + rs_avfOAV = rs_OAVAvfDs; + rs_fanHeatOAV = rs_avfOAV * rs_OAVFanPwr * 3.413f; // fan power, Btuh + } + rs_amfOAV = AVFtoAMF(rs_avfOAV); // mass flow + + // else rs_OAVAvfD = rs_OAVFanPwrD = 0.f from above +} // RSYS::rs_OAVAirFlow //----------------------------------------------------------------------------- int RSYS::rs_OAVAttempt() // returns // -1 not possible { - if (rs_SupplyAirState( RSYS::rsmOAV) != 3) - { rs_mode = rsmOFF; - return -1; // OAV not available - } - - double tSup = rs_asSup.as_tdb; - - ZNR* zp; - int okOAV = 0; - RLUPC( ZrB, zp, rs_IsZoneServed( zp)) - { okOAV += zp->zn_OAVAttempt( tSup); - if (okOAV < 0) - break; - } - if (okOAV > 0) - { // at least 1 zone wants OAV - // find zone air temp with available air - // OAV not OK if any zone above zn_tzspC - double fSize = rs_amfReq[ 0] > 0. // fraction of total air that is avaailable - ? min( rs_amf / rs_amfReq[ 0], 1.) - : 0.; - RLUPC( ZrB, zp, rs_IsZoneServed( zp)) - { // set zn_rsAmfSup, zn_rsAmfRet, and zn_rsFSize - zp->zn_SetRSYSAmf( fSize, 0); - double mCp = zp->zn_rsAmfSup*Top.tp_airSH; - double tzx = zp->zn_TAirCR( mCp*tSup, mCp, 0.); - if (tzx > zp->zn_tzspC /* && RSYS can cool */) - { // OAV cannot hold tz below cooling set point, disable - okOAV = 0; - break; - } - } - } - - if (okOAV <= 0) - { // OAV not OK, revert any partial results - RLUPC( ZrB, zp, rs_IsZoneServed( zp)) - { zp->zn_rsAmfSup = zp->zn_rsAmfRet = 0.; - zp->zn_rsFSize = 0.; - // zp->tz at floating temp - zp->zn_tzsp = 0.f; - zp->zn_rsAmfSysReq[ 0] = 0.f; - ZnresB[ zp->ss].curr.S.nShVentH = 0; // vent heating not possible if no vent - } - rs_mode = rsmOFF; - return -1; - } - - // OAV can hold acceptable temp in all zones - // leave rs_mode as rsmOAV - RLUPC( ZrB, zp, rs_IsZoneServed( zp)) - { zp->zn_hcMode = rsmOAV; // say this zone has been successfully OAVed - // most further this-step HVAC modelling is skipped - if (ZnresB[ zp->ss].curr.S.nShVentH) - { const int WARNMAX = 20; - int warnCount = ZnresB[ zp->ss].zr_GetAllIntervalTotal( ZNRES_IVL_SUB_NSHVENTH); - if (warnCount <= WARNMAX) - warn( "Zone '%s', %s: unhelpful vent heating (supply temp = %0.2f)%s", - zp->Name(), Top.When( C_IVLCH_S), tSup, - warnCount == WARNMAX - ? "\n Suppressing further vent heating messages for this zone" : ""); - } - } - - return 0; -} // RSYS::rs_OAVAttempt + if (rs_SupplyAirState(RSYS::rsmOAV) != 3) { + rs_mode = rsmOFF; + return -1; // OAV not available + } + + double tSup = rs_asSup.as_tdb; + + ZNR *zp; + int okOAV = 0; + RLUPC(ZrB, zp, rs_IsZoneServed(zp)) { + okOAV += zp->zn_OAVAttempt(tSup); + if (okOAV < 0) + break; + } + if (okOAV > 0) { // at least 1 zone wants OAV + // find zone air temp with available air + // OAV not OK if any zone above zn_tzspC + double fSize = rs_amfReq[0] > 0. // fraction of total air that is avaailable + ? min(rs_amf / rs_amfReq[0], 1.) + : 0.; + RLUPC(ZrB, zp, + rs_IsZoneServed(zp)) { // set zn_rsAmfSup, zn_rsAmfRet, and zn_rsFSize + zp->zn_SetRSYSAmf(fSize, 0); + double mCp = zp->zn_rsAmfSup * Top.tp_airSH; + double tzx = zp->zn_TAirCR(mCp * tSup, mCp, 0.); + if (tzx > + zp->zn_tzspC /* && RSYS can cool */) { // OAV cannot hold tz below + // cooling set point, disable + okOAV = 0; + break; + } + } + } + + if (okOAV <= 0) { // OAV not OK, revert any partial results + RLUPC(ZrB, zp, rs_IsZoneServed(zp)) { + zp->zn_rsAmfSup = zp->zn_rsAmfRet = 0.; + zp->zn_rsFSize = 0.; + // zp->tz at floating temp + zp->zn_tzsp = 0.f; + zp->zn_rsAmfSysReq[0] = 0.f; + ZnresB[zp->ss].curr.S.nShVentH = + 0; // vent heating not possible if no vent + } + rs_mode = rsmOFF; + return -1; + } + + // OAV can hold acceptable temp in all zones + // leave rs_mode as rsmOAV + RLUPC(ZrB, zp, rs_IsZoneServed(zp)) { + zp->zn_hcMode = rsmOAV; // say this zone has been successfully OAVed + // most further this-step HVAC modelling is skipped + if (ZnresB[zp->ss].curr.S.nShVentH) { + const int WARNMAX = 20; + int warnCount = + ZnresB[zp->ss].zr_GetAllIntervalTotal(ZNRES_IVL_SUB_NSHVENTH); + if (warnCount <= WARNMAX) + warn("Zone '%s', %s: unhelpful vent heating (supply temp = %0.2f)%s", + zp->Name(), Top.When(C_IVLCH_S), tSup, + warnCount == WARNMAX + ? "\n Suppressing further vent heating messages for this zone" + : ""); + } + } + + return 0; +} // RSYS::rs_OAVAttempt //----------------------------------------------------------------------------- int ZNR::zn_OAVAttempt( - double tSup) // available supply air temp (at register), F + double tSup) // available supply air temp (at register), F // sets zn_tzsp and zn_rsAmfSysReq[ 0] @@ -3092,253 +3082,250 @@ int ZNR::zn_OAVAttempt( // 0 no OAV required, zone temp OK // 1 OAV useful { - zn_rsAmfSysReq[ 0] = 0.; // insurance: no air requested + zn_rsAmfSysReq[0] = 0.; // insurance: no air requested - if (tz < zn_tzspH /* && RSYS could heat*/) - return -9999; // zone needs heat, don't OAV + if (tz < zn_tzspH /* && RSYS could heat*/) + return -9999; // zone needs heat, don't OAV - RSYS* rs = zn_GetRSYS(); -#if 1 // spec change, 10-9-2013 - zn_tzsp = max( zn_tzspD, rs->rs_OAVTdbInlet + rs->rs_OAVTdiff); + RSYS *rs = zn_GetRSYS(); +#if 1 // spec change, 10-9-2013 + zn_tzsp = max(zn_tzspD, rs->rs_OAVTdbInlet + rs->rs_OAVTdiff); #else -x zn_tzsp = max( zn_tzspD, tSup + rs->rs_OAVTdiff); -#endif - if (zn_tzsp > zn_tzspC /*&& RSYS could cool*/) - return -9999; // no amount of OAV will prevent cooling - if (tz < zn_tzsp) - return 0; // no OAV required - - // dry air mass flow to hold at set point - double amfOAV; - if (tSup >= zn_tzsp) - { // OAV causes heating, run full volume -#if defined( _DEBUG) - if (tSup > 100.) - printf( "Excessive OAV supply temp %0.1f\n", tSup); -#endif - ZnresB[ ss].curr.S.nShVentH = 1; - amfOAV = DBL_MAX; - } - else - amfOAV = zn_AmfHvacCR( zn_tzsp, tSup); - zn_rsAmfSysReq[ 0] = rs->rs_ZoneAirRequest( amfOAV, 0); + x zn_tzsp = max(zn_tzspD, tSup + rs->rs_OAVTdiff); +#endif + if (zn_tzsp > zn_tzspC /*&& RSYS could cool*/) + return -9999; // no amount of OAV will prevent cooling + if (tz < zn_tzsp) + return 0; // no OAV required + + // dry air mass flow to hold at set point + double amfOAV; + if (tSup >= zn_tzsp) { // OAV causes heating, run full volume +#if defined(_DEBUG) + if (tSup > 100.) + printf("Excessive OAV supply temp %0.1f\n", tSup); +#endif + ZnresB[ss].curr.S.nShVentH = 1; + amfOAV = DBL_MAX; + } else + amfOAV = zn_AmfHvacCR(zn_tzsp, tSup); + zn_rsAmfSysReq[0] = rs->rs_ZoneAirRequest(amfOAV, 0); - return 1; // OAV is possible + return 1; // OAV is possible -} // ZNR::zn_OAVAttempt +} // ZNR::zn_OAVAttempt //----------------------------------------------------------------------------- void RSYS::rs_SetRunConstants() // set model vals that do not vary during simulation // *and* do not depend on (possibly) autosized capacities // redundant calls OK { - // temp rise due to cooling fan, F - // constant even if capacity is changed (e.g. during autosize) - // (cuz air flow is proportional to capacity via rs_vfPerTon) - rs_fanDeltaTC = rs_fanPwrC * 3.413f / (60.f * .075f * Top.tp_airSH); - - rs_speedFMin = 1.f; // altered iff variable speed - - // duct non-leak fractions - // Note: never 0! - for (int iHC=0; iHC<2; iHC++) - for (int iSR=0; iSR<2; iSR++) - { int iDS = rs_Dsi( iSR, iHC); - rs_ducts[ iHC].ductLkXF[ iSR] = iDS > 0 - ? 1.f - min( DsR[ iDS].ds_leakF, .99f) - : 1.f; - } + // temp rise due to cooling fan, F + // constant even if capacity is changed (e.g. during autosize) + // (cuz air flow is proportional to capacity via rs_vfPerTon) + rs_fanDeltaTC = rs_fanPwrC * 3.413f / (60.f * .075f * Top.tp_airSH); + + rs_speedFMin = 1.f; // altered iff variable speed + + // duct non-leak fractions + // Note: never 0! + for (int iHC = 0; iHC < 2; iHC++) + for (int iSR = 0; iSR < 2; iSR++) { + int iDS = rs_Dsi(iSR, iHC); + rs_ducts[iHC].ductLkXF[iSR] = + iDS > 0 ? 1.f - min(DsR[iDS].ds_leakF, .99f) : 1.f; + } - rs_OAVSetup(); + rs_OAVSetup(); -} // rs_SetRunConstants +} // rs_SetRunConstants //----------------------------------------------------------------------------- -void RSYS::rs_SetWorkingPtrs() // set runtime pointers to meters etc. +void RSYS::rs_SetWorkingPtrs() // set runtime pointers to meters etc. // WHY: simplifies runtime code { - rs_pMtrElec = MtrB.GetAtSafe( rs_elecMtri); // elec mtr or NULL - rs_pMtrFuel = MtrB.GetAtSafe( rs_fuelMtri); // fuel mtr or NULL - rs_pMtrHeat = rs_IsElecHeat() ? rs_pMtrElec : rs_pMtrFuel; // heat mtr or NULL - rs_pMtrAux = rs_IsFuelAuxH() ? rs_pMtrFuel : rs_pMtrElec; - rs_pLoadMtr[ 0] = LdMtrR.GetAtSafe(rs_loadMtri); - rs_pLoadMtr[ 1] = LdMtrR.GetAtSafe(rs_htgLoadMtri); - rs_pLoadMtr[ 2] = LdMtrR.GetAtSafe(rs_clgLoadMtri); - rs_pSrcSideLoadMtr[ 0] = LdMtrR.GetAtSafe(rs_srcSideLoadMtri); - rs_pSrcSideLoadMtr[ 1] = LdMtrR.GetAtSafe(rs_htgSrcSideLoadMtri); - rs_pSrcSideLoadMtr[ 2] = LdMtrR.GetAtSafe(rs_clgSrcSideLoadMtri); - rs_pCHDHWSYS = WsR.GetAtSafe(rs_CHDHWSYSi); -} // RSYS::rs_SetMTRPtrs + rs_pMtrElec = MtrB.GetAtSafe(rs_elecMtri); // elec mtr or NULL + rs_pMtrFuel = MtrB.GetAtSafe(rs_fuelMtri); // fuel mtr or NULL + rs_pMtrHeat = rs_IsElecHeat() ? rs_pMtrElec : rs_pMtrFuel; // heat mtr or NULL + rs_pMtrAux = rs_IsFuelAuxH() ? rs_pMtrFuel : rs_pMtrElec; + rs_pLoadMtr[0] = LdMtrR.GetAtSafe(rs_loadMtri); + rs_pLoadMtr[1] = LdMtrR.GetAtSafe(rs_htgLoadMtri); + rs_pLoadMtr[2] = LdMtrR.GetAtSafe(rs_clgLoadMtri); + rs_pSrcSideLoadMtr[0] = LdMtrR.GetAtSafe(rs_srcSideLoadMtri); + rs_pSrcSideLoadMtr[1] = LdMtrR.GetAtSafe(rs_htgSrcSideLoadMtri); + rs_pSrcSideLoadMtr[2] = LdMtrR.GetAtSafe(rs_clgSrcSideLoadMtri); + rs_pCHDHWSYS = WsR.GetAtSafe(rs_CHDHWSYSi); +} // RSYS::rs_SetMTRPtrs //----------------------------------------------------------------------------- -RC RSYS::rs_SetupSizes( // derive capacity-dependent values - BOOL bAlways /*=TRUE*/) // TRUE: always - // FALSE: iff autosize +RC RSYS::rs_SetupSizes( // derive capacity-dependent values + BOOL bAlways /*=TRUE*/) // TRUE: always + // FALSE: iff autosize // CAUTION: only call is with bAlways=TRUE // Review re autosize if used elsewhere 12-2012 { - RC rc = RCOK; - if (bAlways || rs_isAuszH) - rc |= rs_SetupCapH(); - if (bAlways || rs_isAuszC) - rc |= rs_SetupCapC(); - return rc; -} // RSYS::rs_SetupSizes + RC rc = RCOK; + if (bAlways || rs_isAuszH) + rc |= rs_SetupCapH(); + if (bAlways || rs_isAuszC) + rc |= rs_SetupCapC(); + return rc; +} // RSYS::rs_SetupSizes //----------------------------------------------------------------------------- -RC RSYS::rs_SetupCapH( // set heating members that do not vary during simulation - float avfH /*=-1*/, // heating AVF, cfm std air if known - // else derived from rs_capH - int options /*=0*/) // option bits - // 1: assume not autosize (ignore Top.tp_autoSizing) +RC RSYS::rs_SetupCapH( // set heating members that do not vary during simulation + float avfH /*=-1*/, // heating AVF, cfm std air if known + // else derived from rs_capH + int options /*=0*/) // option bits + // 1: assume not autosize (ignore Top.tp_autoSizing) // returns RCOK iff success { - RC rc = RCOK; - if (avfH > 0.f) - { rs_amfH = AVFtoAMF( avfH); - rs_capH = rs_amfH * Top.tp_airSH * rs_tdDesH; - } - else - { float nomCap; - if (rs_IsASHP()) - { if (Top.tp_autoSizing && !(options&1)) - { nomCap = rs_capH; // ASHP autosize derived from rs_capH - } - else - { rc |= rs_SetupASHP(); // set default capacities/efficiencies, derive constants - // multiple calls OK (e.g. during autosizing) - // final call needed after autosize complete - nomCap = rs_cap47; - } - } - else if (rs_IsWSHP()) - { - rc |= rs_SetupWSHP(); // set default capacities/efficiencies, derive constants - // multiple calls OK (e.g. during autosizing) - // final call needed after autosize complete - nomCap = rs_capH; - } - else if (rs_IsCHDHW()) - { - nomCap = rs_capH; - - } - else - { // non-HP, non-CHDHW - nomCap = rs_capH; - if (!rs_CanHaveAuxHeat()) - rs_capAuxH = 0.f; // insurance - } - rs_amfH = nomCap / (rs_tdDesH * Top.tp_airSH); // nominal full speed dry-air mass flow rate, lb/hr - avfH = AMFtoAVF( rs_amfH); - } - rs_fanHeatH = avfH * rs_fanPwrH * BtuperWh; - - rs_DefaultCapNomsIf(); // update nominal capacities (no calc effect) - - return rc; - -} // RSYS::rs_SetupCapH + RC rc = RCOK; + if (avfH > 0.f) { + rs_amfH = AVFtoAMF(avfH); + rs_capH = rs_amfH * Top.tp_airSH * rs_tdDesH; + } else { + float nomCap; + if (rs_IsASHP()) { + if (Top.tp_autoSizing && !(options & 1)) { + nomCap = rs_capH; // ASHP autosize derived from rs_capH + } else { + rc |= rs_SetupASHP(); // set default capacities/efficiencies, derive + // constants + // multiple calls OK (e.g. during autosizing) + // final call needed after autosize complete + nomCap = rs_cap47; + } + } else if (rs_IsWSHP()) { + rc |= rs_SetupWSHP(); // set default capacities/efficiencies, derive + // constants + // multiple calls OK (e.g. during autosizing) + // final call needed after autosize complete + nomCap = rs_capH; + } else if (rs_IsCHDHW()) { + nomCap = rs_capH; + + } else { // non-HP, non-CHDHW + nomCap = rs_capH; + if (!rs_CanHaveAuxHeat()) + rs_capAuxH = 0.f; // insurance + } + rs_amfH = + nomCap / + (rs_tdDesH * + Top.tp_airSH); // nominal full speed dry-air mass flow rate, lb/hr + avfH = AMFtoAVF(rs_amfH); + } + rs_fanHeatH = avfH * rs_fanPwrH * BtuperWh; + + rs_DefaultCapNomsIf(); // update nominal capacities (no calc effect) + + return rc; + +} // RSYS::rs_SetupCapH //----------------------------------------------------------------------------- -float RSYS::rs_FanHRtdPerTon( // fan heat included in ratings - float capNomTons) // total cooling or heating capacity, tons +float RSYS::rs_FanHRtdPerTon( // fan heat included in ratings + float capNomTons) // total cooling or heating capacity, tons // returns fan heat included in ratings, Btuh { - float fanHRtdPerTon = - rs_IsPkgRoom() || rs_adjForFanHt == C_NOYESCH_NO - ? 0.f // PkgRoom or user override: no fan adjustment - : rs_fan.fn_motTy == C_MOTTYCH_PSC ? 500.f // PSC = Permanent Split Capacitor - // use .365 W/cfm - // .365 W/cfm * 400 cfm/ton * 3.412 Btuh/W = 498 Btuh - - // else BPM = Brushless Permanent Magnet (aka ECM) - : rs_IsWSHP() ? 212.f // WSHP: rated with 0 external static, assume .156 W/cfm - : 283.f; // BPM other: representative value from Abram Conant + float fanHRtdPerTon = + rs_IsPkgRoom() || rs_adjForFanHt == C_NOYESCH_NO + ? 0.f // PkgRoom or user override: no fan adjustment + : rs_fan.fn_motTy == C_MOTTYCH_PSC + ? 500.f // PSC = Permanent Split Capacitor + // use .365 W/cfm + // .365 W/cfm * 400 cfm/ton * 3.412 Btuh/W = 498 Btuh + + // else BPM = Brushless Permanent Magnet (aka ECM) + : rs_IsWSHP() + ? 212.f // WSHP: rated with 0 external static, assume .156 W/cfm + : 283.f; // BPM other: representative value from Abram Conant - return capNomTons * fanHRtdPerTon; + return capNomTons * fanHRtdPerTon; -} // RSYS::rs_FanHRtdPerTon +} // RSYS::rs_FanHRtdPerTon //----------------------------------------------------------------------------- -void RSYS::rs_SetupFanC( // derive fan cooling fan info - float avfC /*=-1.f*/) // cooling AVF, cfm std air if known - // else derived from rs_cap95 +void RSYS::rs_SetupFanC( // derive fan cooling fan info + float avfC /*=-1.f*/) // cooling AVF, cfm std air if known + // else derived from rs_cap95 // sets rs_amfC, rs_fanHeatC, rs_fanRtdC // also can set rs_cap95 (if avfC > 0) { - float capNomTons; - if (avfC > 0.f) - { rs_amfC = AVFtoAMF( avfC); - rs_cap95 = rs_ClgCapForAMF( rs_amfC); - capNomTons = rs_ClgCapNomTons(); - } - else - { capNomTons = rs_ClgCapNomTons(); - avfC = rs_vfPerTon * capNomTons; // avf (standard air) - rs_amfC = AVFtoAMF( avfC); // amf using standard air density, lbm/hr - } - - rs_fanHeatC = avfC * rs_fanPwrC * BtuperWh; // full speed operating fan power, Btuh - - rs_fanHRtdC = rs_FanHRtdPerTon( capNomTons); // fan heat included in rated capacity, Btuh - // rs_fanHRtdH is derived independently - -} // RSYS::rs_SetupFanC + float capNomTons; + if (avfC > 0.f) { + rs_amfC = AVFtoAMF(avfC); + rs_cap95 = rs_ClgCapForAMF(rs_amfC); + capNomTons = rs_ClgCapNomTons(); + } else { + capNomTons = rs_ClgCapNomTons(); + avfC = rs_vfPerTon * capNomTons; // avf (standard air) + rs_amfC = AVFtoAMF(avfC); // amf using standard air density, lbm/hr + } + + rs_fanHeatC = + avfC * rs_fanPwrC * BtuperWh; // full speed operating fan power, Btuh + + rs_fanHRtdC = + rs_FanHRtdPerTon(capNomTons); // fan heat included in rated capacity, Btuh + // rs_fanHRtdH is derived independently + +} // RSYS::rs_SetupFanC //----------------------------------------------------------------------------- -RC RSYS::rs_SetupCapC( // derive constants that depend on capacity - float avfC /*=-1*/, // air flow if known, passed to rs_SetupFanC - [[maybe_unused]] int options /*=0*/) // option bits TBD +RC RSYS::rs_SetupCapC( // derive constants that depend on capacity + float avfC /*=-1*/, // air flow if known, passed to rs_SetupFanC + [[maybe_unused]] int options /*=0*/) // option bits TBD // sets RSYS cooling members that do not vary during simulation { - RC rc = RCOK; + RC rc = RCOK; - rs_SetupFanC( avfC); // sets rs_cap95 if avfC > 0., else sets rs_amfC - // derives rs_fanHRtdC - - // rs_cap95 now known = net total cooling cap at 95 F (> 0) - - // notes re capacity manipulations - // load = capSensT = capnf*shr - qFanOp - // capnf = (cap95 + qfanRat)*fChg*fSize*fCondCap - // let F = fChg*fCondCap*shr - // load = (cap95 + qFanRat)*F - qFanOp - // load = (cap95*(1 + fanRatX))*F - cap95*fanOpX - // load = cap95*(F*(1+fanRatX)-fanOpX - // cap95 = (load + fanOpX)/(F*(1 + fanRatX)) - - // rs_capnfX used by several models - float cap95nf = -(fabs(rs_cap95) + rs_fanHRtdC); // coil (gross) total capacity at 95 F, Btuh - // (< 0) - rs_capnfX = min( cap95nf * rs_fChg, -10.f); // apply charge adjustment - - if (rs_Is1Spd()) - { - // base value for SEERnf and EERnf calculations - // used only for single speed - float inpX = 1.09f * rs_cap95 / rs_SEER - rs_fanHRtdC / 3.413f; // input power, W - rs_SEERnfX = inpX > 0.f ? rs_fChg * (1.09f * rs_cap95 + rs_fanHRtdC) / inpX - : rs_SEER; - - inpX = rs_cap95 / rs_EER95 - rs_fanHRtdC / 3.413f; - rs_EERnfX = inpX > 0.f ? -rs_capnfX / inpX : rs_EER95; // gross total EER - } + rs_SetupFanC(avfC); // sets rs_cap95 if avfC > 0., else sets rs_amfC + // derives rs_fanHRtdC - if (rs_IsVCClg()) - { - // default other cooling capacities if needed - if (!IsSet(RSYS_CAP82)) - rs_cap82 = rs_capRat8295 * rs_cap95; + // rs_cap95 now known = net total cooling cap at 95 F (> 0) - if (!IsSet(RSYS_CAP115)) - rs_cap115 = rs_capRat11595 * rs_cap95; + // notes re capacity manipulations + // load = capSensT = capnf*shr - qFanOp + // capnf = (cap95 + qfanRat)*fChg*fSize*fCondCap + // let F = fChg*fCondCap*shr + // load = (cap95 + qFanRat)*F - qFanOp + // load = (cap95*(1 + fanRatX))*F - cap95*fanOpX + // load = cap95*(F*(1+fanRatX)-fanOpX + // cap95 = (load + fanOpX)/(F*(1 + fanRatX)) - // cooling performance map - // currently (2-22) used for rs_IsVC() only - rc |= rs_SetupBtwxtClg(); - } + // rs_capnfX used by several models + float cap95nf = -(fabs(rs_cap95) + + rs_fanHRtdC); // coil (gross) total capacity at 95 F, Btuh + // (< 0) + rs_capnfX = min(cap95nf * rs_fChg, -10.f); // apply charge adjustment - rs_DefaultCapNomsIf(); // update nominal capacities (no calc effect) + if (rs_Is1Spd()) { + // base value for SEERnf and EERnf calculations + // used only for single speed + float inpX = + 1.09f * rs_cap95 / rs_SEER - rs_fanHRtdC / 3.413f; // input power, W + rs_SEERnfX = inpX > 0.f ? rs_fChg * (1.09f * rs_cap95 + rs_fanHRtdC) / inpX + : rs_SEER; - return rc; + inpX = rs_cap95 / rs_EER95 - rs_fanHRtdC / 3.413f; + rs_EERnfX = inpX > 0.f ? -rs_capnfX / inpX : rs_EER95; // gross total EER + } + + if (rs_IsVCClg()) { + // default other cooling capacities if needed + if (!IsSet(RSYS_CAP82)) + rs_cap82 = rs_capRat8295 * rs_cap95; + + if (!IsSet(RSYS_CAP115)) + rs_cap115 = rs_capRat11595 * rs_cap95; -} // RSYS::rs_SetupCapC + // cooling performance map + // currently (2-22) used for rs_IsVC() only + rc |= rs_SetupBtwxtClg(); + } + + rs_DefaultCapNomsIf(); // update nominal capacities (no calc effect) + + return rc; + +} // RSYS::rs_SetupCapC //---------------------------------------------------------------------------- #if 0 RC RSYS::rs_SetDuctSizes() @@ -3349,235 +3336,231 @@ RC RSYS::rs_SetDuctSizes() { DUCTSEG* pDS = DsR[ iDS]; } } -} +} #endif //---------------------------------------------------------------------------- int RSYS::rs_IsAutoSizing() const // returns rsmOFF, rsmHEAT, or rsmCOOL { - return (Top.tp_dsDay == 1 && rs_isAuszH) ? rsmHEAT - : (Top.tp_dsDay == 2 && rs_isAuszC) ? rsmCOOL - : rsmOFF; // = 0 -} // RSYS::rs_IsAutoSizing + return (Top.tp_dsDay == 1 && rs_isAuszH) ? rsmHEAT + : (Top.tp_dsDay == 2 && rs_isAuszC) ? rsmCOOL + : rsmOFF; // = 0 +} // RSYS::rs_IsAutoSizing //---------------------------------------------------------------------------- -RC RSYS::rs_BegHour() -{ RC rc = RCOK; - - if (Top.isBegDay) - { // init capacity ratios for day - // track smallest fxCap for heating and cooling - // = largest load relative to capacity - // thus init to large value - rs_fxCapHDay = rs_fxCapCDay = 20.f; - - // testing/debugging aids - // insurance: do at sim beg so autosizing complete - if (Top.tp_isBegMainSim) - { -#if 0 && defined( _DEBUG) +RC RSYS::rs_BegHour() { + RC rc = RCOK; + + if (Top.isBegDay) { // init capacity ratios for day + // track smallest fxCap for heating and cooling + // = largest load relative to capacity + // thus init to large value + rs_fxCapHDay = rs_fxCapCDay = 20.f; + + // testing/debugging aids + // insurance: do at sim beg so autosizing complete + if (Top.tp_isBegMainSim) { +#if 0 && defined(_DEBUG) if (rs_IsASHP()) { // back-derive HSPF (self-check) rs_HSPFCheckASHP( 0x100 + 0); rs_PerfDataASHP(); // write CSV file of performance points } #endif - if (rs_perfMap == C_NOYESCH_YES) - rs_PerfMapAC(); // make performance map - } + if (rs_perfMap == C_NOYESCH_YES) + rs_PerfMapAC(); // make performance map + } - // OAV daily setup - rs_OAVAirFlow(); - } + // OAV daily setup + rs_OAVAirFlow(); + } - return rc; -} // RSYS::rs_BegHour + return rc; +} // RSYS::rs_BegHour //---------------------------------------------------------------------------- -RC RSYS::rs_BegSubhr() -{ - RC rc = RCOK; +RC RSYS::rs_BegSubhr() { + RC rc = RCOK; - rs_tSupLs = rs_asSup.as_tdb; - rs_mode = rsmOFF; // mode not yet known - rs_ClearSubhrResults(); - RsResR[ss].curr.S.rsr_Zero(); // clear associated RSYSRES results + rs_tSupLs = rs_asSup.as_tdb; + rs_mode = rsmOFF; // mode not yet known + rs_ClearSubhrResults(); + RsResR[ss].curr.S.rsr_Zero(); // clear associated RSYSRES results - rs_fxCap[ 0] = rs_fxCap[ 1] = 0.f; // excess capacity not known + rs_fxCap[0] = rs_fxCap[1] = 0.f; // excess capacity not known - if (!IsSet( RSYS_TDBOUT)) - rs_tdbOut = Top.tDbOSh; // default outdoor temp - // else set via input expression + if (!IsSet(RSYS_TDBOUT)) + rs_tdbOut = Top.tDbOSh; // default outdoor temp + // else set via input expression - if (!IsSet( RSYS_OAVTDBINLET)) - rs_OAVTdbInlet = Top.tDbOSh; - // else set via input expression + if (!IsSet(RSYS_OAVTDBINLET)) + rs_OAVTdbInlet = Top.tDbOSh; + // else set via input expression - return rc; -} // RSYS::rs_BegSubhr + return rc; +} // RSYS::rs_BegSubhr //---------------------------------------------------------------------------- -RC RSYS::rs_EndSubhr() -{ - RC rc = RCOK; - - // track excess capacity for day - // used in re autosizing - if (rs_fxCap[ 0] > 0.f) - { if (rs_mode == rsmHEAT) - { if (rs_fxCap[ 0] < rs_fxCapHDay) - rs_fxCapHDay = rs_fxCap[ 0]; - } - else if (rs_mode == rsmCOOL) - { if (rs_fxCap[ 0] < rs_fxCapCDay) - rs_fxCapCDay = rs_fxCap[ 0]; - } - } - - // populate results - // Note: MTRs and LOADMTs are cleared in ::doBegIvl() - RSYSRES_IVL_SUB& R = RsResR[ ss].curr.S; - - R.fhTot = R.fhParasitic = rs_parFuel * Top.tp_subhrDur; // assign fuel parasitics to heating - if (rs_parElec > 0.f) - { // assign electrical parasitics per current mode or last active mode - float ePar = rs_parElec * BtuperWh * Top.tp_subhrDur; - int modeX = rs_mode != rsmOFF ? rs_mode : rs_modeLastActive; - if (modeX <= rsmHEAT) - R.ehTot = R.ehParasitic = ePar; // rsmOFF and rsmHEAT -> heating - else if (modeX == rsmCOOL) - R.ecTot = R.ecParasitic = ePar; - else - R.evTot = R.evParasitic = ePar; - } - - R.hrsOn = rs_runF * Top.tp_subhrDur; - float qFan = rs_outFan * Top.tp_subhrDur; - float eFan = rs_inFan * Top.tp_subhrDur; - - if (rs_mode == rsmHEAT) - { - R.hrsOnAux = rs_runFAux * Top.tp_subhrDur; - R.qhPrimary = rs_outSen * Top.tp_subhrDur; - R.qhDefrost = rs_outDefrost * Top.tp_subhrDur; - R.qhAux = rs_outAux * Top.tp_subhrDur; - R.qhFan = qFan; - R.qhNet = R.qhPrimary + R.qhDefrost + R.qhAux + R.qhFan; - - // heating input electricity and fuel (rs_xxx values are Btuh) - (rs_IsElecHeat() ? R.ehPrimary : R.fhPrimary) = rs_inPrimary * Top.tp_subhrDur; - if (rs_IsFuelAuxH()) - { R.fhDefrost = rs_inDefrost * Top.tp_subhrDur; - R.fhAux = rs_inAux * Top.tp_subhrDur; - } - else - { R.ehDefrost = rs_inDefrost * Top.tp_subhrDur; - R.ehAux = rs_inAux * Top.tp_subhrDur; - } - R.ehFan = eFan; - R.ehTot += R.ehPrimary + R.ehDefrost + R.ehAux + R.ehFan; - /* + R.ehParasitic see above */ - R.fhTot += R.fhPrimary + R.fhDefrost + R.fhAux /* + R.fhParasitic*/; - - for (int iM = 0; iM < 2; iM++) - { // accumulate values to LOADMETERs - // [0] accums both heating and cooling - // [1] accums heating - if (rs_pLoadMtr[ iM]) // primary = coil loads - rs_pLoadMtr[ iM]->S.qHtg += R.qhPrimary; - if (rs_pSrcSideLoadMtr[ iM]) // source-side (= "outdoor coil" or environment source) - // + = from env: >0 during heating = coil heating - compressor electricity - rs_pSrcSideLoadMtr[ iM]->S.qHtg += R.qhPrimary - R.ehPrimary; - } - } - else if (rs_mode == rsmCOOL) - { - R.qcSen = rs_outSen * Top.tp_subhrDur; - R.qcLat = rs_outLat * Top.tp_subhrDur; - R.qcFan = qFan; - R.qcSenNet = R.qcSen + R.qcFan; - - R.ecPrimary = rs_inPrimary * Top.tp_subhrDur; - R.ecFan = eFan; - - R.ecTot += R.ecPrimary + R.ecFan /* + R.ecParasitic, see above */; - - for (int iM = 0; iM < 3; iM += 2) - { // accumulate values to LOADMETERs - // [0] accums both heating and cooling - // [2] accums cooling - if (rs_pLoadMtr[iM]) // primary = coil loads - rs_pLoadMtr[iM]->S.qClg += R.qcSen + R.qcLat; - if (rs_pSrcSideLoadMtr[iM]) // source-side (= "outdoor coil" or environment source) - // + = from env: <0 when cooling = total cooling + compressor electricity - rs_pSrcSideLoadMtr[iM]->S.qClg += R.qcSen + R.qcLat - R.ecPrimary; - } - } - else if (rs_mode == rsmOAV) - { // sensible "output" = fan power - R.evFan = eFan; - R.evTot += R.evFan; /* + R.evParasitic, see above */ - } - - // verify RSYSRES_IVL_SUB layout at compile time - // fixed sequence allows array access by rs_mode (see code below) - // rsmHEAT/rsmCOOL/rsmOAV definitions must be consistent with member sequences. -#define QZONECHK( m, oDif) static_assert( &(((RSYSRES_IVL_SUB *)0)->m)-&(((RSYSRES_IVL_SUB *)0)->qhZoneSen) == oDif, "Bad seq " #m) - QZONECHK(qhZoneSen, (rsmHEAT - 1) * 2); - QZONECHK(qhZoneLat, (rsmHEAT - 1) * 2 + 1); - QZONECHK(qcZoneSen, (rsmCOOL - 1) * 2); - QZONECHK(qcZoneLat, (rsmCOOL - 1) * 2 + 1); - QZONECHK(qvZoneSen, (rsmOAV - 1) * 2); - QZONECHK(qvZoneLat, (rsmOAV - 1) * 2 + 1); - static_assert(rsmCOUNT == 4, "Bad rsm enum"); +RC RSYS::rs_EndSubhr() { + RC rc = RCOK; + + // track excess capacity for day + // used in re autosizing + if (rs_fxCap[0] > 0.f) { + if (rs_mode == rsmHEAT) { + if (rs_fxCap[0] < rs_fxCapHDay) + rs_fxCapHDay = rs_fxCap[0]; + } else if (rs_mode == rsmCOOL) { + if (rs_fxCap[0] < rs_fxCapCDay) + rs_fxCapCDay = rs_fxCap[0]; + } + } + + // populate results + // Note: MTRs and LOADMTs are cleared in ::doBegIvl() + RSYSRES_IVL_SUB &R = RsResR[ss].curr.S; + + R.fhTot = R.fhParasitic = + rs_parFuel * Top.tp_subhrDur; // assign fuel parasitics to heating + if (rs_parElec > 0.f) { // assign electrical parasitics per current mode or + // last active mode + float ePar = rs_parElec * BtuperWh * Top.tp_subhrDur; + int modeX = rs_mode != rsmOFF ? rs_mode : rs_modeLastActive; + if (modeX <= rsmHEAT) + R.ehTot = R.ehParasitic = ePar; // rsmOFF and rsmHEAT -> heating + else if (modeX == rsmCOOL) + R.ecTot = R.ecParasitic = ePar; + else + R.evTot = R.evParasitic = ePar; + } + + R.hrsOn = rs_runF * Top.tp_subhrDur; + float qFan = rs_outFan * Top.tp_subhrDur; + float eFan = rs_inFan * Top.tp_subhrDur; + + if (rs_mode == rsmHEAT) { + R.hrsOnAux = rs_runFAux * Top.tp_subhrDur; + R.qhPrimary = rs_outSen * Top.tp_subhrDur; + R.qhDefrost = rs_outDefrost * Top.tp_subhrDur; + R.qhAux = rs_outAux * Top.tp_subhrDur; + R.qhFan = qFan; + R.qhNet = R.qhPrimary + R.qhDefrost + R.qhAux + R.qhFan; + + // heating input electricity and fuel (rs_xxx values are Btuh) + (rs_IsElecHeat() ? R.ehPrimary : R.fhPrimary) = + rs_inPrimary * Top.tp_subhrDur; + if (rs_IsFuelAuxH()) { + R.fhDefrost = rs_inDefrost * Top.tp_subhrDur; + R.fhAux = rs_inAux * Top.tp_subhrDur; + } else { + R.ehDefrost = rs_inDefrost * Top.tp_subhrDur; + R.ehAux = rs_inAux * Top.tp_subhrDur; + } + R.ehFan = eFan; + R.ehTot += R.ehPrimary + R.ehDefrost + R.ehAux + R.ehFan; + /* + R.ehParasitic see above */ + R.fhTot += R.fhPrimary + R.fhDefrost + R.fhAux /* + R.fhParasitic*/; + + for (int iM = 0; iM < 2; iM++) { // accumulate values to LOADMETERs + // [0] accums both heating and cooling + // [1] accums heating + if (rs_pLoadMtr[iM]) // primary = coil loads + rs_pLoadMtr[iM]->S.qHtg += R.qhPrimary; + if (rs_pSrcSideLoadMtr[iM]) // source-side (= "outdoor coil" or + // environment source) + // + = from env: >0 during heating = coil heating - compressor + // electricity + rs_pSrcSideLoadMtr[iM]->S.qHtg += R.qhPrimary - R.ehPrimary; + } + } else if (rs_mode == rsmCOOL) { + R.qcSen = rs_outSen * Top.tp_subhrDur; + R.qcLat = rs_outLat * Top.tp_subhrDur; + R.qcFan = qFan; + R.qcSenNet = R.qcSen + R.qcFan; + + R.ecPrimary = rs_inPrimary * Top.tp_subhrDur; + R.ecFan = eFan; + + R.ecTot += R.ecPrimary + R.ecFan /* + R.ecParasitic, see above */; + + for (int iM = 0; iM < 3; iM += 2) { // accumulate values to LOADMETERs + // [0] accums both heating and cooling + // [2] accums cooling + if (rs_pLoadMtr[iM]) // primary = coil loads + rs_pLoadMtr[iM]->S.qClg += R.qcSen + R.qcLat; + if (rs_pSrcSideLoadMtr[iM]) // source-side (= "outdoor coil" or + // environment source) + // + = from env: <0 when cooling = total cooling + compressor + // electricity + rs_pSrcSideLoadMtr[iM]->S.qClg += R.qcSen + R.qcLat - R.ecPrimary; + } + } else if (rs_mode == rsmOAV) { // sensible "output" = fan power + R.evFan = eFan; + R.evTot += R.evFan; /* + R.evParasitic, see above */ + } + + // verify RSYSRES_IVL_SUB layout at compile time + // fixed sequence allows array access by rs_mode (see code below) + // rsmHEAT/rsmCOOL/rsmOAV definitions must be consistent with member + // sequences. +#define QZONECHK(m, oDif) \ + static_assert(&(((RSYSRES_IVL_SUB *)0)->m) - \ + &(((RSYSRES_IVL_SUB *)0)->qhZoneSen) == \ + oDif, \ + "Bad seq " #m) + QZONECHK(qhZoneSen, (rsmHEAT - 1) * 2); + QZONECHK(qhZoneLat, (rsmHEAT - 1) * 2 + 1); + QZONECHK(qcZoneSen, (rsmCOOL - 1) * 2); + QZONECHK(qcZoneLat, (rsmCOOL - 1) * 2 + 1); + QZONECHK(qvZoneSen, (rsmOAV - 1) * 2); + QZONECHK(qvZoneLat, (rsmOAV - 1) * 2 + 1); + static_assert(rsmCOUNT == 4, "Bad rsm enum"); #undef QZONECHK - if (rs_mode != rsmOFF) - { // heat transfers from this RSYS to all zones (including duct losses) - const ZNR* zp; - int iMX = 2 * (rs_mode - 1); - RLUPC(ZrB, zp, rs_IsZoneServed(zp)) - { - (&R.qhZoneSen)[iMX] += float(zp->zn_qsHvac * Top.tp_subhrDur); - (&R.qhZoneLat)[iMX] += float(zp->zn_qlHvac * Top.tp_subhrDur); - } - } - - return rc; -} // RSYS::rs_EndSubhr + if (rs_mode != rsmOFF) { // heat transfers from this RSYS to all zones + // (including duct losses) + const ZNR *zp; + int iMX = 2 * (rs_mode - 1); + RLUPC(ZrB, zp, rs_IsZoneServed(zp)) { + (&R.qhZoneSen)[iMX] += float(zp->zn_qsHvac * Top.tp_subhrDur); + (&R.qhZoneLat)[iMX] += float(zp->zn_qlHvac * Top.tp_subhrDur); + } + } + + return rc; +} // RSYS::rs_EndSubhr //----------------------------------------------------------------------------- RC RSYS::rs_AfterSubhr() // end-subhour after-exprs/reports stuff for loads // set 'prior interval' variables etc: if done sooner, probes come out wrong. { - rs_modeLs = rs_mode; - if (rs_mode != rsmOFF) - rs_modeLastActive = rs_mode; - rs_loadFLs = rs_loadF; - return RCOK; -} // RSYS::rs_AfterSubhr + rs_modeLs = rs_mode; + if (rs_mode != rsmOFF) + rs_modeLastActive = rs_mode; + rs_loadFLs = rs_loadF; + return RCOK; +} // RSYS::rs_AfterSubhr //----------------------------------------------------------------------------- RC RSYS::rs_AfterHour() // end-hour after-exprs/reports stuff for loads // set 'prior interval' variables etc: if done sooner, probes come out wrong. { - RC rc = RCOK; + RC rc = RCOK; - if (Top.isEndDay) - { // last step of day - int auszMode = rs_IsAutoSizing(); - if (auszMode != rsmOFF) - rc = rs_endP1DsdIter( auszMode); - } + if (Top.isEndDay) { // last step of day + int auszMode = rs_IsAutoSizing(); + if (auszMode != rsmOFF) + rc = rs_endP1DsdIter(auszMode); + } - // prior interval values: none + // prior interval values: none - return rc; -} // RSYS::rs_AfterHour + return rc; +} // RSYS::rs_AfterHour //----------------------------------------------------------------------------- void RSYS::rs_HeatingOutletAirState( - int auszMode /*=rsmOff*/) // active autosizing, if any - // rsmOFF: none (normal simulation) - // rsmHEAT: heating - // rsmCOOL: do not call + int auszMode /*=rsmOff*/) // active autosizing, if any + // rsmOFF: none (normal simulation) + // rsmHEAT: heating + // rsmCOOL: do not call // derives RSYS outlet conditions (before distribution losses) for heating mode @@ -3591,104 +3574,92 @@ void RSYS::rs_HeatingOutletAirState( // (plus intermediate correlation results) { - // rs_asOut = rs_asIn; // init to entering state (by caller) - - rs_tdbCoilIn = rs_asOut.as_tdb; - - // determine current heating capacity and efficiency - - float capHt = 0.f; // current capacity, Btuh - // = rs_capHt expect for variable speed - - if (auszMode == rsmHEAT && Top.tp_pass1A) - { // autosize warmup: assume fixed temp rise - rs_capHt = capHt = rs_asOut.as_CalcQSen2(rs_asRet.as_tdb + rs_tdDesH, rs_amf); - rs_effHt = 1.f; // need nz value, else ASHP assumes compressor off - } - else if (rs_IsASHP()) - { - if (auszMode != rsmOFF) - { // ASHP heat autosize (based on rs_capH) - rs_effHt = 1.f; - rs_capHt = capHt = rs_capH; - rs_capAuxH = rs_capH; // same cap for aux during autosizing - // used below if needed - } - else - { // ASHP simulation (not autosize) - // run full model - capHt = rs_CapEffASHP2(); // sets rs_capHt and rs_EffHt - } - } - else if (rs_IsWSHP()) - { - const float airMassFlowF = 1.f; // temporary assumption - float capF, inpF; - /*rc |=*/ WSHPPerf.whp_HeatingFactors(capF, inpF, rs_tdbOut, rs_tdbCoilIn, airMassFlowF); - rs_capHt = capHt = (rs_capH - rs_fanHRtdH) * capF; // gross heating capacity - float inpX = ((rs_capH / rs_COP47) - rs_fanHRtdH) * inpF; // gross input power - rs_effHt = rs_capHt / inpX * rs_fEffH; - } - else if (rs_IsCHDHW()) - { - if (rs_capHt == 0.f) - { // do initial setup only once - rs_CurCapHtCHDHW(); - } - capHt = rs_capHt * rs_speedF; - rs_effHt = 1.f; - } - else - { // not heat pump of any type - rs_effHt = rs_IsFanCoil() ? 1.f : rs_AFUE * rs_fEffH; - rs_capHt = capHt = rs_capH; // includes fan heat - } - - // add heat to air stream - rs_asOut.as_AddQSen2( capHt, rs_amf); - - // auxiliary heat supply air state - // rs_capAuxH is known - if (rs_CanHaveAuxHeat() && rs_speedF == 1.f) - { - // double tdbWas = rs_asOutAux.as_tdb; - float fanHeatAux; - if (rs_ctrlAuxH == C_AUXHEATCTRL_CYCLE) - { // aux cycles: aux out = prim out + aux heat - // fan heat already in prim out - rs_asOutAux = rs_asOut; - fanHeatAux = 0.f; - } - else - { // aux alternate or locked out: prim off - // fan heat must be added - rs_asOutAux = rs_asIn; - fanHeatAux = rs_fanHeatH; - } - - rs_asOutAux.as_AddQSen2(rs_capAuxH + fanHeatAux, rs_amf); - rs_asSupAux = rs_asOutAux; - rs_SupplyDSEAndDucts(rs_asSupAux); - } - -#if defined( RSYS_FIXEDOAT) - rs_asSup.as_Set(120., .001); -#endif - return; -} // RSYS::rs_HeatingOutletAirState + // rs_asOut = rs_asIn; // init to entering state (by caller) + + rs_tdbCoilIn = rs_asOut.as_tdb; + + // determine current heating capacity and efficiency + + float capHt = 0.f; // current capacity, Btuh + // = rs_capHt expect for variable speed + + if (auszMode == rsmHEAT && + Top.tp_pass1A) { // autosize warmup: assume fixed temp rise + rs_capHt = capHt = + rs_asOut.as_CalcQSen2(rs_asRet.as_tdb + rs_tdDesH, rs_amf); + rs_effHt = 1.f; // need nz value, else ASHP assumes compressor off + } else if (rs_IsASHP()) { + if (auszMode != rsmOFF) { // ASHP heat autosize (based on rs_capH) + rs_effHt = 1.f; + rs_capHt = capHt = rs_capH; + rs_capAuxH = rs_capH; // same cap for aux during autosizing + // used below if needed + } else { // ASHP simulation (not autosize) + // run full model + capHt = rs_CapEffASHP2(); // sets rs_capHt and rs_EffHt + } + } else if (rs_IsWSHP()) { + const float airMassFlowF = 1.f; // temporary assumption + float capF, inpF; + /*rc |=*/WSHPPerf.whp_HeatingFactors(capF, inpF, rs_tdbOut, rs_tdbCoilIn, + airMassFlowF); + rs_capHt = capHt = (rs_capH - rs_fanHRtdH) * capF; // gross heating capacity + float inpX = + ((rs_capH / rs_COP47) - rs_fanHRtdH) * inpF; // gross input power + rs_effHt = rs_capHt / inpX * rs_fEffH; + } else if (rs_IsCHDHW()) { + if (rs_capHt == 0.f) { // do initial setup only once + rs_CurCapHtCHDHW(); + } + capHt = rs_capHt * rs_speedF; + rs_effHt = 1.f; + } else { // not heat pump of any type + rs_effHt = rs_IsFanCoil() ? 1.f : rs_AFUE * rs_fEffH; + rs_capHt = capHt = rs_capH; // includes fan heat + } + + // add heat to air stream + rs_asOut.as_AddQSen2(capHt, rs_amf); + + // auxiliary heat supply air state + // rs_capAuxH is known + if (rs_CanHaveAuxHeat() && rs_speedF == 1.f) { + // double tdbWas = rs_asOutAux.as_tdb; + float fanHeatAux; + if (rs_ctrlAuxH == + C_AUXHEATCTRL_CYCLE) { // aux cycles: aux out = prim out + aux heat + // fan heat already in prim out + rs_asOutAux = rs_asOut; + fanHeatAux = 0.f; + } else { // aux alternate or locked out: prim off + // fan heat must be added + rs_asOutAux = rs_asIn; + fanHeatAux = rs_fanHeatH; + } + + rs_asOutAux.as_AddQSen2(rs_capAuxH + fanHeatAux, rs_amf); + rs_asSupAux = rs_asOutAux; + rs_SupplyDSEAndDucts(rs_asSupAux); + } + +#if defined(RSYS_FIXEDOAT) + rs_asSup.as_Set(120., .001); +#endif + return; +} // RSYS::rs_HeatingOutletAirState //----------------------------------------------------------------------------- -void RSYS::rs_CoolingSHR() // derive cooling sensible heat ratio +void RSYS::rs_CoolingSHR() // derive cooling sensible heat ratio // call: rs_tdbCoilIn = coil entering dry bulb, F // rs_twbCoilIn = coil entering wet bulb, F // rs_tdbOut = outdoor drybulb seen by condenser, F // rs_vfPerTon = indoor std air cfm / ton // return: rs_SHR = SHR under current conditions { -#if 0 && defined( _DEBUG) +#if 0 && defined(_DEBUG) x if (!Top.isWarmup) x printf("Hit\n"); #endif -#if 0 && defined( _DEBUG) +#if 0 && defined(_DEBUG) if (!Top.isWarmup) { if (fabs(rs_asIn.as_tdb - 80.) < .2 @@ -3697,169 +3668,164 @@ x printf("Hit\n"); printf("Hit\n"); } #endif -#if 0 && defined( _DEBUG) +#if 0 && defined(_DEBUG) rs_tdbOut = 95.f; rs_tdbCoilIn = 80.f; rs_twbCoilIn = 67.f; rs_vfPerTon = 400.f; #endif - rs_SHR = CoolingSHR(rs_tdbOut, rs_tdbCoilIn, rs_twbCoilIn, rs_vfPerTon); + rs_SHR = CoolingSHR(rs_tdbOut, rs_tdbCoilIn, rs_twbCoilIn, rs_vfPerTon); -} // RSYS::rs_CoolingSHR +} // RSYS::rs_CoolingSHR //----------------------------------------------------------------------------- -static float CoolingCapF1Spd( // capacity factor for 1 spd model - float SHR, // sensible heat ratio for current conditions - float tdbOut, // outdoor dry bulb, F - float tdbCoilIn, // coil entering dry bulb, F - float twbCoilIn, // coil entering wet bulb, F - float vfPerTon) // coil air flow std air cfm / ton +static float CoolingCapF1Spd( // capacity factor for 1 spd model + float SHR, // sensible heat ratio for current conditions + float tdbOut, // outdoor dry bulb, F + float tdbCoilIn, // coil entering dry bulb, F + float twbCoilIn, // coil entering wet bulb, F + float vfPerTon) // coil air flow std air cfm / ton // return: fCondCap = current conditions factor for total gross capacity { - float fCondCap; - if (SHR > 0.9999f) - fCondCap = // dry coil - 0.009483100f * tdbCoilIn - // + 0.f * twbCoilIn - - 0.000600600f * tdbOut - - 0.000148900f * vfPerTon - - 0.000032600f * tdbCoilIn * tdbOut - + 0.000011900f * tdbCoilIn * vfPerTon - // + 0.f * twbCoilIn * tdbOut - // + 0.f * twbCoilIn * vfPerTon - - 0.000005050f * tdbOut * vfPerTon - // + 0.f * twbCoilIn * twbCoilIn - - 52.561740000f / vfPerTon - + 0.430751600f; - else - fCondCap = // wet coil - // 0.f * * tdbCoilIn - + 0.009645900f * twbCoilIn - + 0.002536900f * tdbOut - + 0.000171500f * vfPerTon - // + 0.f * tdbCoilIn* tdbOut - // + 0.f * tdbCoilIn* vfPerTon - - 0.000095900f * twbCoilIn * tdbOut - + 0.000008180f * twbCoilIn * vfPerTon - - 0.000007550f * tdbOut * vfPerTon - + 0.000105700f * twbCoilIn * twbCoilIn - - 53.542300000f / vfPerTon - + 0.381567150f; - - return fCondCap; - -} // CoolingCapF1Spd + float fCondCap; + if (SHR > 0.9999f) + fCondCap = // dry coil + 0.009483100f * tdbCoilIn + // + 0.f * twbCoilIn + - 0.000600600f * tdbOut - 0.000148900f * vfPerTon - + 0.000032600f * tdbCoilIn * tdbOut + + 0.000011900f * tdbCoilIn * vfPerTon + // + 0.f * twbCoilIn * tdbOut + // + 0.f * twbCoilIn * vfPerTon + - 0.000005050f * tdbOut * vfPerTon + // + 0.f * twbCoilIn * twbCoilIn + - 52.561740000f / vfPerTon + 0.430751600f; + else + fCondCap = // wet coil + // 0.f * * tdbCoilIn + +0.009645900f * twbCoilIn + 0.002536900f * tdbOut + + 0.000171500f * vfPerTon + // + 0.f * tdbCoilIn* tdbOut + // + 0.f * tdbCoilIn* vfPerTon + - 0.000095900f * twbCoilIn * tdbOut + + 0.000008180f * twbCoilIn * vfPerTon - 0.000007550f * tdbOut * vfPerTon + + 0.000105700f * twbCoilIn * twbCoilIn - 53.542300000f / vfPerTon + + 0.381567150f; + + return fCondCap; + +} // CoolingCapF1Spd //----------------------------------------------------------------------------- -float RSYS::rs_CoolingCapF1Spd( // capacity factor for 1 spd model - float tdbOut /*=0.f*/) +float RSYS::rs_CoolingCapF1Spd( // capacity factor for 1 spd model + float tdbOut /*=0.f*/) // return: fCondCap = current conditions factor for total gross capacity { - if (tdbOut == 0.f) - tdbOut = rs_tdbOut; + if (tdbOut == 0.f) + tdbOut = rs_tdbOut; - rs_fCondCap = CoolingCapF1Spd(rs_SHR, tdbOut, rs_tdbCoilIn, rs_twbCoilIn, rs_vfPerTon); - return rs_fCondCap; + rs_fCondCap = + CoolingCapF1Spd(rs_SHR, tdbOut, rs_tdbCoilIn, rs_twbCoilIn, rs_vfPerTon); + return rs_fCondCap; -} // RSYS::rs_CoolingCapF1Spd +} // RSYS::rs_CoolingCapF1Spd //----------------------------------------------------------------------------- -static float CoolingInpF1Spd( // input factor for 1 spd model - float SHR, // sensible heat ratio for current conditions - float tdbOut, // outdoor dry bulb, F - float tdbCoilIn, // coil entering dry bulb, F - float twbCoilIn, // coil entering wet bulb, F - float vfPerTon, // coil air flow std air cfm / ton - float& fInpSEER) +static float CoolingInpF1Spd( // input factor for 1 spd model + float SHR, // sensible heat ratio for current conditions + float tdbOut, // outdoor dry bulb, F + float tdbCoilIn, // coil entering dry bulb, F + float twbCoilIn, // coil entering wet bulb, F + float vfPerTon, // coil air flow std air cfm / ton + float &fInpSEER) // return: fCondInp = current conditions factor for compressor input { - float fBase, fInpEER; - if (SHR > 0.9999f) - { fBase = 0.0046103f * tdbCoilIn // dry coil - // + 0.f * twbCoilIn - + 0.0125598f * tdbOut - - 0.000512f * vfPerTon - - 0.0000357f * tdbCoilIn * tdbOut - + 0.0000105f * tdbCoilIn * vfPerTon; - // + 0.f * twbCoilIn * tdbOut - // + 0.f * twbCoilIn * vfPerTon - // + 0.f * tdbOut * vfPerTon - // + 0.f * twbCoilIn * twbCoilIn - // + 0.f / vfPerTon; - fInpSEER = fBase - 0.316172311f; - fInpEER = fBase - 0.475306500f; - } - else - { - fBase = // 0.f * tdbCoilIn - - 0.0202256f * twbCoilIn - + 0.0236703f * tdbOut - - 0.0006638f * vfPerTon - // + 0.f * tdbCoilIn* tdbOut - // + 0.f * tdbCoilIn* vfPerTon - - 0.0001841f * twbCoilIn * tdbOut - + 0.0000214f * twbCoilIn * vfPerTon - - 0.00000812f * tdbOut * vfPerTon - + 0.0002971f * twbCoilIn * twbCoilIn - - 27.95672f / vfPerTon; - fInpSEER = fBase + 0.209951063f; - fInpEER = fBase + 0.015003100f; - } - - return fInpEER; -} // CoolingInpF1Spd + float fBase, fInpEER; + if (SHR > 0.9999f) { + fBase = 0.0046103f * tdbCoilIn // dry coil + // + 0.f * twbCoilIn + + 0.0125598f * tdbOut - 0.000512f * vfPerTon - + 0.0000357f * tdbCoilIn * tdbOut + 0.0000105f * tdbCoilIn * vfPerTon; + // + 0.f * twbCoilIn * tdbOut + // + 0.f * twbCoilIn * vfPerTon + // + 0.f * tdbOut * vfPerTon + // + 0.f * twbCoilIn * twbCoilIn + // + 0.f / vfPerTon; + fInpSEER = fBase - 0.316172311f; + fInpEER = fBase - 0.475306500f; + } else { + fBase = // 0.f * tdbCoilIn + -0.0202256f * twbCoilIn + 0.0236703f * tdbOut - + 0.0006638f * vfPerTon + // + 0.f * tdbCoilIn* tdbOut + // + 0.f * tdbCoilIn* vfPerTon + - 0.0001841f * twbCoilIn * tdbOut + 0.0000214f * twbCoilIn * vfPerTon - + 0.00000812f * tdbOut * vfPerTon + 0.0002971f * twbCoilIn * twbCoilIn - + 27.95672f / vfPerTon; + fInpSEER = fBase + 0.209951063f; + fInpEER = fBase + 0.015003100f; + } + + return fInpEER; +} // CoolingInpF1Spd //----------------------------------------------------------------------------- -float RSYS::rs_CoolingEff1Spd( // cooling efficiency, 1 spd model - float tdbOut /*=0.f*/) +float RSYS::rs_CoolingEff1Spd( // cooling efficiency, 1 spd model + float tdbOut /*=0.f*/) // sets rs_fCondSEER, rs_SEERnf, rs_fCondEER, rs_EERnf, rs_EERt // returns input power modifier { - if (tdbOut == 0.f) - tdbOut = rs_tdbOut; + if (tdbOut == 0.f) + tdbOut = rs_tdbOut; - float fInpSEER; - float fInpEER = CoolingInpF1Spd(rs_SHR, tdbOut, rs_tdbCoilIn, rs_twbCoilIn, rs_vfPerTon, fInpSEER); + float fInpSEER; + float fInpEER = CoolingInpF1Spd(rs_SHR, tdbOut, rs_tdbCoilIn, rs_twbCoilIn, + rs_vfPerTon, fInpSEER); - rs_fCondSEER = rs_fCondCap / fInpSEER; - rs_SEERnf = rs_SEERnfX * rs_fCondSEER; + rs_fCondSEER = rs_fCondCap / fInpSEER; + rs_SEERnf = rs_SEERnfX * rs_fCondSEER; - rs_fCondEER = rs_fCondCap / fInpEER; - rs_EERnf = rs_EERnfX * rs_fCondEER; + rs_fCondEER = rs_fCondCap / fInpEER; + rs_EERnf = rs_EERnfX * rs_fCondEER; - rs_EERt = tdbOut < 82.f ? rs_SEERnf - : tdbOut < 95.f ? rs_SEERnf + (tdbOut - 82.f) * (rs_EERnf - rs_SEERnf) / 13.f - : rs_EERnf; + rs_EERt = tdbOut < 82.f ? rs_SEERnf + : tdbOut < 95.f + ? rs_SEERnf + (tdbOut - 82.f) * (rs_EERnf - rs_SEERnf) / 13.f + : rs_EERnf; - return fInpEER; + return fInpEER; -} // RSYS::rs_CoolingEff1Spd +} // RSYS::rs_CoolingEff1Spd //----------------------------------------------------------------------------- -void RSYS::rs_CoolingEnteringAirFactorsVS( // adjustments for entering (indoor) air state - float& capF, // returned: capacity factor - float& inpF) const // returned: compressor input power factor +void RSYS::rs_CoolingEnteringAirFactorsVS( // adjustments for entering (indoor) + // air state + float &capF, // returned: capacity factor + float &inpF) const // returned: compressor input power factor { - float capFN = CoolingCapF1Spd(rs_SHR, rs_tdbOut, rs_tdbCoilIn, rs_twbCoilIn, rs_vfPerTon); - float capFD = CoolingCapF1Spd(0.7f, rs_tdbOut, 80.f, 67.f, 400.f); - capF = capFN / capFD; + float capFN = CoolingCapF1Spd(rs_SHR, rs_tdbOut, rs_tdbCoilIn, rs_twbCoilIn, + rs_vfPerTon); + float capFD = CoolingCapF1Spd(0.7f, rs_tdbOut, 80.f, 67.f, 400.f); + capF = capFN / capFD; - float fSink; - float inpFN = CoolingInpF1Spd(rs_SHR, rs_tdbOut, rs_tdbCoilIn, rs_twbCoilIn, rs_vfPerTon, fSink); - float inpFD = CoolingInpF1Spd(0.7f, rs_tdbOut, 80.f, 67.f, 400.f, fSink); + float fSink; + float inpFN = CoolingInpF1Spd(rs_SHR, rs_tdbOut, rs_tdbCoilIn, rs_twbCoilIn, + rs_vfPerTon, fSink); + float inpFD = CoolingInpF1Spd(0.7f, rs_tdbOut, 80.f, 67.f, 400.f, fSink); - inpF = inpFN / inpFD; + inpF = inpFN / inpFD; -} // RSYS::rs_CoolingEnteringAirFactorsVS +} // RSYS::rs_CoolingEnteringAirFactorsVS //----------------------------------------------------------------------------- -void RSYS::rs_CoolingOutletAirState( // cooling outlet (leaving) air state - int auszMode /*=rsmOff*/) // active autosizing, if any - // rsmOFF: none (normal simulation) - // rsmCOOL: cooling - // rsmHEAT: do not call +void RSYS::rs_CoolingOutletAirState( // cooling outlet (leaving) air state + int auszMode /*=rsmOff*/) // active autosizing, if any + // rsmOFF: none (normal simulation) + // rsmCOOL: cooling + // rsmHEAT: do not call -// derives RSYS outlet conditions (before supply distribution losses) for cooling mode +// derives RSYS outlet conditions (before supply distribution losses) for +// cooling mode // call: rs_asIn = entering state (return duct leaving state) // rs_asOut = rs_asIn (initialized for calc) @@ -3873,7 +3839,7 @@ void RSYS::rs_CoolingOutletAirState( // cooling outlet (leaving) air state // (plus intermediate correlation results) { -#if 0 && defined( _DEBUG) +#if 0 && defined(_DEBUG) static bool bDoneExport = false; if (!bDoneExport) { @@ -3882,120 +3848,126 @@ void RSYS::rs_CoolingOutletAirState( // cooling outlet (leaving) air state } #endif -#if 0 && defined( _DEBUG) +#if 0 && defined(_DEBUG) x if (Top.jDay == 242 && Top.iHr == 14) x printf("\nhit"); #endif - // fan heat: coil entering conditions - // rs_asOut = rs_asIn per caller - if (rs_fan.fanTy == C_FANTYCH_BLOWTHRU) - { rs_asOut.as_AddQSen(rs_speedF*rs_fanHeatC, rs_amf); - rs_twbCoilIn = rs_asOut.as_Twb(); - } - else - rs_twbCoilIn = rs_twbIn; // use known value - rs_tdbCoilIn = rs_asOut.as_tdb; + // fan heat: coil entering conditions + // rs_asOut = rs_asIn per caller + if (rs_fan.fanTy == C_FANTYCH_BLOWTHRU) { + rs_asOut.as_AddQSen(rs_speedF * rs_fanHeatC, rs_amf); + rs_twbCoilIn = rs_asOut.as_Twb(); + } else + rs_twbCoilIn = rs_twbIn; // use known value + rs_tdbCoilIn = rs_asOut.as_tdb; + + if (rs_IsFanCoil()) { + rs_SHR = rs_SHRtarget; // user input (may be expression) + rs_capnfX = rs_capTotCt = -rs_cap95; + rs_fCondCap = 1.f; + rs_capSenCt = rs_capTotCt * rs_SHR; + } else { // compression cooling + + rs_CoolingSHR(); + + // no fan (coil-only) capacities at current conditions (fan heat elsewhere) + // total, sensible + if (auszMode == rsmCOOL && + Top.tp_pass1A) { // autosizing warmup: assume coil can produce design + // delta-T + // (ignore sign of rs_tdDesC) + // why: produces (more) stable supply air state + rs_CoolingCapF1Spd(); // use 1 spd model for both 1 spd and VC + float coilDT = // temp diff across coil (< 0) + -fabs(rs_tdDesC) * rs_fCondCap - rs_fanDeltaTC; + rs_capSenCt = coilDT * rs_amf * Top.tp_airSH; + rs_capTotCt = rs_capSenCt / rs_SHR; // total, Btuh (note rs_SHR>0 always) + } else if (rs_IsWSHP()) { + const float airMassFlowF = 1.f; // temporary assumption + float capF, capSenF, inpF; + /* rc |= */ WSHPPerf.whp_CoolingFactors(capF, capSenF, inpF, rs_tdbOut, + rs_tdbCoilIn, rs_twbCoilIn, + airMassFlowF); + rs_capTotCt = + rs_capnfX * capF; // total gross capacity at current conditions, Btuh + rs_capSenCt = rs_SHRtarget * rs_capTotCt * + capSenF; // SHR set using air-to-air approach + if (rs_capSenCt > rs_capTotCt) { + rs_capSenCt = rs_capTotCt; + } + rs_SHR = rs_capSenCt / rs_capTotCt; + float inpX = ((rs_cap95 / (rs_EER95 / BtuperWh)) - rs_fanHRtdC) * inpF; + rs_effCt = -rs_capTotCt / inpX; + } else if (rs_Is1Spd()) { // 1 spd, not autosizing warmup, use full model + rs_CoolingCapF1Spd(); // use 1 spd model for both 1 spd + rs_capTotCt = + rs_capnfX * + rs_fCondCap; // total gross capacity at current conditions, Btuh + rs_capSenCt = + rs_SHR * + rs_capTotCt; // sensible gross capacity at current conditions, Btuh + + rs_CoolingEff1Spd(); // derive rs_EERt + + rs_effCt = rs_SHR * rs_fEffC * rs_EERt / + BtuperWh; // compressor efficiency (COP) + // = (Btuh sensible)/(Btuh compressor) + } else { + ASSERT(rs_IsVCClg()); + // variable capacity + // total capacities and input, Btuh + // include rated fan power + // cap values are net Btuh, <0 (w/o rs_fChg adjust) + float capClg, inpClg, capClgMin, inpClgMin; + /* RC rc = */ rs_GetPerfBtwxt(rs_pRgiClg, rs_tdbOut, capClg, inpClg, + capClgMin, inpClgMin); + + capClgMin = bracket(capClg, capClgMin, 0.05f * capClg); + inpClgMin = bracket(-capClgMin / 10.f, inpClgMin, inpClg); + + rs_speedFMin = capClg >= 0.f + ? 1.f + : capClgMin / capClg; // rs_speedFMin = min/max ratio + // rs_fChg adjustment cancels +#if defined(_DEBUG) + if (rs_speedFMin <= 0.f || rs_speedFMin > 1.f) + printf("\nBad rs_speedFMin"); +#endif - if (rs_IsFanCoil()) - { - rs_SHR = rs_SHRtarget; // user input (may be expression) - rs_capnfX = rs_capTotCt = -rs_cap95; - rs_fCondCap = 1.f; - rs_capSenCt = rs_capTotCt * rs_SHR; - } - else - { // compression cooling - - rs_CoolingSHR(); - - // no fan (coil-only) capacities at current conditions (fan heat elsewhere) - // total, sensible - if (auszMode == rsmCOOL && Top.tp_pass1A) - { // autosizing warmup: assume coil can produce design delta-T - // (ignore sign of rs_tdDesC) - // why: produces (more) stable supply air state - rs_CoolingCapF1Spd(); // use 1 spd model for both 1 spd and VC - float coilDT = // temp diff across coil (< 0) - -fabs(rs_tdDesC) * rs_fCondCap - rs_fanDeltaTC; - rs_capSenCt = coilDT * rs_amf * Top.tp_airSH; - rs_capTotCt = rs_capSenCt / rs_SHR; // total, Btuh (note rs_SHR>0 always) - } - else if (rs_IsWSHP()) - { - const float airMassFlowF = 1.f; // temporary assumption - float capF, capSenF, inpF; - /* rc |= */ WSHPPerf.whp_CoolingFactors(capF, capSenF, inpF, - rs_tdbOut, rs_tdbCoilIn, rs_twbCoilIn, airMassFlowF); - rs_capTotCt = rs_capnfX * capF; // total gross capacity at current conditions, Btuh - rs_capSenCt = rs_SHRtarget * rs_capTotCt * capSenF; // SHR set using air-to-air approach - if (rs_capSenCt > rs_capTotCt) - { - rs_capSenCt = rs_capTotCt; - } - rs_SHR = rs_capSenCt / rs_capTotCt; - float inpX = ((rs_cap95 / (rs_EER95 / BtuperWh)) - rs_fanHRtdC) * inpF; - rs_effCt = -rs_capTotCt / inpX; - } - else if (rs_Is1Spd()) - { // 1 spd, not autosizing warmup, use full model - rs_CoolingCapF1Spd(); // use 1 spd model for both 1 spd - rs_capTotCt = rs_capnfX * rs_fCondCap; // total gross capacity at current conditions, Btuh - rs_capSenCt = rs_SHR * rs_capTotCt; // sensible gross capacity at current conditions, Btuh + // use correlations to get adjusments for entering air state + float fCondInp; + rs_CoolingEnteringAirFactorsVS(rs_fCondCap, fCondInp); - rs_CoolingEff1Spd(); // derive rs_EERt + rs_capTotCt // total coil capacity (= gross) at current conditions and + // speed, Btuh + = (capClg - rs_fanHRtdC) * rs_fChg * rs_fCondCap * rs_speedF; + rs_capSenCt = rs_SHR * rs_capTotCt; // sensible coil capacity at current + // conditions and speed, Btuh - rs_effCt = rs_SHR * rs_fEffC * rs_EERt / BtuperWh; // compressor efficiency (COP) - // = (Btuh sensible)/(Btuh compressor) - } - else - { - ASSERT(rs_IsVCClg()); - // variable capacity - // total capacities and input, Btuh - // include rated fan power - // cap values are net Btuh, <0 (w/o rs_fChg adjust) - float capClg, inpClg, capClgMin, inpClgMin; - /* RC rc = */ rs_GetPerfBtwxt(rs_pRgiClg, rs_tdbOut, - capClg, inpClg, capClgMin, inpClgMin); - - capClgMin = bracket(capClg, capClgMin, 0.05f * capClg); - inpClgMin = bracket(-capClgMin / 10.f, inpClgMin, inpClg); - - rs_speedFMin = capClg >= 0.f ? 1.f : capClgMin / capClg; // rs_speedFMin = min/max ratio - // rs_fChg adjustment cancels -#if defined( _DEBUG) - if (rs_speedFMin <= 0.f || rs_speedFMin > 1.f) - printf("\nBad rs_speedFMin"); -#endif - - // use correlations to get adjusments for entering air state - float fCondInp; - rs_CoolingEnteringAirFactorsVS(rs_fCondCap, fCondInp); - - rs_capTotCt // total coil capacity (= gross) at current conditions and speed, Btuh - = (capClg - rs_fanHRtdC) * rs_fChg * rs_fCondCap * rs_speedF; - rs_capSenCt = rs_SHR * rs_capTotCt; // sensible coil capacity at current conditions and speed, Btuh - - float inpX = (inpClg - rs_fanHRtdC) * fCondInp; // full speed input power at current conditions w/o fan - if (rs_speedF < 1.f) - { - // capTotMinCt = (capClgMin - rs_fanHRtdC) * rs_fChg * rs_fCondCap * rs_speedFMin; - float inpXMin = (inpClgMin - rs_fanHRtdC * rs_speedFMin) * fCondInp; // min speed input power at current conditions w/o fan - - inpX = inpClgMin - + (inpX - inpXMin) * (rs_speedF - rs_speedFMin) / (1.f - rs_speedFMin); - } + float inpX = + (inpClg - rs_fanHRtdC) * + fCondInp; // full speed input power at current conditions w/o fan + if (rs_speedF < 1.f) { + // capTotMinCt = (capClgMin - rs_fanHRtdC) * rs_fChg * rs_fCondCap * + // rs_speedFMin; + float inpXMin = + (inpClgMin - rs_fanHRtdC * rs_speedFMin) * + fCondInp; // min speed input power at current conditions w/o fan - rs_effCt = abs(rs_capSenCt) / inpX; // compressor-only sensible COP - } - } + inpX = inpClgMin + (inpX - inpXMin) * (rs_speedF - rs_speedFMin) / + (1.f - rs_speedFMin); + } + + rs_effCt = abs(rs_capSenCt) / inpX; // compressor-only sensible COP + } + } - rs_capLatCt = rs_capTotCt - rs_capSenCt; // latent, Btuh + rs_capLatCt = rs_capTotCt - rs_capSenCt; // latent, Btuh - // outlet (leaving) conditions -#if defined( _DEBUG) - float hIn = rs_asIn.as_Enthalpy(); + // outlet (leaving) conditions +#if defined(_DEBUG) + float hIn = rs_asIn.as_Enthalpy(); #endif #if 0 @@ -4006,636 +3978,639 @@ x float qLat = rs_capLatCt; x int rx = rs_asOut.as_AddQSenLat( qSen, qLat, rs_amf, 0.95f); x rs_asOut = asSav; #endif - int ret = rs_asOut.as_AddQSenLat( rs_capSenCt, rs_capLatCt, rs_amf, 0.95f); - if (ret == 1) - { // as_AddQSenLat() applied humidity limit - // update capacities - rs_capTotCt = rs_capSenCt + rs_capLatCt; - rs_fCondCap = rs_capTotCt / rs_capnfX; // back-calc consistent value - rs_SHR = rs_capTotCt != 0.f ? rs_capSenCt / rs_capTotCt : 0.f; - } + int ret = rs_asOut.as_AddQSenLat(rs_capSenCt, rs_capLatCt, rs_amf, 0.95f); + if (ret == 1) { // as_AddQSenLat() applied humidity limit + // update capacities + rs_capTotCt = rs_capSenCt + rs_capLatCt; + rs_fCondCap = rs_capTotCt / rs_capnfX; // back-calc consistent value + rs_SHR = rs_capTotCt != 0.f ? rs_capSenCt / rs_capTotCt : 0.f; + } - // draw-thru fan heat - if (rs_fan.fanTy != C_FANTYCH_BLOWTHRU) - rs_asOut.as_AddQSen( rs_speedF*rs_fanHeatC, rs_amf); + // draw-thru fan heat + if (rs_fan.fanTy != C_FANTYCH_BLOWTHRU) + rs_asOut.as_AddQSen(rs_speedF * rs_fanHeatC, rs_amf); -#if defined( RSYS_FIXEDOAT) - rs_asOut.as_Set(50., .001); +#if defined(RSYS_FIXEDOAT) + rs_asOut.as_Set(50., .001); #endif - rs_capSenNetFS = rs_capSenCt / rs_speedF + rs_fanHeatC; // net full speed sensible capacity + rs_capSenNetFS = + rs_capSenCt / rs_speedF + rs_fanHeatC; // net full speed sensible capacity -#if defined( _DEBUG) - if (!Top.isWarmup) - { double tSup = rs_asOut.as_tdb; - if (tSup < 20. || tSup > 200.) - orWarn("unreasonable cooling supply temp %.2f F", tSup); +#if defined(_DEBUG) + if (!Top.isWarmup) { + double tSup = rs_asOut.as_tdb; + if (tSup < 20. || tSup > 200.) + orWarn("unreasonable cooling supply temp %.2f F", tSup); #if 0 if (rs_asIn.as_tdb - tSup < 10.) orWarn("low cooling deltaT -- tRet=%.2f F, tSup=%.2f, SHR=%.3f", rs_asIn.as_tdb, tSup, rs_SHR); #endif - float hOut = rs_asOut.as_Enthalpy(); - float hDelta = rs_amf * (hOut - hIn); - float capTot = rs_capSenCt + rs_capLatCt + rs_speedF*rs_fanHeatC; - float fDiff = frDiff( hDelta, capTot); - if (fDiff > .0002f) - orWarn( "coil enthalpy mismatch (%.5f)", fDiff); - } + float hOut = rs_asOut.as_Enthalpy(); + float hDelta = rs_amf * (hOut - hIn); + float capTot = rs_capSenCt + rs_capLatCt + rs_speedF * rs_fanHeatC; + float fDiff = frDiff(hDelta, capTot); + if (fDiff > .0002f) + orWarn("coil enthalpy mismatch (%.5f)", fDiff); + } #endif -} // RSYS::rs_CoolingOutletAirState +} // RSYS::rs_CoolingOutletAirState //---------------------------------------------------------------------------- -void RSYS::rs_GetPerfClg( // derive current cooling performance values - float& capTot, // total capacity, kBtuh - float& capSen, // sensible capacity, kBtuh - float& pwrIn) const // input power, kW +void RSYS::rs_GetPerfClg( // derive current cooling performance values + float &capTot, // total capacity, kBtuh + float &capSen, // sensible capacity, kBtuh + float &pwrIn) const // input power, kW // Used only for reporting (not simulation) 10-2018 // All returned values include fan heat / power { - pwrIn = (fabs( rs_capSenCt / rs_effCt) + rs_fanHeatC) - / (BtuperWh * 1000.f); + pwrIn = (fabs(rs_capSenCt / rs_effCt) + rs_fanHeatC) / (BtuperWh * 1000.f); - capTot = (fabs( rs_capTotCt)-rs_fanHeatC)/1000.f; - capSen = (fabs( rs_capSenCt)-rs_fanHeatC)/1000.f; + capTot = (fabs(rs_capTotCt) - rs_fanHeatC) / 1000.f; + capSen = (fabs(rs_capSenCt) - rs_fanHeatC) / 1000.f; #if 0 x // check: derived total capacity from air stream enthalpy change x float capTotX = rs_amf * (rs_asOut.as_Enthalpy() - rs_asIn.as_Enthalpy()); #endif -} // RSYS::rs_GetPerfClg +} // RSYS::rs_GetPerfClg //------------------------------------------------------------------------------- -RC RSYS::rs_PerfMapAC() // generate AC cooling performance map +RC RSYS::rs_PerfMapAC() // generate AC cooling performance map // testing aid { #if 1 -// Goodman -static float fAF[] = { .550f, .700f, .875f, 1.f, 1.125f, 0.f }; -static float tdbI[] = { 70.f, 75.f, 80.f, 85.f, 0.f }; -static float twbI[] = { 59.f, 63.f, 67.f, 71.f }; -static float tdbO[] = { 65.f, 75.f, 85.f, 95.f, 105.f, 115.f, 0.f }; + // Goodman + static float fAF[] = {.550f, .700f, .875f, 1.f, 1.125f, 0.f}; + static float tdbI[] = {70.f, 75.f, 80.f, 85.f, 0.f}; + static float twbI[] = {59.f, 63.f, 67.f, 71.f}; + static float tdbO[] = {65.f, 75.f, 85.f, 95.f, 105.f, 115.f, 0.f}; #else -// Carrier -static float fAF[] = { .875f, 1.f, 1.125f, 0.f }; -static float tdbI[] = { 70.f, 75.f, 80.f, 0.f }; -static float twbI[] = { 57.f, 62.f, 67.f, 72.f }; -static float tdbO[] = { 75.f, 85.f, 95.f, 105.f, 115.f, 125.f, 0.f }; -#endif - - // write to file PM_.csv - const char* nameX = name; - if (IsBlank( nameX)) - nameX = "RSYS"; - FILE* f = fopen( strtprintf( "PM_%s.csv", nameX), "wt"); - if (!f) - return RCBAD; // can't open file - - RSYS rsSave( *this); // save for restore at exit - // (we alter mbrs here) - - fprintf(f, "%s\n", rs_desc.CStrIfNotBlank(nameX)); - - fprintf( f, "cfm,IDB (F),IWB (F),ODB (F),CapTot (kBtuh),CapSen (kBtuh),Pwr (kW)\n"); - - float capNomTonsX = rs_ClgCapNomTons(); // exact - float capNomTons = rs_ClgCapNomTons( .5f); // nearest .5 ton - float vfPerTonNom = 400.f * capNomTons / capNomTonsX; - if (Top.tp_airSH <= 0.f) - Top.tp_Psychro(); // set air constants - - for (int iAF=0;fAF[ iAF]>0.f; iAF++) - { rs_vfPerTon = vfPerTonNom * fAF[ iAF]; - float cfm = rs_vfPerTon * capNomTonsX; - rs_SetRunConstants(); - rs_SetupSizes(); - for (int idbI=0; tdbI[ idbI] > 0.f; idbI++ ) - { for (int iwbI=0; twbI[ iwbI] > 0.f; iwbI++) - { if (twbI[ iwbI] >= tdbI[ idbI]) - continue; - // entering conditions - rs_twbIn = twbI[ iwbI]; - float w = psyHumRat1( tdbI[ idbI], rs_twbIn); - rs_asIn.as_Set( tdbI[ idbI], w); - for (int idbO=0; tdbO[ idbO] > 0.f; idbO++) - { // outdoor conditions - rs_asOut = rs_asIn; - rs_tdbOut = tdbO[ idbO]; - rs_CoolingOutletAirState(); - float capTot, capSen, pwrIn; - rs_GetPerfClg( capTot, capSen, pwrIn); - fprintf( f, "%.f,%.f,%.f,%.f,%.2f,%.2f,%.2f\n", - cfm, tdbI[ idbI], rs_twbIn, rs_tdbOut, - capTot, capSen, pwrIn); - } - } - } - } - - fclose( f); - - *this = rsSave; // restore entry state - - return RCOK; -} // RSYS::rs_PerfMapAC + // Carrier + static float fAF[] = {.875f, 1.f, 1.125f, 0.f}; + static float tdbI[] = {70.f, 75.f, 80.f, 0.f}; + static float twbI[] = {57.f, 62.f, 67.f, 72.f}; + static float tdbO[] = {75.f, 85.f, 95.f, 105.f, 115.f, 125.f, 0.f}; +#endif + + // write to file PM_.csv + const char *nameX = name; + if (IsBlank(nameX)) + nameX = "RSYS"; + FILE *f = fopen(strtprintf("PM_%s.csv", nameX), "wt"); + if (!f) + return RCBAD; // can't open file + + RSYS rsSave(*this); // save for restore at exit + // (we alter mbrs here) + + fprintf(f, "%s\n", rs_desc.CStrIfNotBlank(nameX)); + + fprintf( + f, + "cfm,IDB (F),IWB (F),ODB (F),CapTot (kBtuh),CapSen (kBtuh),Pwr (kW)\n"); + + float capNomTonsX = rs_ClgCapNomTons(); // exact + float capNomTons = rs_ClgCapNomTons(.5f); // nearest .5 ton + float vfPerTonNom = 400.f * capNomTons / capNomTonsX; + if (Top.tp_airSH <= 0.f) + Top.tp_Psychro(); // set air constants + + for (int iAF = 0; fAF[iAF] > 0.f; iAF++) { + rs_vfPerTon = vfPerTonNom * fAF[iAF]; + float cfm = rs_vfPerTon * capNomTonsX; + rs_SetRunConstants(); + rs_SetupSizes(); + for (int idbI = 0; tdbI[idbI] > 0.f; idbI++) { + for (int iwbI = 0; twbI[iwbI] > 0.f; iwbI++) { + if (twbI[iwbI] >= tdbI[idbI]) + continue; + // entering conditions + rs_twbIn = twbI[iwbI]; + float w = psyHumRat1(tdbI[idbI], rs_twbIn); + rs_asIn.as_Set(tdbI[idbI], w); + for (int idbO = 0; tdbO[idbO] > 0.f; idbO++) { // outdoor conditions + rs_asOut = rs_asIn; + rs_tdbOut = tdbO[idbO]; + rs_CoolingOutletAirState(); + float capTot, capSen, pwrIn; + rs_GetPerfClg(capTot, capSen, pwrIn); + fprintf(f, "%.f,%.f,%.f,%.f,%.2f,%.2f,%.2f\n", cfm, tdbI[idbI], + rs_twbIn, rs_tdbOut, capTot, capSen, pwrIn); + } + } + } + } + + fclose(f); + + *this = rsSave; // restore entry state + + return RCOK; +} // RSYS::rs_PerfMapAC //------------------------------------------------------------------------------- -RC RSYS::rs_ExportCorrelationValues() // write CSV file containing values from RSYS correlations +RC RSYS::rs_ExportCorrelationValues() // write CSV file containing values from + // RSYS correlations // testing aid { - static float fAF[] = { .550f, .700f, .875f, 1.f, 1.125f, 0.f }; - static float tdbI[] = { 70.f, 72.f, 74.f, 76.f, 78.f, 80.f, 85.f, 0.f }; - static float twbI[] = { 54.f, 56.f, 58.f, 60.f, 62.f, 64.f, 67.f, 71.f, 0.f }; - static float tdbO[] = { 65.f, 75.f, 82.f, 85.f, 95.f, 105.f, 115.f, 0.f }; - - // write to file PM_.csv - const char* nameX = name; - if (IsBlank(nameX)) - nameX = "RSYS"; - FILE* f = fopen(strtprintf("CORVALS_%s.csv", nameX), "wt"); - if (!f) - return RCBAD; // can't open file - - RSYS rsSave(*this); // save for restore at exit - // (we alter mbrs here) - - fprintf(f, "%s\n", rs_desc.CStrIfNotBlank(nameX)); - - fprintf(f, "cfm/ton,ODB (F),EDB (F),EWB (F),RH,SHR,fCap,fEER,fInp\n"); - - float vfPerTonNom = 400.f; - - if (Top.tp_airSH <= 0.f) - Top.tp_Psychro(); // set air constants - - for (int iAF = 0; fAF[iAF] > 0.f; iAF++) - { - rs_vfPerTon = vfPerTonNom * fAF[iAF]; - char sVf[20]; - sprintf(sVf, "%.f", rs_vfPerTon); - for (int idbO = 0; tdbO[idbO] > 0.f; idbO++) - { // outdoor conditions - rs_tdbOut = tdbO[idbO]; - char sDbO[20]; - sprintf(sDbO, "%.f", rs_tdbOut); - - for (int idbI = 0; tdbI[idbI] > 0.f; idbI++) - { // entering dry bulb - rs_tdbCoilIn = tdbI[idbI]; - char sDbI[20]; - sprintf(sDbI, "%.f", rs_tdbCoilIn); - for (int iwbI = 0; twbI[iwbI] > 0.f; iwbI++) - { // entering wet bulb conditions - rs_twbCoilIn = twbI[iwbI]; - if (rs_twbCoilIn > rs_tdbCoilIn) - continue; - float rh = psyRelHum(rs_tdbCoilIn, rs_twbCoilIn); - rs_CoolingSHR(); - rs_CoolingCapF1Spd(); - float fCondInp = rs_CoolingEff1Spd(); - fprintf(f, "%s,%s,%s,%.f,%.3f,%.3f,%.3f,%.3f,%.3f\n", - sVf, sDbO, sDbI, rs_twbCoilIn, rh, rs_SHR, rs_fCondCap, rs_fCondEER, fCondInp); - } - } - } - } - - fclose(f); - - *this = rsSave; // restore entry state - - return RCOK; -} // RSYS::rs_ExportCorrelationValues + static float fAF[] = {.550f, .700f, .875f, 1.f, 1.125f, 0.f}; + static float tdbI[] = {70.f, 72.f, 74.f, 76.f, 78.f, 80.f, 85.f, 0.f}; + static float twbI[] = {54.f, 56.f, 58.f, 60.f, 62.f, 64.f, 67.f, 71.f, 0.f}; + static float tdbO[] = {65.f, 75.f, 82.f, 85.f, 95.f, 105.f, 115.f, 0.f}; + + // write to file PM_.csv + const char *nameX = name; + if (IsBlank(nameX)) + nameX = "RSYS"; + FILE *f = fopen(strtprintf("CORVALS_%s.csv", nameX), "wt"); + if (!f) + return RCBAD; // can't open file + + RSYS rsSave(*this); // save for restore at exit + // (we alter mbrs here) + + fprintf(f, "%s\n", rs_desc.CStrIfNotBlank(nameX)); + + fprintf(f, "cfm/ton,ODB (F),EDB (F),EWB (F),RH,SHR,fCap,fEER,fInp\n"); + + float vfPerTonNom = 400.f; + + if (Top.tp_airSH <= 0.f) + Top.tp_Psychro(); // set air constants + + for (int iAF = 0; fAF[iAF] > 0.f; iAF++) { + rs_vfPerTon = vfPerTonNom * fAF[iAF]; + char sVf[20]; + sprintf(sVf, "%.f", rs_vfPerTon); + for (int idbO = 0; tdbO[idbO] > 0.f; idbO++) { // outdoor conditions + rs_tdbOut = tdbO[idbO]; + char sDbO[20]; + sprintf(sDbO, "%.f", rs_tdbOut); + + for (int idbI = 0; tdbI[idbI] > 0.f; idbI++) { // entering dry bulb + rs_tdbCoilIn = tdbI[idbI]; + char sDbI[20]; + sprintf(sDbI, "%.f", rs_tdbCoilIn); + for (int iwbI = 0; twbI[iwbI] > 0.f; + iwbI++) { // entering wet bulb conditions + rs_twbCoilIn = twbI[iwbI]; + if (rs_twbCoilIn > rs_tdbCoilIn) + continue; + float rh = psyRelHum(rs_tdbCoilIn, rs_twbCoilIn); + rs_CoolingSHR(); + rs_CoolingCapF1Spd(); + float fCondInp = rs_CoolingEff1Spd(); + fprintf(f, "%s,%s,%s,%.f,%.3f,%.3f,%.3f,%.3f,%.3f\n", sVf, sDbO, sDbI, + rs_twbCoilIn, rh, rs_SHR, rs_fCondCap, rs_fCondEER, fCondInp); + } + } + } + } + + fclose(f); + + *this = rsSave; // restore entry state + + return RCOK; +} // RSYS::rs_ExportCorrelationValues //----------------------------------------------------------------------------- -RC RSYS::rs_PerfDataASHP() -{ - // write to file PD_.csv - const char* nameX = name; - int iHSPF = int( 0.5f + 10.f*rs_HSPF); - if (IsBlank( nameX)) - nameX = "RSYS"; - FILE* f = fopen( strtprintf( "PD_%s%d.csv", nameX, iHSPF), "wt"); - if (!f) - return RCBAD; // can't open file - - RSYS rsSave( *this); // save for restore at exit - // (we alter mbrs here) - - fprintf( f, "%s,All values include rating fan power,,%s,CSE %s\n", - rs_desc.CStrIfNotBlank( nameX), - Top.runDateTime.CStr(), - Top.tp_progVersion.CStr()); - - fprintf( f, "HSPF=%0.2f, HSPF CSE=%0.3f,,HSPF MP=%0.3f,,HSPF ESL=%0.3f,,HSPF E+=%0.3f\n", - rs_HSPF, - rs_HSPFCheckASHP( 0), rs_HSPFCheckASHP( 1), rs_HSPFCheckASHP( 2), rs_HSPFCheckASHP( 3)); - - fprintf( f, "tODB,cap CSE,inp CSE,COP CSE,cap MP,inp MP,COP MP,cap ESL,inp ESL,COP ESL,cap E+,inp E+,COP E+\n"); - - for (int iR=0; iR<3; iR++) - { float tRat[] = { 17, 35, 47 }; - rs_PerfDataASHP1( f, tRat[ iR]); - } - - fprintf( f, "\n"); - - for (int iDB = -10; iDB<63; iDB++) - rs_PerfDataASHP1( f, float( iDB)); - - fprintf( f, "\ncap17/cap47 vs HSPF\n"); - - for (iHSPF=65; iHSPF<140; iHSPF++) - { rs_HSPF = float( iHSPF)/10.f; - fprintf( f, "%0.3f,%0.3f\n", rs_HSPF, rs_CapRat1747()); - } - - // loop over range of HSPFs and cap47s, derive back-calc HSPF - fprintf( f, "\nBack-Calculated HSPF\n"); - const float cds[] = { 0.f, .125f, .25f, -1.f }; - const float hspfs[] = { 6.5f, 7.0f, 7.5f, 8.0f, 8.5f, 9.0f, 9.5f, 10.0f, 10.5f, 11.0f, 11.5f, 12.f, 12.5f, 13.f, -1. }; - const float cap47s[] = { 18000.f, 24000.f, 54999.f, 55000.f, -1.f }; - - ClrSet( RSYS_COP47); - ClrSet( RSYS_CAP35); - ClrSet( RSYS_COP35); - ClrSet( RSYS_CAP17); - ClrSet( RSYS_COP17); - - for (int iH=0; hspfs[ iH] > 0.f; iH++) - { rs_HSPF = hspfs[ iH]; - for (int iC=0; cap47s[ iC] > 0.f; iC++) - { rs_cap47 = cap47s[ iC]; - for (int iD=0; cds[ iD] >= 0.f; iD++) - { rs_CdH = cds[ iD]; - rs_SetupASHP(); - if (iD==0) - fprintf( f, "%0.3f,%0.f", rs_HSPF, rs_cap47); - float bcHSPF = rs_HSPFCheckASHP( 0); - fprintf( f, ",%0.3f", bcHSPF); - } - fprintf( f, "\n"); - } - } - - fclose( f); - *this = rsSave; // restore entry state - - return RCOK; -} // RSYS::rs_PerfDataASHP +RC RSYS::rs_PerfDataASHP() { + // write to file PD_.csv + const char *nameX = name; + int iHSPF = int(0.5f + 10.f * rs_HSPF); + if (IsBlank(nameX)) + nameX = "RSYS"; + FILE *f = fopen(strtprintf("PD_%s%d.csv", nameX, iHSPF), "wt"); + if (!f) + return RCBAD; // can't open file + + RSYS rsSave(*this); // save for restore at exit + // (we alter mbrs here) + + fprintf(f, "%s,All values include rating fan power,,%s,CSE %s\n", + rs_desc.CStrIfNotBlank(nameX), Top.runDateTime.CStr(), + Top.tp_progVersion.CStr()); + + fprintf(f, + "HSPF=%0.2f, HSPF CSE=%0.3f,,HSPF MP=%0.3f,,HSPF ESL=%0.3f,,HSPF " + "E+=%0.3f\n", + rs_HSPF, rs_HSPFCheckASHP(0), rs_HSPFCheckASHP(1), + rs_HSPFCheckASHP(2), rs_HSPFCheckASHP(3)); + + fprintf(f, "tODB,cap CSE,inp CSE,COP CSE,cap MP,inp MP,COP MP,cap ESL,inp " + "ESL,COP ESL,cap E+,inp E+,COP E+\n"); + + for (int iR = 0; iR < 3; iR++) { + float tRat[] = {17, 35, 47}; + rs_PerfDataASHP1(f, tRat[iR]); + } + + fprintf(f, "\n"); + + for (int iDB = -10; iDB < 63; iDB++) + rs_PerfDataASHP1(f, float(iDB)); + + fprintf(f, "\ncap17/cap47 vs HSPF\n"); + + for (iHSPF = 65; iHSPF < 140; iHSPF++) { + rs_HSPF = float(iHSPF) / 10.f; + fprintf(f, "%0.3f,%0.3f\n", rs_HSPF, rs_CapRat1747()); + } + + // loop over range of HSPFs and cap47s, derive back-calc HSPF + fprintf(f, "\nBack-Calculated HSPF\n"); + const float cds[] = {0.f, .125f, .25f, -1.f}; + const float hspfs[] = {6.5f, 7.0f, 7.5f, 8.0f, 8.5f, 9.0f, 9.5f, 10.0f, + 10.5f, 11.0f, 11.5f, 12.f, 12.5f, 13.f, -1.}; + const float cap47s[] = {18000.f, 24000.f, 54999.f, 55000.f, -1.f}; + + ClrSet(RSYS_COP47); + ClrSet(RSYS_CAP35); + ClrSet(RSYS_COP35); + ClrSet(RSYS_CAP17); + ClrSet(RSYS_COP17); + + for (int iH = 0; hspfs[iH] > 0.f; iH++) { + rs_HSPF = hspfs[iH]; + for (int iC = 0; cap47s[iC] > 0.f; iC++) { + rs_cap47 = cap47s[iC]; + for (int iD = 0; cds[iD] >= 0.f; iD++) { + rs_CdH = cds[iD]; + rs_SetupASHP(); + if (iD == 0) + fprintf(f, "%0.3f,%0.f", rs_HSPF, rs_cap47); + float bcHSPF = rs_HSPFCheckASHP(0); + fprintf(f, ",%0.3f", bcHSPF); + } + fprintf(f, "\n"); + } + } + + fclose(f); + *this = rsSave; // restore entry state + + return RCOK; +} // RSYS::rs_PerfDataASHP //----------------------------------------------------------------------------- -void RSYS::rs_PerfDataASHP1( // test aid: ASHP performance from alt models - FILE* f, // where to write - float tdbOut) // outdoor temp, F +void RSYS::rs_PerfDataASHP1( // test aid: ASHP performance from alt models + FILE *f, // where to write + float tdbOut) // outdoor temp, F // all values include rating fan power { - // CSE "original" - float COP; - float cap = rs_PerfASHP( 0+0x100, tdbOut, COP, 0.f); - float inp = cap / COP; - - // Micropas - float COPMP; - float capMP = rs_PerfASHP( 1+0x100, tdbOut, COPMP, 0.f); - float inpMP = capMP / COPMP; - - // ESL - float COPESL; - float capESL = rs_PerfASHP( 2+0x100, tdbOut, COPESL, rs_fanHRtdH); - float inpESL = capESL / COPESL; - - // E+ - float COPEP; - float capEP = rs_PerfASHP( 3+0x100, tdbOut, COPEP, rs_fanHRtdH); - float inpEP = capEP / COPEP; - - fprintf( f, "%.f, %.f, %.f, %.3f, %.f, %.f, %.3f, %.f, %.f, %.3f, %.f, %.f, %.3f\n", - tdbOut, cap, inp, COP, - capMP, inpMP, COPMP, - capESL, inpESL, COPESL, - capEP, inpEP, COPEP); - -} // RSYS::rs_PerfDataASHP1 + // CSE "original" + float COP; + float cap = rs_PerfASHP(0 + 0x100, tdbOut, COP, 0.f); + float inp = cap / COP; + + // Micropas + float COPMP; + float capMP = rs_PerfASHP(1 + 0x100, tdbOut, COPMP, 0.f); + float inpMP = capMP / COPMP; + + // ESL + float COPESL; + float capESL = rs_PerfASHP(2 + 0x100, tdbOut, COPESL, rs_fanHRtdH); + float inpESL = capESL / COPESL; + + // E+ + float COPEP; + float capEP = rs_PerfASHP(3 + 0x100, tdbOut, COPEP, rs_fanHRtdH); + float inpEP = capEP / COPEP; + + fprintf( + f, + "%.f, %.f, %.f, %.3f, %.f, %.f, %.3f, %.f, %.f, %.3f, %.f, %.f, %.3f\n", + tdbOut, cap, inp, COP, capMP, inpMP, COPMP, capESL, inpESL, COPESL, capEP, + inpEP, COPEP); + +} // RSYS::rs_PerfDataASHP1 //----------------------------------------------------------------------------- -float RSYS::rs_PerfASHP( // ASHP performance (simplified call) - int ashpModel, // 0=CSE, 1=MP, 2=ESL, 3=Cutler - // + 0x100: do NOT model defrost heating - // else model defrost if available - float tdbOut, // outdoor dry bulb, F - float& COP, // returned: compressor-only full speed COP at tdbOut - float fanHAdj /*=0.f*/) // fan power adjustment, Btuh - // removed from capacity and input (before COP calc) +float RSYS::rs_PerfASHP( // ASHP performance (simplified call) + int ashpModel, // 0=CSE, 1=MP, 2=ESL, 3=Cutler + // + 0x100: do NOT model defrost heating + // else model defrost if available + float tdbOut, // outdoor dry bulb, F + float &COP, // returned: compressor-only full speed COP at tdbOut + float fanHAdj /*=0.f*/) // fan power adjustment, Btuh + // removed from capacity and input (before COP calc) // returns total heating capacity (compressor + capDefrostHt), Btuh { - float capHt, inpHt, capDfHt, capHtMin, inpHtMin, capDfHtMin; - COP = rs_PerfASHP2(ashpModel, tdbOut, fanHAdj, - capHt, inpHt, capDfHt, capHtMin, inpHtMin, capDfHtMin); + float capHt, inpHt, capDfHt, capHtMin, inpHtMin, capDfHtMin; + COP = rs_PerfASHP2(ashpModel, tdbOut, fanHAdj, capHt, inpHt, capDfHt, + capHtMin, inpHtMin, capDfHtMin); - return capHt; + return capHt; -} // RSYS::rs_PerfASHP +} // RSYS::rs_PerfASHP //------------------------------------------------------------------------------ -float RSYS::rs_PerfASHP2( // ASHP performance - int ashpModel, // 0=CSE, 1=MP, 2=ESL, 3=Cutler - // + 0x100: do NOT model defrost heating - // else model defrost if available - // Note: variable speed supported for CSE only - float tdbOut, // outdoor dry bulb, F - float fanHAdj, // fan power adjustment, Btuh - // removed from capacity and input (before COP calc) - float& capHt, // returned: compressor-only full speed heating capacity - float& inpHt, // returned: compressor-only full speed input power, Btuh - float& capDfHt, // returned: defrost full speed aux heat output, Btuh - // = strip/furnace output to make up compressor cooling - // (depends on rs_defrostModel) - float& capHtMin, // returned: compressor-only min speed heating capacity, Btuh - // (= capHt if single speed) - float& inpHtMin, // returned: compressor-only min speed input power, Btuh - // (= inpHt if single speed) - float& capDfHtMin, // returned: defrost min speed aux heat output, Btuh - // (= capDfHt if single speed) - float COPAdjF /*=1.f*/) // COP adjustment factor - // multiplies final COP result +float RSYS::rs_PerfASHP2( // ASHP performance + int ashpModel, // 0=CSE, 1=MP, 2=ESL, 3=Cutler + // + 0x100: do NOT model defrost heating + // else model defrost if available + // Note: variable speed supported for CSE only + float tdbOut, // outdoor dry bulb, F + float fanHAdj, // fan power adjustment, Btuh + // removed from capacity and input (before COP calc) + float &capHt, // returned: compressor-only full speed heating capacity + float &inpHt, // returned: compressor-only full speed input power, Btuh + float &capDfHt, // returned: defrost full speed aux heat output, Btuh + // = strip/furnace output to make up compressor cooling + // (depends on rs_defrostModel) + float + &capHtMin, // returned: compressor-only min speed heating capacity, Btuh + // (= capHt if single speed) + float &inpHtMin, // returned: compressor-only min speed input power, Btuh + // (= inpHt if single speed) + float &capDfHtMin, // returned: defrost min speed aux heat output, Btuh + // (= capDfHt if single speed) + float COPAdjF /*=1.f*/) // COP adjustment factor + // multiplies final COP result // returns compressor-only full-speed COP (as adjusted by COPAdjF) { - capDfHt = capDfHtMin = 0.f; - BOOL bDoDefrost = (ashpModel & 0x100) == 0 - && rs_defrostModel == C_RSYSDEFROSTMODELCH_REVCYCLEAUX; - ashpModel &= 0xff; - - if (ashpModel == 0) - { - int iDefrost = rs_HasDefrost() && tdbOut > 17.f && tdbOut < 45.f; // 1 if defrost, else 0 - if (rs_IsASHPVC()) - { // retrieve min/max cap and input at tdbOut - // rs_pRgiHtg[ 0] includes 5, 17, 35, and 47 F points - // values include fan heat/power - /* rc = */ rs_GetPerfBtwxt(rs_pRgiHtg[ 0], tdbOut, capHt, inpHt, capHtMin, inpHtMin); -#if defined( _DEBUG) - if (capHt < capHtMin) - printf("\ncapHt < capHtMin"); -#endif - rs_speedFMin = capHt <= 0.f ? 1.f : capHtMin / capHt; - if (iDefrost && bDoDefrost) - { // C_RSYSDEFROSTMODELCH_REVCYCLEAUX - // include sufficient aux heat to make up for ASHP cooling - // = steady-state cap - integrated cap - // limit to available aux heat capacity - // Note: does NOT over-commit aux because compressor - // and aux do not run simultaneously. - float capHtSS, capHtSSMin, inpSink; - /* rc = */ rs_GetPerfBtwxt(rs_pRgiHtg[1], tdbOut, capHtSS, inpSink, capHtSSMin, inpSink); - capDfHt = bracket(0.f, capHtSS - capHt, rs_capAuxH); - capDfHtMin = bracket(0.f, capHtSSMin - capHtMin, rs_capAuxH); - } - capHt -= fanHAdj; - inpHt -= fanHAdj; - capHtMin -= fanHAdj * rs_speedFMin; - inpHtMin -= fanHAdj * rs_speedFMin; - } - else - { - // CSE AHRI 210/240 model - // input power and heating capacity at current conditions (Btuh) - float tdbM17 = tdbOut - 17.f; - inpHt = rs_inp17 + rs_ASHPInpF[iDefrost] * tdbM17 - fanHAdj; - capHt = rs_cap17 + rs_ASHPCapF[iDefrost] * tdbM17 - fanHAdj; - if (iDefrost && bDoDefrost) - { // C_RSYSDEFROSTMODELCH_REVCYCLEAUX - // include sufficient aux heat to make up for ASHP cooling - // limit to available aux heat capacity - // Note: does NOT over-commit aux because compressor - // and aux do not run simultaneously. - capDfHt = (rs_ASHPCapF[0] - rs_ASHPCapF[1]) * tdbM17; - if (capDfHt > rs_capAuxH) - capDfHt = rs_capAuxH; - } - } - } - else - { // DOE-2 curve fit methods - float tdbOut2 = tdbOut * tdbOut; - float tdbOut3 = tdbOut2 * tdbOut; - - float capNet; // capacity with fan - double fCap, fEIR; - if (ashpModel == 1) - { // Micropas curves from Ken Nittler 5-2013) - // Believed to be from DOE-2.1E? - // DOE-2 curves should be applied to compressor-only - // but it appears the Micropas implementation applied to values with fan - - fCap = 0.302 + 0.0121*tdbOut + 0.0000759*tdbOut2 - 0.000000384*tdbOut3; - capNet = fCap * rs_cap47; // capacity with fan - capHt = capNet - fanHAdj; // capacity w/o fan - - float EIR47 = 1./(rs_HSPF*0.4f); // EIR at 47F w/fan power - fEIR = 1.9 - 0.025*tdbOut + 0.000134*tdbOut2 - 0.000000213*tdbOut3; - inpHt = capNet * fEIR * EIR47 - fanHAdj; // input w/o fan - } - else if (ashpModel == 2) - { // ESL curves from Juan-Carlos Baltazar (ESL, Texas A&M), 5-2013 - // From DOE-2.1E? - - // capacity (curve SDL-C51) - fCap = 0.34148808 + 0.00894102*tdbOut + 0.00010787 * tdbOut2; - capHt = fCap * rs_cap47; - - // EIR (curve SDL-C56) - fEIR = 2.03914613 - 0.03906753*tdbOut + 0.00045617*tdbOut2 - 0.00000203*tdbOut3; - - // fan / no-fan issue - - // ESL method used in their RESNET runs - // approximates HSPF w/o fans - // however, they discovered after the fact that RESNET procedure is based on - // rated HSPF (with fans) - float HSPFX = 1./((1./rs_HSPF) - .01095); - float EIR47X = .582 * (1./(HSPFX/3.413)); // compressor-only EIR @ 47 F - - // capNet? - inpHt = capHt * fEIR * EIR47X + fanHAdj; // input w/o fan - } - else if (ashpModel == 3) - { // EnergyPlus model with Cutler/NREL coefficients - -// Data from tables 12 and 13 of NREL/TP-5500-56354 (Cutler et al.) January 2013 -// organization: epXXX[ iHS][ iCoeff] -// iHS: 0=cooling, 1 spd; 1=cooling, 2 spd lo; 2= cooling, 2 spd hi -// 3=heating, 1 spd; 4=heating, 2 spd lo; 5= heating, 2 spd hi -// iCoeff: 0=a, 1=b, 2=c, 3=d, 4=e, 5=f -// capacity coefficents -static const double epCap[ 6][ 6] = { -{ 3.68637657, -0.098352478, 0.000956357, 0.005838141, -0.0000127, -0.000131702 }, -{ 3.998418659, -0.108728222, 0.001056818, 0.007512314, -0.0000139, -0.000164716 }, -{ 3.466810106, -0.091476056, 0.000901205, 0.004163355, -0.00000919, -0.000110829 }, -{ 0.566333415, -0.000744164, -0.0000103, 0.009414634, 0.0000506, -0.00000675 }, -{ 0.335690634, 0.002405123, -0.0000464, 0.013498735, 0.0000499, -0.00000725 }, -{ 0.306358843, 0.005376987, -0.0000579, 0.011645092, 0.0000591, -0.0000203 }}; -// EIR coefficients -static const double epEIR[ 6][ 6] = { -{ -3.437356399, 0.136656369, -0.001049231, -0.0079378, 0.000185435, -0.0001441 }, -{ -4.282911381, 0.181023691, -0.001357391, -0.026310378, 0.000333282, -0.000197405 }, -{ -3.557757517, 0.112737397, -0.000731381, 0.013184877, 0.000132645, -0.000338716 }, -{ 0.718398423, 0.003498178, 0.000142202, -0.005724331, 0.00014085, -0.000215321 }, -{ 0.36338171, 0.013523725, 0.000258872, -0.009450269, 0.000439519, -0.000653723 }, -{ 0.981100941, -0.005158493, 0.000243416, -0.005274352, 0.000230742, -0.000336954 }}; - - - tdbOut2 = tdbOut * tdbOut; - float tdbEnt = 70.f; - float tdbEnt2 = tdbEnt*tdbEnt; - float tdbX = tdbOut*tdbEnt; - const int iHS = 3; // heating, 1 spd - - fCap = epCap[ iHS][ 0] - + epCap[ iHS][ 1]*tdbEnt - + epCap[ iHS][ 2]*tdbEnt2 - + epCap[ iHS][ 3]*tdbOut - + epCap[ iHS][ 4]*tdbOut2 - + epCap[ iHS][ 5]*tdbX; - capHt = fCap * rs_cap47; - - fEIR = epEIR[ iHS][ 0] - + epEIR[ iHS][ 1]*tdbEnt - + epEIR[ iHS][ 2]*tdbEnt2 - + epEIR[ iHS][ 3]*tdbOut - + epEIR[ iHS][ 4]*tdbOut2 - + epEIR[ iHS][ 5]*tdbX; - inpHt = capHt * fEIR / rs_COP47; - } -#if defined( _DEBUG) - else - ASSERT( 1); // missing case + capDfHt = capDfHtMin = 0.f; + BOOL bDoDefrost = (ashpModel & 0x100) == 0 && + rs_defrostModel == C_RSYSDEFROSTMODELCH_REVCYCLEAUX; + ashpModel &= 0xff; + + if (ashpModel == 0) { + int iDefrost = rs_HasDefrost() && tdbOut > 17.f && + tdbOut < 45.f; // 1 if defrost, else 0 + if (rs_IsASHPVC()) { // retrieve min/max cap and input at tdbOut + // rs_pRgiHtg[ 0] includes 5, 17, 35, and 47 F points + // values include fan heat/power + /* rc = */ rs_GetPerfBtwxt(rs_pRgiHtg[0], tdbOut, capHt, inpHt, capHtMin, + inpHtMin); +#if defined(_DEBUG) + if (capHt < capHtMin) + printf("\ncapHt < capHtMin"); +#endif + rs_speedFMin = capHt <= 0.f ? 1.f : capHtMin / capHt; + if (iDefrost && bDoDefrost) { // C_RSYSDEFROSTMODELCH_REVCYCLEAUX + // include sufficient aux heat to make up for ASHP cooling + // = steady-state cap - integrated cap + // limit to available aux heat capacity + // Note: does NOT over-commit aux because compressor + // and aux do not run simultaneously. + float capHtSS, capHtSSMin, inpSink; + /* rc = */ rs_GetPerfBtwxt(rs_pRgiHtg[1], tdbOut, capHtSS, inpSink, + capHtSSMin, inpSink); + capDfHt = bracket(0.f, capHtSS - capHt, rs_capAuxH); + capDfHtMin = bracket(0.f, capHtSSMin - capHtMin, rs_capAuxH); + } + capHt -= fanHAdj; + inpHt -= fanHAdj; + capHtMin -= fanHAdj * rs_speedFMin; + inpHtMin -= fanHAdj * rs_speedFMin; + } else { + // CSE AHRI 210/240 model + // input power and heating capacity at current conditions (Btuh) + float tdbM17 = tdbOut - 17.f; + inpHt = rs_inp17 + rs_ASHPInpF[iDefrost] * tdbM17 - fanHAdj; + capHt = rs_cap17 + rs_ASHPCapF[iDefrost] * tdbM17 - fanHAdj; + if (iDefrost && bDoDefrost) { // C_RSYSDEFROSTMODELCH_REVCYCLEAUX + // include sufficient aux heat to make up for ASHP cooling + // limit to available aux heat capacity + // Note: does NOT over-commit aux because compressor + // and aux do not run simultaneously. + capDfHt = (rs_ASHPCapF[0] - rs_ASHPCapF[1]) * tdbM17; + if (capDfHt > rs_capAuxH) + capDfHt = rs_capAuxH; + } + } + } else { // DOE-2 curve fit methods + float tdbOut2 = tdbOut * tdbOut; + float tdbOut3 = tdbOut2 * tdbOut; + + float capNet; // capacity with fan + double fCap, fEIR; + if (ashpModel == 1) { // Micropas curves from Ken Nittler 5-2013) + // Believed to be from DOE-2.1E? + // DOE-2 curves should be applied to compressor-only + // but it appears the Micropas implementation applied to values with fan + + fCap = + 0.302 + 0.0121 * tdbOut + 0.0000759 * tdbOut2 - 0.000000384 * tdbOut3; + capNet = fCap * rs_cap47; // capacity with fan + capHt = capNet - fanHAdj; // capacity w/o fan + + float EIR47 = 1. / (rs_HSPF * 0.4f); // EIR at 47F w/fan power + fEIR = 1.9 - 0.025 * tdbOut + 0.000134 * tdbOut2 - 0.000000213 * tdbOut3; + inpHt = capNet * fEIR * EIR47 - fanHAdj; // input w/o fan + } else if (ashpModel == 2) { // ESL curves from Juan-Carlos Baltazar (ESL, + // Texas A&M), 5-2013 + // From DOE-2.1E? + + // capacity (curve SDL-C51) + fCap = 0.34148808 + 0.00894102 * tdbOut + 0.00010787 * tdbOut2; + capHt = fCap * rs_cap47; + + // EIR (curve SDL-C56) + fEIR = 2.03914613 - 0.03906753 * tdbOut + 0.00045617 * tdbOut2 - + 0.00000203 * tdbOut3; + + // fan / no-fan issue + + // ESL method used in their RESNET runs + // approximates HSPF w/o fans + // however, they discovered after the fact that RESNET procedure is based + // on + // rated HSPF (with fans) + float HSPFX = 1. / ((1. / rs_HSPF) - .01095); + float EIR47X = + .582 * (1. / (HSPFX / 3.413)); // compressor-only EIR @ 47 F + + // capNet? + inpHt = capHt * fEIR * EIR47X + fanHAdj; // input w/o fan + } else if (ashpModel == + 3) { // EnergyPlus model with Cutler/NREL coefficients + + // Data from tables 12 and 13 of NREL/TP-5500-56354 (Cutler et al.) + // January 2013 + // organization: epXXX[ iHS][ iCoeff] + // iHS: 0=cooling, 1 spd; 1=cooling, 2 spd lo; 2= cooling, 2 spd hi + // 3=heating, 1 spd; 4=heating, 2 spd lo; 5= heating, 2 spd hi + // iCoeff: 0=a, 1=b, 2=c, 3=d, 4=e, 5=f + // capacity coefficents + static const double epCap[6][6] = { + {3.68637657, -0.098352478, 0.000956357, 0.005838141, -0.0000127, + -0.000131702}, + {3.998418659, -0.108728222, 0.001056818, 0.007512314, -0.0000139, + -0.000164716}, + {3.466810106, -0.091476056, 0.000901205, 0.004163355, -0.00000919, + -0.000110829}, + {0.566333415, -0.000744164, -0.0000103, 0.009414634, 0.0000506, + -0.00000675}, + {0.335690634, 0.002405123, -0.0000464, 0.013498735, 0.0000499, + -0.00000725}, + {0.306358843, 0.005376987, -0.0000579, 0.011645092, 0.0000591, + -0.0000203}}; + // EIR coefficients + static const double epEIR[6][6] = { + {-3.437356399, 0.136656369, -0.001049231, -0.0079378, 0.000185435, + -0.0001441}, + {-4.282911381, 0.181023691, -0.001357391, -0.026310378, 0.000333282, + -0.000197405}, + {-3.557757517, 0.112737397, -0.000731381, 0.013184877, 0.000132645, + -0.000338716}, + {0.718398423, 0.003498178, 0.000142202, -0.005724331, 0.00014085, + -0.000215321}, + {0.36338171, 0.013523725, 0.000258872, -0.009450269, 0.000439519, + -0.000653723}, + {0.981100941, -0.005158493, 0.000243416, -0.005274352, 0.000230742, + -0.000336954}}; + + tdbOut2 = tdbOut * tdbOut; + float tdbEnt = 70.f; + float tdbEnt2 = tdbEnt * tdbEnt; + float tdbX = tdbOut * tdbEnt; + const int iHS = 3; // heating, 1 spd + + fCap = epCap[iHS][0] + epCap[iHS][1] * tdbEnt + epCap[iHS][2] * tdbEnt2 + + epCap[iHS][3] * tdbOut + epCap[iHS][4] * tdbOut2 + + epCap[iHS][5] * tdbX; + capHt = fCap * rs_cap47; + + fEIR = epEIR[iHS][0] + epEIR[iHS][1] * tdbEnt + epEIR[iHS][2] * tdbEnt2 + + epEIR[iHS][3] * tdbOut + epEIR[iHS][4] * tdbOut2 + + epEIR[iHS][5] * tdbX; + inpHt = capHt * fEIR / rs_COP47; + } +#if defined(_DEBUG) + else + ASSERT(1); // missing case #endif #if 0 0 capHt = fCap * rs_cap47; 0 inpHt = capNet * fEIR * EIR47 - fanHRtd; #endif - } + } - float COP; - if (capHt < 0.01f || inpHt < 0.01f) - { COP = 0.f; - capHt = 0.f; - } - else - COP = COPAdjF * capHt / inpHt; // compressor-only efficiency + float COP; + if (capHt < 0.01f || inpHt < 0.01f) { + COP = 0.f; + capHt = 0.f; + } else + COP = COPAdjF * capHt / inpHt; // compressor-only efficiency - capHt += capDfHt; // include defrost heat in capacity - capHtMin += capDfHtMin; + capHt += capDfHt; // include defrost heat in capacity + capHtMin += capDfHtMin; - if (!rs_IsASHPVC()) - { // single speed - capHtMin = capHt; - inpHtMin = inpHt; - capDfHtMin = capDfHt; - } + if (!rs_IsASHPVC()) { // single speed + capHtMin = capHt; + inpHtMin = inpHt; + capDfHtMin = capDfHt; + } - return COP; -} // RSYS::rs_PerfASHP2 + return COP; +} // RSYS::rs_PerfASHP2 //----------------------------------------------------------------------------- -RC RSYS::rs_SetupASHP() // set ASHP defaults and derived parameters +RC RSYS::rs_SetupASHP() // set ASHP defaults and derived parameters // caution: all required args assumed present and >0 // call during autosize or later // redundant calls OK { - RC rc = RCOK; + RC rc = RCOK; - // inter-default cap95 / cap47 - // at least one is present (see rs_CkF) - if (!IsSet( RSYS_CAP95)) - rs_cap95 = ASHPCap95FromCap47( rs_cap47, IsSet(RSYS_CAPRAT9547), rs_capRat9547); - else if (!IsSet( RSYS_CAP47)) - rs_cap47 = ASHPCap47FromCap95( rs_cap95, IsSet(RSYS_CAPRAT9547), rs_capRat9547); + // inter-default cap95 / cap47 + // at least one is present (see rs_CkF) + if (!IsSet(RSYS_CAP95)) + rs_cap95 = + ASHPCap95FromCap47(rs_cap47, IsSet(RSYS_CAPRAT9547), rs_capRat9547); + else if (!IsSet(RSYS_CAP47)) + rs_cap47 = + ASHPCap47FromCap95(rs_cap95, IsSet(RSYS_CAPRAT9547), rs_capRat9547); - // fan power included in heating ratings - // derive independently of cooling even though actually the same fan - // WHY: CA procedures allow separate heating / cooling sizing - // say 0 for air-to-water - rs_fanHRtdH = rs_IsASHPHydronic() - ? 0.f - : rs_FanHRtdPerTon( rs_cap47 / 12000.f); + // fan power included in heating ratings + // derive independently of cooling even though actually the same fan + // WHY: CA procedures allow separate heating / cooling sizing + // say 0 for air-to-water + rs_fanHRtdH = + rs_IsASHPHydronic() ? 0.f : rs_FanHRtdPerTon(rs_cap47 / 12000.f); - if (!IsSet( RSYS_CAP17) || rs_IsPkgRoom()) - rs_cap17 = max( rs_CapRat1747()*rs_cap47, 1.f); + if (!IsSet(RSYS_CAP17) || rs_IsPkgRoom()) + rs_cap17 = max(rs_CapRat1747() * rs_cap47, 1.f); - if (!IsSet( RSYS_CAP05)) - rs_cap05 = max(rs_CapRat0547() * rs_cap47, 1.f); + if (!IsSet(RSYS_CAP05)) + rs_cap05 = max(rs_CapRat0547() * rs_cap47, 1.f); - if (!IsSet(RSYS_CAP35)) - rs_cap35 = rs_Cap35Default(rs_cap47, rs_cap17); + if (!IsSet(RSYS_CAP35)) + rs_cap35 = rs_Cap35Default(rs_cap47, rs_cap17); #if ASHP_COPREG == 1 - // "traditional" model - if (!IsSet( RSYS_COP47)) - rs_COP47 = 0.3038073f * rs_HSPF - 1.984475f*rs_cap17/rs_cap47 + 2.360116f; - if (!IsSet( RSYS_COP17)) - rs_COP17 = 0.2359355f * rs_HSPF + 1.205568f*rs_cap17/rs_cap47 - 0.1660746; + // "traditional" model + if (!IsSet(RSYS_COP47)) + rs_COP47 = + 0.3038073f * rs_HSPF - 1.984475f * rs_cap17 / rs_cap47 + 2.360116f; + if (!IsSet(RSYS_COP17)) + rs_COP17 = + 0.2359355f * rs_HSPF + 1.205568f * rs_cap17 / rs_cap47 - 0.1660746; #elif ASHP_COPREG == 2 - // Revised 5-31-2013 - if (!IsSet( RSYS_COP47)) - rs_COP47 = 0.3225f * rs_HSPF + 0.9099f; - if (!IsSet( RSYS_COP17)) - rs_COP17 = 0.2186f * rs_HSPF + 0.6734f; + // Revised 5-31-2013 + if (!IsSet(RSYS_COP47)) + rs_COP47 = 0.3225f * rs_HSPF + 0.9099f; + if (!IsSet(RSYS_COP17)) + rs_COP17 = 0.2186f * rs_HSPF + 0.6734f; #elif ASHP_COPREG == 3 - // COP/HSPF fit 6-4-2013 - if (!IsSet( RSYS_COP47)) - rs_COP47 = -0.0129 * rs_HSPF * rs_HSPF + 0.5397 * rs_HSPF; - if (!IsSet( RSYS_COP17)) - rs_COP17 = -.00970 * rs_HSPF * rs_HSPF + 0.3805 * rs_HSPF; + // COP/HSPF fit 6-4-2013 + if (!IsSet(RSYS_COP47)) + rs_COP47 = -0.0129 * rs_HSPF * rs_HSPF + 0.5397 * rs_HSPF; + if (!IsSet(RSYS_COP17)) + rs_COP17 = -.00970 * rs_HSPF * rs_HSPF + 0.3805 * rs_HSPF; #elif ASHP_COPREG == 4 - // kW per ton fit 6-4-2013 - if (!IsSet( RSYS_COP47)) - { float kwPerTon = -0.089 * rs_HSPF + 1.7236; - rs_COP47 = 12000.f / (kwPerTon * 3413.f); - } - if (!IsSet( RSYS_COP17)) - { float kwPerTon = -0.0722 * rs_HSPF + 1.4896; - rs_COP17 = 12000.f * rs_cap17 / (rs_cap47 * kwPerTon * 3413.f); - } + // kW per ton fit 6-4-2013 + if (!IsSet(RSYS_COP47)) { + float kwPerTon = -0.089 * rs_HSPF + 1.7236; + rs_COP47 = 12000.f / (kwPerTon * 3413.f); + } + if (!IsSet(RSYS_COP17)) { + float kwPerTon = -0.0722 * rs_HSPF + 1.4896; + rs_COP17 = 12000.f * rs_cap17 / (rs_cap47 * kwPerTon * 3413.f); + } #elif ASHP_COPREG == 5 - // force COP17 = 1.8 at HSPF = 6.8 6-4-2013 - // same as ASHP_COPREG == 2 except for COP17 when HSPF < 8 - if (!IsSet( RSYS_COP47)) - rs_COP47 = 0.3225f * rs_HSPF + 0.9099f; - if (!IsSet( RSYS_COP17)) - rs_COP17 = rs_HSPF < 8 - ? 0.5183f * rs_HSPF - 1.7244f - : 0.2186f * rs_HSPF + 0.6734f; + // force COP17 = 1.8 at HSPF = 6.8 6-4-2013 + // same as ASHP_COPREG == 2 except for COP17 when HSPF < 8 + if (!IsSet(RSYS_COP47)) + rs_COP47 = 0.3225f * rs_HSPF + 0.9099f; + if (!IsSet(RSYS_COP17)) + rs_COP17 = + rs_HSPF < 8 ? 0.5183f * rs_HSPF - 1.7244f : 0.2186f * rs_HSPF + 0.6734f; #elif ASHP_COPREG == 6 - if (rs_IsPkgRoom()) - { rs_COP17 = 0.6870f * rs_COP47; - // rs_cap17 set above - } - else if (!rs_IsASHPHydronic()) - { // COP47: default as per ASHP_COPREG == 2 - // COP17 / COP35: adjust so HSPF is matched - if (!IsSet( RSYS_COP47)) - rs_COP47 = 0.3225f * rs_HSPF + 0.9099f; - if (!IsSet( RSYS_COP17)) - { rs_COP17 = 0.2186f * rs_HSPF + 0.6734f; - int iTry; - RC rc1; - const int nTry = 40; - for (iTry=0; iTry0 // call during autosize or later // redundant calls OK { - RC rc = RCOK; + RC rc = RCOK; - // inter-default cap95 (aka capC) <-> capH - // at least one is present (see rs_CkF) - if (!IsSet(RSYS_CAP95)) - rs_cap95 = WSHPPerf.whp_CapCFromCapH(rs_capH, IsSet(RSYS_CAPRATCH), rs_capRatCH); - else if (!IsSet(RSYS_CAPH)) - rs_capH = WSHPPerf.whp_CapHFromCapC(rs_cap95, IsSet(RSYS_CAPRATCH), rs_capRatCH); + // inter-default cap95 (aka capC) <-> capH + // at least one is present (see rs_CkF) + if (!IsSet(RSYS_CAP95)) + rs_cap95 = + WSHPPerf.whp_CapCFromCapH(rs_capH, IsSet(RSYS_CAPRATCH), rs_capRatCH); + else if (!IsSet(RSYS_CAPH)) + rs_capH = + WSHPPerf.whp_CapHFromCapC(rs_cap95, IsSet(RSYS_CAPRATCH), rs_capRatCH); - rs_fanHRtdH = rs_FanHRtdPerTon(rs_capH / 12000.f); + rs_fanHRtdH = rs_FanHRtdPerTon(rs_capH / 12000.f); - return rc; -} // RSYS::rs_SetupWSHP + return rc; +} // RSYS::rs_SetupWSHP //----------------------------------------------------------------------------- -RC RSYS::rs_SetupCHDHW() // check/set up combined heat / DWH +RC RSYS::rs_SetupCHDHW() // check/set up combined heat / DWH // call from rs_TopRSys2() // *after* rs_SetWorkingPtrs() // *after* topDHW() -- DHWSYS, DHWHEATER, etc must exist // returns RCOK iff CHDHW config is valid { - RC rc = RCOK; + RC rc = RCOK; - DHWSYS* pWS = rs_GetCHDHWSYS(); + DHWSYS *pWS = rs_GetCHDHWSYS(); - if (!pWS) - rc |= oer("Missing rsCHDHWSYS"); // impossible? due to prior checks - else - rc |= pWS->ws_CheckCHDHWConfig( this); + if (!pWS) + rc |= oer("Missing rsCHDHWSYS"); // impossible? due to prior checks + else + rc |= pWS->ws_CheckCHDHWConfig(this); - if (!rc) - { - rs_pCHDHW = new CHDHW( this); - float blowerEfficacy = float(rs_pCHDHW->hvt_GetRatedBlowerEfficacy()); - if (!IsSet(RSYS_FANPWRH)) - rs_fanPwrH = blowerEfficacy; - rc |= rs_pCHDHW->hvt_Init(rs_fanPwrH); + if (!rc) { + rs_pCHDHW = new CHDHW(this); + float blowerEfficacy = float(rs_pCHDHW->hvt_GetRatedBlowerEfficacy()); + if (!IsSet(RSYS_FANPWRH)) + rs_fanPwrH = blowerEfficacy; + rc |= rs_pCHDHW->hvt_Init(rs_fanPwrH); - rs_tdDesH = rs_pCHDHW->hvt_GetTRise(); - rs_capH = rs_pCHDHW->hvt_GetRatedCap(); + rs_tdDesH = rs_pCHDHW->hvt_GetTRise(); + rs_capH = rs_pCHDHW->hvt_GetRatedCap(); - // rated fan heat (unused?) - rs_fanHRtdH = blowerEfficacy * rs_pCHDHW->hvt_GetRatedBlowerAVF() * BtuperWh; - } + // rated fan heat (unused?) + rs_fanHRtdH = + blowerEfficacy * rs_pCHDHW->hvt_GetRatedBlowerAVF() * BtuperWh; + } - return rc; -} // RSYS::rs_SetupCHDHW + return rc; +} // RSYS::rs_SetupCHDHW //---------------------------------------------------------------------------- -void RSYS::rs_CurCapHtCHDHW() // current CHDHW heating cap etc +void RSYS::rs_CurCapHtCHDHW() // current CHDHW heating cap etc // sets rs_tCoilEW, rs_capHtMin, rs_capHt, and rs_speedFMin { - DHWSYS * pWS = rs_GetCHDHWSYS(); - rs_tCoilEW = pWS->ws_GetCHDHWTSupply(); - rs_pCHDHW->hvt_CapHtgMinMax(rs_tCoilEW, rs_capHtMin, rs_capHt); - rs_speedFMin = rs_capHtMin / rs_capHt; -} // RSYS::rs_CurCapHtCHDHW + DHWSYS *pWS = rs_GetCHDHWSYS(); + rs_tCoilEW = pWS->ws_GetCHDHWTSupply(); + rs_pCHDHW->hvt_CapHtgMinMax(rs_tCoilEW, rs_capHtMin, rs_capHt); + rs_speedFMin = rs_capHtMin / rs_capHt; +} // RSYS::rs_CurCapHtCHDHW //----------------------------------------------------------------------------- // helper performance point re btwxt setup // = temperature + array of perf values -struct VSPERFP -{ - enum { ppCAPHS, ppINPHS, ppCAPLS, ppINPLS, ppCOUNT }; - VSPERFP(float temp, float capHS, float COPHS, float capLS, float COPLS) - : pp_temp(temp) - { - pp_data[ppCAPHS] = capHS; - pp_data[ppINPHS] = abs(capHS / max(COPHS, .1f)); - pp_data[ppCAPLS] = capLS; - pp_data[ppINPLS] = abs(capLS / max(COPLS, .1f)); - } - - double pp_temp; // temp for this point, F - std::array< double, ppCOUNT> pp_data{ 0. }; // data - double operator [](int i) const { return pp_data[i]; } - static int NData() { return ppCOUNT; } -}; // struct VSPERFP +struct VSPERFP { + enum { ppCAPHS, ppINPHS, ppCAPLS, ppINPLS, ppCOUNT }; + VSPERFP(float temp, float capHS, float COPHS, float capLS, float COPLS) + : pp_temp(temp) { + pp_data[ppCAPHS] = capHS; + pp_data[ppINPHS] = abs(capHS / max(COPHS, .1f)); + pp_data[ppCAPLS] = capLS; + pp_data[ppINPLS] = abs(capLS / max(COPLS, .1f)); + } + + double pp_temp; // temp for this point, F + std::array pp_data{0.}; // data + double operator[](int i) const { return pp_data[i]; } + static int NData() { return ppCOUNT; } +}; // struct VSPERFP //----------------------------------------------------------------------------- -RC RSYS::rs_SetRunConstantsASHP() // finalize constant data for simulation +RC RSYS::rs_SetRunConstantsASHP() // finalize constant data for simulation // uses: rs_cap/COP 47/35/17/05 // sets: rs_inp17/35/47, rs_COP35 (if needed), slopes, performance map(s), // returns RCOK iff success { - RC rc = RCOK; + RC rc = RCOK; - // min speed defaults - // WHY: multiple calls possible with different rs_COP17 (at least) - if (!IsSet(RSYS_COPMIN47)) - rs_COPMin47 = rs_COP47; - if (!IsSet(RSYS_COPMIN17)) - rs_COPMin17 = rs_COP17; - if (!IsSet(RSYS_COPMIN05)) - rs_COPMin05 = rs_COP05; - - rs_inp47 = rs_cap47 / max( rs_COP47, .1f); // full speed input power, Btuh - rs_inp17 = rs_cap17 / max( rs_COP17, .1f); - - if (!IsSet( RSYS_COP35)) - { rs_inp35 = rs_Inp35Default( rs_inp47, rs_inp17); - rs_COP35 = rs_cap35 / max( rs_inp35, .1f); - } - else - rs_inp35 = rs_cap35 / max( rs_COP35, .1f); + // min speed defaults + // WHY: multiple calls possible with different rs_COP17 (at least) + if (!IsSet(RSYS_COPMIN47)) + rs_COPMin47 = rs_COP47; + if (!IsSet(RSYS_COPMIN17)) + rs_COPMin17 = rs_COP17; + if (!IsSet(RSYS_COPMIN05)) + rs_COPMin05 = rs_COP05; - rs_ASHPCapF[ 0] = (rs_cap47-rs_cap17) / (47.f-17.f); - rs_ASHPInpF[ 0] = (rs_inp47-rs_inp17) / (47.f-17.f); + rs_inp47 = rs_cap47 / max(rs_COP47, .1f); // full speed input power, Btuh + rs_inp17 = rs_cap17 / max(rs_COP17, .1f); - rs_ASHPCapF[ 1] = (rs_cap35-rs_cap17) / (35.f-17.f); - rs_ASHPInpF[ 1] = (rs_inp35-rs_inp17) / (35.f-17.f); + if (!IsSet(RSYS_COP35)) { + rs_inp35 = rs_Inp35Default(rs_inp47, rs_inp17); + rs_COP35 = rs_cap35 / max(rs_inp35, .1f); + } else + rs_inp35 = rs_cap35 / max(rs_COP35, .1f); - // Btwxt heating performance map setup - // currently (2-22) used for rs_IsVC() only - float capMin35 = rs_Cap35Default(rs_CapMin47(), rs_CapMin17()); + rs_ASHPCapF[0] = (rs_cap47 - rs_cap17) / (47.f - 17.f); + rs_ASHPInpF[0] = (rs_inp47 - rs_inp17) / (47.f - 17.f); - if (!IsSet(RSYS_COPMIN35)) - { - float inpMin47 = rs_CapMin47() / max(rs_COPMin47, .1f); - float inpMin17 = rs_CapMin17() / max(rs_COPMin17, .1f); - float inpMin35 = rs_Inp35Default(inpMin47, inpMin17); - rs_COPMin35 = capMin35 / max(inpMin35, .1f); - } + rs_ASHPCapF[1] = (rs_cap35 - rs_cap17) / (35.f - 17.f); + rs_ASHPInpF[1] = (rs_inp35 - rs_inp17) / (35.f - 17.f); + + // Btwxt heating performance map setup + // currently (2-22) used for rs_IsVC() only + float capMin35 = rs_Cap35Default(rs_CapMin47(), rs_CapMin17()); + + if (!IsSet(RSYS_COPMIN35)) { + float inpMin47 = rs_CapMin47() / max(rs_COPMin47, .1f); + float inpMin17 = rs_CapMin17() / max(rs_COPMin17, .1f); + float inpMin35 = rs_Inp35Default(inpMin47, inpMin17); + rs_COPMin35 = capMin35 / max(inpMin35, .1f); + } #if 0 float tx = FindLimitingPoint(5.f, rs_cap05, rs_COP05, 17.f, rs_cap17, rs_COP17); #endif - // integrated performance (including defrost degradation) - std::vector< VSPERFP> ppV; - ppV.emplace_back(5.f, rs_cap05, rs_COP05, rs_CapMin05(), rs_COPMin05); - ppV.emplace_back(17.f, rs_cap17, rs_COP17, rs_CapMin17(), rs_COPMin17); - ppV.emplace_back(35.f, rs_cap35, rs_COP35, capMin35, rs_COPMin35); - ppV.emplace_back(47.f, rs_cap47, rs_COP47, rs_CapMin47(), rs_COPMin47); - rc |= rs_SetupBtwxt("Heating w/defrost", rs_pRgiHtg[0], ppV); - - // steady-state performance (no defrost degradation) - // linear w/o 35 F point - // used iff temp in 17 - 45 F range - ppV.clear(); - ppV.emplace_back(17.f, rs_cap17, rs_COP17, rs_CapMin17(), rs_COPMin17); - ppV.emplace_back(47.f, rs_cap47, rs_COP47, rs_CapMin47(), rs_COPMin47); - rc |= rs_SetupBtwxt("Heating w/o defrost", rs_pRgiHtg[1], ppV); - - return rc; -} // RSYS::rs_SetRunConstantsASHP + // integrated performance (including defrost degradation) + std::vector ppV; + ppV.emplace_back(5.f, rs_cap05, rs_COP05, rs_CapMin05(), rs_COPMin05); + ppV.emplace_back(17.f, rs_cap17, rs_COP17, rs_CapMin17(), rs_COPMin17); + ppV.emplace_back(35.f, rs_cap35, rs_COP35, capMin35, rs_COPMin35); + ppV.emplace_back(47.f, rs_cap47, rs_COP47, rs_CapMin47(), rs_COPMin47); + rc |= rs_SetupBtwxt("Heating w/defrost", rs_pRgiHtg[0], ppV); + + // steady-state performance (no defrost degradation) + // linear w/o 35 F point + // used iff temp in 17 - 45 F range + ppV.clear(); + ppV.emplace_back(17.f, rs_cap17, rs_COP17, rs_CapMin17(), rs_COPMin17); + ppV.emplace_back(47.f, rs_cap47, rs_COP47, rs_CapMin47(), rs_COPMin47); + rc |= rs_SetupBtwxt("Heating w/o defrost", rs_pRgiHtg[1], ppV); + + return rc; +} // RSYS::rs_SetRunConstantsASHP //----------------------------------------------------------------------------- -RC RSYS::rs_SetupBtwxtClg() -{ - RC rc = RCOK; +RC RSYS::rs_SetupBtwxtClg() { + RC rc = RCOK; - // vector of performance points - // note cooling capacities <0 - std::vector< VSPERFP> ppV; - ppV.emplace_back(82.f, -rs_cap82, rs_COP82, -rs_CapMin82(), rs_COPMin82); - ppV.emplace_back(95.f, -rs_cap95, rs_COP95, -rs_CapMin95(), rs_COPMin95); - ppV.emplace_back(115.f, -rs_cap115, rs_COP115, -rs_CapMin115(), rs_COPMin115); + // vector of performance points + // note cooling capacities <0 + std::vector ppV; + ppV.emplace_back(82.f, -rs_cap82, rs_COP82, -rs_CapMin82(), rs_COPMin82); + ppV.emplace_back(95.f, -rs_cap95, rs_COP95, -rs_CapMin95(), rs_COPMin95); + ppV.emplace_back(115.f, -rs_cap115, rs_COP115, -rs_CapMin115(), rs_COPMin115); - // Populate Btwxt interpolator grid data from performance points - rc |= rs_SetupBtwxt("Cooling", rs_pRgiClg, ppV); + // Populate Btwxt interpolator grid data from performance points + rc |= rs_SetupBtwxt("Cooling", rs_pRgiClg, ppV); - return rc; -} // RSYS::rs_SetupBtwxtClg + return rc; +} // RSYS::rs_SetupBtwxtClg //----------------------------------------------------------------------------- -float RSYS::rs_CapHtCurSpeedF() const // heating cap at current speed +float RSYS::rs_CapHtCurSpeedF() const // heating cap at current speed // valid for rs_speedFMin <= rs_speedF <= 1 // returns non-cycling heating capacity (including fan and defrost aux), Btuh { -// cap = rs_capHtMin -// + (rs_capHt - rs_capHtMin) * (rs_speedF - rs_speedFMin) / (1.f - rs_speedFMin); -// and rs_speedFMin = rs_capHtMin / rs_capHt -// thus cap reduces to ... - return rs_capHt * rs_speedF; -} // RSYS::rs_CapHtCurSpeedF + // cap = rs_capHtMin + // + (rs_capHt - rs_capHtMin) * (rs_speedF - rs_speedFMin) / (1.f - + //rs_speedFMin); + // and rs_speedFMin = rs_capHtMin / rs_capHt + // thus cap reduces to ... + return rs_capHt * rs_speedF; +} // RSYS::rs_CapHtCurSpeedF //----------------------------------------------------------------------------- -float RSYS::rs_CapDfHtCurSpeedF() const -{ - float capDfHt = rs_speedF == 1.f - ? rs_capDfHt - : rs_capDfHtMin - + (rs_capDfHt - rs_capDfHtMin) * (rs_speedF - rs_speedFMin) / (1.f - rs_speedFMin); - return capDfHt; -} // RSYS::rs_capDfHtCurSpeedF() +float RSYS::rs_CapDfHtCurSpeedF() const { + float capDfHt = rs_speedF == 1.f + ? rs_capDfHt + : rs_capDfHtMin + (rs_capDfHt - rs_capDfHtMin) * + (rs_speedF - rs_speedFMin) / + (1.f - rs_speedFMin); + return capDfHt; +} // RSYS::rs_capDfHtCurSpeedF() //----------------------------------------------------------------------------- -float RSYS::rs_InpHtCurSpeedF() const -{ - float inpHt = rs_speedF == 1.f - ? rs_inpHt - : rs_inpHtMin - + (rs_inpHt - rs_inpHtMin) * (rs_speedF - rs_speedFMin) / (1.f - rs_speedFMin); - return inpHt; -} // RSYS::rs_InpHtCurSpeedF() +float RSYS::rs_InpHtCurSpeedF() const { + float inpHt = rs_speedF == 1.f + ? rs_inpHt + : rs_inpHtMin + (rs_inpHt - rs_inpHtMin) * + (rs_speedF - rs_speedFMin) / + (1.f - rs_speedFMin); + return inpHt; +} // RSYS::rs_InpHtCurSpeedF() //----------------------------------------------------------------------------- -RC RSYS::rs_SetupBtwxt( // init/populate btwxt for heating runtime interpolation - const char* tag, // identifying text for this interpolator (for messages) - Btwxt::RegularGridInterpolator*& pRgi, // returned: heap ptr to Btwxt interpolator object - // note: any prior contents deleted - const std::vector< VSPERFP> ppV) // performance point vector +RC RSYS::rs_SetupBtwxt( // init/populate btwxt for heating runtime interpolation + const char *tag, // identifying text for this interpolator (for messages) + Btwxt::RegularGridInterpolator * + &pRgi, // returned: heap ptr to Btwxt interpolator object + // note: any prior contents deleted + const std::vector ppV) // performance point vector { - RC rc = RCOK; + RC rc = RCOK; - delete pRgi; // delete prior if any + delete pRgi; // delete prior if any - int nD = VSPERFP::NData(); - int nPts = static_cast(ppV.size()); - std::vector< double> gridODB; - std::vector< std::vector> values; - values.resize(nD); + int nD = VSPERFP::NData(); + int nPts = static_cast(ppV.size()); + std::vector gridODB; + std::vector> values; + values.resize(nD); - for (int iPt = 0; iPt(this); + auto MX = std::make_shared(this); - // single grid variable = dry-bulb temp (allow linear extrapolation) - Btwxt::GridAxis dbtRange(gridODB, - Btwxt::InterpolationMethod::linear, Btwxt::ExtrapolationMethod::linear, - { -DBL_MAX, DBL_MAX }, "Dry-bulb temp", MX); + // single grid variable = dry-bulb temp (allow linear extrapolation) + Btwxt::GridAxis dbtRange(gridODB, Btwxt::InterpolationMethod::linear, + Btwxt::ExtrapolationMethod::linear, + {-DBL_MAX, DBL_MAX}, "Dry-bulb temp", MX); - std::vector dbt{ dbtRange}; + std::vector dbt{dbtRange}; - pRgi = new Btwxt::RegularGridInterpolator(dbt, values, tag, MX); + pRgi = new Btwxt::RegularGridInterpolator(dbt, values, tag, MX); #if 0 // test code @@ -5020,23 +4991,23 @@ RC RSYS::rs_SetupBtwxt( // init/populate btwxt for heating runtime interpolation result = (*rs_pRgiHtg)(targ); #endif - return rc; + return rc; -} // RSYS::rs_SetupBtwxt +} // RSYS::rs_SetupBtwxt //----------------------------------------------------------------------------- -RC RSYS::rs_GetPerfBtwxt( // retrieve performance info from btwxt map - Btwxt::RegularGridInterpolator* pRgi, // interpolation data - float tdbOut, // outdoor dry-bulb temp, F - float& cap, // returned: full speed capacity - float& inp, // returned: full speed input power, Btuh - float& capMin, // returned: min speed capacity, Btuh - float& inpMin) // returned: min speed input power, Btuh +RC RSYS::rs_GetPerfBtwxt( // retrieve performance info from btwxt map + Btwxt::RegularGridInterpolator *pRgi, // interpolation data + float tdbOut, // outdoor dry-bulb temp, F + float &cap, // returned: full speed capacity + float &inp, // returned: full speed input power, Btuh + float &capMin, // returned: min speed capacity, Btuh + float &inpMin) // returned: min speed input power, Btuh // Note: data generally net (include fan heat/power) per setup input // returns RCOK iff success // else ? { - RC rc = RCOK; -#if 0 && defined( _DEBUG) + RC rc = RCOK; +#if 0 && defined(_DEBUG) if (tdbOut > 33.f && tdbOut < 34.f) printf("\nCold"); #endif @@ -5045,424 +5016,422 @@ RC RSYS::rs_GetPerfBtwxt( // retrieve performance info from btwxt map std::vector< double> targ{ tdbOut, 999. }; auto result = (*pRgi)(targ); #else - std::vector< double> targ{ tdbOut }; - auto result = (*pRgi)(targ); -#endif - cap = result[VSPERFP::ppCAPHS]; - inp = result[VSPERFP::ppINPHS]; - capMin = result[VSPERFP::ppCAPLS]; - inpMin = result[VSPERFP::ppINPLS]; - return rc; -} // RSYS::rs_GetPerfBtwxt + std::vector targ{tdbOut}; + auto result = (*pRgi)(targ); +#endif + cap = result[VSPERFP::ppCAPHS]; + inp = result[VSPERFP::ppINPHS]; + capMin = result[VSPERFP::ppCAPLS]; + inpMin = result[VSPERFP::ppINPLS]; + return rc; +} // RSYS::rs_GetPerfBtwxt //----------------------------------------------------------------------------- -float RSYS::rs_CapEffASHP( // performance at current conditions - float tdbOut/*=-999.f*/, // outdoor dry-bulb temp, F - // default = rs_tdbOut (weather file value or expression) - int ashpModel /*=0*/, // alternative model - // 0=CSE, 1=MP, 2=ESL, 3=E+ - // +0x100: do NOT model defrost heating - // +0x200: ignore rs_fEffH (efficiency adjustment) - float fanHRtd /*=-1.f*/, // fan power included in rating, Btuh - // default = rs_fanHRtdH - float fanHOpr /*=-1.f*/, // operating fan power, Btuh - // default = rs_fanHeatH - float COPAdjF /*=-1.f*/) // COP adjustment factor - // default = rs_fEffH +float RSYS::rs_CapEffASHP( // performance at current conditions + float tdbOut /*=-999.f*/, // outdoor dry-bulb temp, F + // default = rs_tdbOut (weather file value or + // expression) + int ashpModel /*=0*/, // alternative model + // 0=CSE, 1=MP, 2=ESL, 3=E+ + // +0x100: do NOT model defrost heating + // +0x200: ignore rs_fEffH (efficiency adjustment) + float fanHRtd /*=-1.f*/, // fan power included in rating, Btuh + // default = rs_fanHRtdH + float fanHOpr /*=-1.f*/, // operating fan power, Btuh + // default = rs_fanHeatH + float COPAdjF /*=-1.f*/) // COP adjustment factor + // default = rs_fEffH // model does not depend on indoor conditions // sets rs_effHt, rs_capHt, and rs_capDfHt -// returns rs_capHt, Btuh = total heating capacity including fan and defrost if any -{ - if (tdbOut < -998.f) - tdbOut = rs_tdbOut; - if (fanHRtd < 0.f) - fanHRtd = rs_fanHRtdH; - if (fanHOpr < 0.f) - fanHOpr = rs_fanHeatH; - if (COPAdjF < 0.f) - COPAdjF = rs_fEffH; - - if (tdbOut < rs_ASHPLockOutT) - { rs_effHt = 0.f; // compressor is unavailable - rs_capHt = rs_capHtMin = fanHOpr; // compressor does nothing - rs_inpHt = rs_inpHtMin = 0.f; - } - else - { rs_effHt = rs_PerfASHP2(ashpModel, tdbOut, fanHRtd, rs_capHt, rs_inpHt, rs_capDfHt, - rs_capHtMin, rs_inpHtMin, rs_capDfHtMin, COPAdjF); - rs_capHt += fanHOpr; - rs_capHtMin += fanHOpr * rs_speedFMin; - } - return rs_CapHtCurSpeedF(); -} // RSYS::rs_CapEffASHP +// returns rs_capHt, Btuh = total heating capacity including fan and defrost if +// any +{ + if (tdbOut < -998.f) + tdbOut = rs_tdbOut; + if (fanHRtd < 0.f) + fanHRtd = rs_fanHRtdH; + if (fanHOpr < 0.f) + fanHOpr = rs_fanHeatH; + if (COPAdjF < 0.f) + COPAdjF = rs_fEffH; + + if (tdbOut < rs_ASHPLockOutT) { + rs_effHt = 0.f; // compressor is unavailable + rs_capHt = rs_capHtMin = fanHOpr; // compressor does nothing + rs_inpHt = rs_inpHtMin = 0.f; + } else { + rs_effHt = + rs_PerfASHP2(ashpModel, tdbOut, fanHRtd, rs_capHt, rs_inpHt, rs_capDfHt, + rs_capHtMin, rs_inpHtMin, rs_capDfHtMin, COPAdjF); + rs_capHt += fanHOpr; + rs_capHtMin += fanHOpr * rs_speedFMin; + } + return rs_CapHtCurSpeedF(); +} // RSYS::rs_CapEffASHP //----------------------------------------------------------------------------- -float RSYS::rs_CapEffASHP2() // performance at current conditions (no defaults) +float RSYS::rs_CapEffASHP2() // performance at current conditions (no defaults) // main simulation variant of rs_CapEffASHP() (see above) { - if (rs_tdbOut < rs_ASHPLockOutT) - { rs_effHt = 0.f; // compressor is unavailable - rs_capHt = rs_capHtMin = rs_fanHeatH; // compressor does nothing - rs_inpHt = rs_inpHtMin = 0.f; - } - else if (rs_capHt == 0.f) - { rs_effHt = rs_PerfASHP2( 0, rs_tdbOut, rs_fanHRtdH, rs_capHt, rs_inpHt, rs_capDfHt, - rs_capHtMin, rs_inpHtMin, rs_capDfHtMin, rs_fEffH); - // add operating fan heat/power - rs_capHt += rs_fanHeatH; - rs_capHtMin += rs_fanHeatH * rs_speedFMin; - } - return rs_CapHtCurSpeedF(); -} // RSYS::rs_CapEffASHP2 + if (rs_tdbOut < rs_ASHPLockOutT) { + rs_effHt = 0.f; // compressor is unavailable + rs_capHt = rs_capHtMin = rs_fanHeatH; // compressor does nothing + rs_inpHt = rs_inpHtMin = 0.f; + } else if (rs_capHt == 0.f) { + rs_effHt = + rs_PerfASHP2(0, rs_tdbOut, rs_fanHRtdH, rs_capHt, rs_inpHt, rs_capDfHt, + rs_capHtMin, rs_inpHtMin, rs_capDfHtMin, rs_fEffH); + // add operating fan heat/power + rs_capHt += rs_fanHeatH; + rs_capHtMin += rs_fanHeatH * rs_speedFMin; + } + return rs_CapHtCurSpeedF(); +} // RSYS::rs_CapEffASHP2 //----------------------------------------------------------------------------- -/*static*/ double RSYS::rs_CallCalcHSPF( void* pO, double& COP17) +/*static*/ double RSYS::rs_CallCalcHSPF(void *pO, double &COP17) // callback function for secant() // calcs HSPF using current COP17 // re search for COP17 consistent with specified HSPF // returns HSPF { - RSYS* pRS = (RSYS *)pO; - if (COP17 < 1.01) - COP17 = 1.01; // prevent impossible - pRS->rs_COP17 = COP17; - pRS->rs_SetRunConstantsASHP(); - int ashpModel = 0; - double hspf = pRS->rs_HSPFCheckASHP( ashpModel); - return hspf; -} // RSYS::rs_CallCalcHSPF + RSYS *pRS = (RSYS *)pO; + if (COP17 < 1.01) + COP17 = 1.01; // prevent impossible + pRS->rs_COP17 = COP17; + pRS->rs_SetRunConstantsASHP(); + int ashpModel = 0; + double hspf = pRS->rs_HSPFCheckASHP(ashpModel); + return hspf; +} // RSYS::rs_CallCalcHSPF //---------------------------------------------------------------------------- -RC RSYS::rs_HSPFMatchASHP() // adjust COP17 to yield user's HSPF +RC RSYS::rs_HSPFMatchASHP() // adjust COP17 to yield user's HSPF // uses rs_HSPF, rs_cap47, rs_COP47, rs_cap35, rs_COP35, rs_cap17 (at least) // adjusts rs_COP17 so calculated HSPF = rs_HSPF // returns RCOK iff COP17 found { - int rc = RCOK; - rs_SetRunConstantsASHP(); // ensure consistent starting point - int ashpModel = 0; - double hspf = rs_HSPFCheckASHP( ashpModel); - double COP17 = rs_COP17; - float dCOP17 = hspf > rs_HSPF ? -.1f : +.1f; - int ret = secant( rs_CallCalcHSPF, this, rs_HSPF, .001, - COP17, hspf, // x1, f1 - COP17 + dCOP17); // x2 (f2) - if (ret != 0) - rc = RCBAD; // search failed (caller must handle) + int rc = RCOK; + rs_SetRunConstantsASHP(); // ensure consistent starting point + int ashpModel = 0; + double hspf = rs_HSPFCheckASHP(ashpModel); + double COP17 = rs_COP17; + float dCOP17 = hspf > rs_HSPF ? -.1f : +.1f; + int ret = secant(rs_CallCalcHSPF, this, rs_HSPF, .001, COP17, hspf, // x1, f1 + COP17 + dCOP17); // x2 (f2) + if (ret != 0) + rc = RCBAD; // search failed (caller must handle) - return rc; + return rc; -} // RSYS::HSPFMatchASHP +} // RSYS::HSPFMatchASHP //---------------------------------------------------------------------------- -float RSYS::rs_HSPFCheckASHP( // calculate region 4 HSPF - int options/*=0*/) // low bits = ashpModel - // 0x100 = DbPrintf info for all bins +float RSYS::rs_HSPFCheckASHP( // calculate region 4 HSPF + int options /*=0*/) // low bits = ashpModel + // 0x100 = DbPrintf info for all bins // testing aid, "back calculates" HSPF from ASHP ratings // method = AHRI 210/240 single speed // Prerequisite: rs_CalcInputsAndSlopesASHP() or equivalent // (sets values used by rs_CapEffASHP()) // returns HSPF calculated from RSYS properties { -// bin hours for region 4 -static int nHrReg4[] = -// 62 57 52 ... -{ 297,250,232,209,225,245,283,196,124, 81, 58, 29, 14, 5, 2, 0 }; -static double fHrReg4[] = -// 62 57 52 ... -{ 0.132,0.111,0.103,0.093,0.100,0.109,0.126,0.087,0.055,0.036,0.026,0.013,0.006,0.002,0.001, 0. }; - - if (!rs_IsASHP()) - return -1.f; // not ASHP! - - int ashpModel = options & 0x1F; - int bDoPrint = (options & 0x100) != 0; - - [[ maybe_unused]] int nHrTot = 0; // total bin hours (s/b 2250 for reg 4) - const float tDbIn = 65.f; // indoor design temp - const float tDbDes = 5.f; // outdoor design temp - const float DHR // design heating requirement per AHRI - = rs_DHR( rs_cap47 * (tDbIn - tDbDes) / 60.f); - double outTot = 0.; // total heat delivered (including fan heat), Btu - double inpTot = 0.; // total input (including fan), Btu - const float C = 0.77f; // AHRI "correction factor" - const float cd = rs_CdH; // cyclic degradation coefficient - const float tOff = 0.f; // low temp cut-out "off" temp - const float tOn = 5.f; // low temp cut-out "on" temp - float lockOutTSave = rs_ASHPLockOutT; - rs_ASHPLockOutT = -999.f; // disable our lockout model - // handled via delta in loop below - - rs_CapEffASHP( 47.f, ashpModel+0x100); // debug aid (check values with fan power) - rs_CapEffASHP( 17.f, ashpModel+0x100); - - if (bDoPrint) - DbPrintf( "\nHSPF calc model=%d\n", ashpModel); - - for (int iBin=0; nHrReg4[ iBin] > 0; iBin++) - { nHrTot += nHrReg4[ iBin]; - float tDbBin = float( 62 - iBin*5); - - rs_CapEffASHP( tDbBin, ashpModel+0x100, 0.f, 0.f, 1.f); // derive capacity and efficiency for bin temp - // no fan heat adjustments - // no COP adjust - // load - float f = (tDbBin < tDbIn && tDbDes < tDbIn) - ? (tDbIn - tDbBin) / (tDbIn - tDbDes) - : 0.f; - double BL = f * C * DHR; // building heating load (Eqn 4.2-2) - // always >= 0 - - // other factors - double X = rs_capHt > 0.f - ? min( BL/rs_capHt, 1.) - : double( BL > 0.); // 1. iff load else 0. - double PLF = 1. - cd * ( 1. - X); - - double Eh = rs_effHt > 0. - ? rs_capHt / rs_effHt // input, Btuh - : 0.; - [[maybe_unused]] double EhW = Eh / 3.413; // input, W (check value) - - // low temp cut-out - float delta = tDbBin <= tOff || rs_effHt < 1. ? 0.f - : tDbBin > tOn ? 1.f - : 0.5f; - - double eh = X * Eh * delta / PLF; - double ehBin = eh * fHrReg4[ iBin]; - - double RH = BL - X * delta * rs_capHt; // backup - double RHBin = RH * fHrReg4[ iBin]; - - double inpBin = ehBin + RHBin; - [[maybe_unused]] double inpBinWh = inpBin / 3.413; - - double outBin = BL * fHrReg4[ iBin]; - inpTot += inpBin; - outTot += outBin; - if (bDoPrint) - DbPrintf( "%4.f %6.f %6.f %6.3f %6.f %6.f %6.f\n", - tDbBin, outBin, rs_capHt, rs_effHt, ehBin, RHBin, inpBin); - } - - double inpTotWh = inpTot/3.413; - float HSPF = outTot / inpTotWh; - - if (bDoPrint) - DbPrintf( "\noutTot=%0.f inpTot=%0.f inpTotWh=%0.f HSPF=%0.3f\n", - outTot, inpTot, inpTotWh, HSPF); - - rs_ASHPLockOutT = lockOutTSave; // restore lockout input - - return HSPF; - -} // RSYS::rs_HSPFCheckASHP + // bin hours for region 4 + static int nHrReg4[] = + // 62 57 52 ... + {297, 250, 232, 209, 225, 245, 283, 196, 124, 81, 58, 29, 14, 5, 2, 0}; + static double fHrReg4[] = + // 62 57 52 ... + {0.132, 0.111, 0.103, 0.093, 0.100, 0.109, 0.126, 0.087, + 0.055, 0.036, 0.026, 0.013, 0.006, 0.002, 0.001, 0.}; + + if (!rs_IsASHP()) + return -1.f; // not ASHP! + + int ashpModel = options & 0x1F; + int bDoPrint = (options & 0x100) != 0; + + [[maybe_unused]] int nHrTot = 0; // total bin hours (s/b 2250 for reg 4) + const float tDbIn = 65.f; // indoor design temp + const float tDbDes = 5.f; // outdoor design temp + const float DHR // design heating requirement per AHRI + = rs_DHR(rs_cap47 * (tDbIn - tDbDes) / 60.f); + double outTot = 0.; // total heat delivered (including fan heat), Btu + double inpTot = 0.; // total input (including fan), Btu + const float C = 0.77f; // AHRI "correction factor" + const float cd = rs_CdH; // cyclic degradation coefficient + const float tOff = 0.f; // low temp cut-out "off" temp + const float tOn = 5.f; // low temp cut-out "on" temp + float lockOutTSave = rs_ASHPLockOutT; + rs_ASHPLockOutT = -999.f; // disable our lockout model + // handled via delta in loop below + + rs_CapEffASHP(47.f, + ashpModel + 0x100); // debug aid (check values with fan power) + rs_CapEffASHP(17.f, ashpModel + 0x100); + + if (bDoPrint) + DbPrintf("\nHSPF calc model=%d\n", ashpModel); + + for (int iBin = 0; nHrReg4[iBin] > 0; iBin++) { + nHrTot += nHrReg4[iBin]; + float tDbBin = float(62 - iBin * 5); + + rs_CapEffASHP(tDbBin, ashpModel + 0x100, 0.f, 0.f, + 1.f); // derive capacity and efficiency for bin temp + // no fan heat adjustments + // no COP adjust + // load + float f = (tDbBin < tDbIn && tDbDes < tDbIn) + ? (tDbIn - tDbBin) / (tDbIn - tDbDes) + : 0.f; + double BL = f * C * DHR; // building heating load (Eqn 4.2-2) + // always >= 0 + + // other factors + double X = rs_capHt > 0.f ? min(BL / rs_capHt, 1.) + : double(BL > 0.); // 1. iff load else 0. + double PLF = 1. - cd * (1. - X); + + double Eh = rs_effHt > 0. ? rs_capHt / rs_effHt // input, Btuh + : 0.; + [[maybe_unused]] double EhW = Eh / 3.413; // input, W (check value) + + // low temp cut-out + float delta = tDbBin <= tOff || rs_effHt < 1. ? 0.f + : tDbBin > tOn ? 1.f + : 0.5f; + + double eh = X * Eh * delta / PLF; + double ehBin = eh * fHrReg4[iBin]; + + double RH = BL - X * delta * rs_capHt; // backup + double RHBin = RH * fHrReg4[iBin]; + + double inpBin = ehBin + RHBin; + [[maybe_unused]] double inpBinWh = inpBin / 3.413; + + double outBin = BL * fHrReg4[iBin]; + inpTot += inpBin; + outTot += outBin; + if (bDoPrint) + DbPrintf("%4.f %6.f %6.f %6.3f %6.f %6.f %6.f\n", tDbBin, outBin, + rs_capHt, rs_effHt, ehBin, RHBin, inpBin); + } + + double inpTotWh = inpTot / 3.413; + float HSPF = outTot / inpTotWh; + + if (bDoPrint) + DbPrintf("\noutTot=%0.f inpTot=%0.f inpTotWh=%0.f HSPF=%0.3f\n", outTot, + inpTot, inpTotWh, HSPF); + + rs_ASHPLockOutT = lockOutTSave; // restore lockout input + + return HSPF; + +} // RSYS::rs_HSPFCheckASHP //------------------------------------------------------------------------------- -/*static*/ float RSYS::rs_DHR( // AHRI nominal DHR - float capHt) // heating capacity, Btuh +/*static*/ float RSYS::rs_DHR( // AHRI nominal DHR + float capHt) // heating capacity, Btuh // rounds capacity per AHRI procedures, see AHRI 210/240 2008 section 4.2 ish // used *only* for checking ratings etc (not in simulation) // returns rounded DHR, Btuh { -#if 1 // creative extension to handle small capacities, 10-16-2014 - float roCap = capHt <= 5000.f ? 1000.f - : capHt <= 40000.f ? 5000.f +#if 1 // creative extension to handle small capacities, 10-16-2014 + float roCap = capHt <= 5000.f ? 1000.f + : capHt <= 40000.f ? 5000.f : capHt <= 110000.f ? 10000.f - : 20000.f; - float DHR = max( floor( 0.5f + capHt / roCap) * roCap, 1000.f); + : 20000.f; + float DHR = max(floor(0.5f + capHt / roCap) * roCap, 1000.f); #else -x float roCap = capHt <= 40000.f ? 5000.f -x : capHt <= 110000.f ? 10000.f -x : 20000.f; -x float DHR = max( floor( 0.5f + capHt / roCap) * roCap, 5000.f); + x float roCap = capHt <= 40000.f ? 5000.f x + : capHt <= 110000.f ? 10000.f x + : 20000.f; + x float DHR = max(floor(0.5f + capHt / roCap) * roCap, 5000.f); #endif - return DHR; -} // float RSYS_rsDHR + return DHR; +} // float RSYS_rsDHR //------------------------------------------------------------------------------- -/*static*/ double RSYS::rs_CallCalcCapHt( void* pO, double& cap47) +/*static*/ double RSYS::rs_CallCalcCapHt(void *pO, double &cap47) // callback function for secant() // resizes per cap47 // calcs capacity (including any defrost) at rs_tdbOut // returns capHt, Btuh { - RSYS* pRS = (RSYS *)pO; - double capHt = pRS->rs_CapHtForCap47( cap47); - return capHt; -} // RSYS::rs_CallCalcHSPF + RSYS *pRS = (RSYS *)pO; + double capHt = pRS->rs_CapHtForCap47(cap47); + return capHt; +} // RSYS::rs_CallCalcHSPF //------------------------------------------------------------------------------- -double RSYS::rs_CapHtForCap47( // inner function re ASHP sizing - float cap47) // proposed cap47, Btuh +double RSYS::rs_CapHtForCap47( // inner function re ASHP sizing + float cap47) // proposed cap47, Btuh // note: resizes ASHP, alters many RSYS values // returns total heating output at rs_tdbOut, Btuh { - rs_cap47 = cap47; - rs_SetupCapH( -1.f, 1); // set derived values from rs_cap47 - // calls rs_SetupASHP() -> sets cap17 and cap35 - // sets rs_amfH, rs_fanHeatH - int ashpModel = 0; - float capHt, inpHt, capDfHt, capHtMin, inpHtMin, capDfHtMin; - rs_PerfASHP2(ashpModel, rs_tdbOut, rs_fanHRtdH, - capHt, inpHt, capDfHt, capHtMin, inpHtMin, capDfHtMin); - return capHt + rs_fanHeatH; -} // RSYS::rs_CapHtForCap47 + rs_cap47 = cap47; + rs_SetupCapH(-1.f, 1); // set derived values from rs_cap47 + // calls rs_SetupASHP() -> sets cap17 and cap35 + // sets rs_amfH, rs_fanHeatH + int ashpModel = 0; + float capHt, inpHt, capDfHt, capHtMin, inpHtMin, capDfHtMin; + rs_PerfASHP2(ashpModel, rs_tdbOut, rs_fanHRtdH, capHt, inpHt, capDfHt, + capHtMin, inpHtMin, capDfHtMin); + return capHt + rs_fanHeatH; +} // RSYS::rs_CapHtForCap47 //------------------------------------------------------------------------------- -RC RSYS::rs_SizeHtASHP( // size ASHP - float dsnLoad, // required output, Btuh - float tdbOut) // outdoor dry-bulb temp, F - -// sets rs_cap47 (and dependent members) such that -// heating capacity at tdbOut matches design load - -{ - rs_tdbOut = tdbOut; - RC rc = RCOK; - - // calculate estimated cap47 ignoring defrost and fan power - // use standard straight line fits solved for cap47 - float r17 = min( rs_CapRat1747(), 1.f); // cap17/cap47 - float tdbM17 = tdbOut - 17.f; - float tF; - float d; - if (rs_HasDefrost() && tdbOut > 17.f && tdbOut < 45.f) - { // defrost regime: cap based on 17 - 35 slope - // pkg ASHP does not defrost - tF = tdbM17 / (35.f - 17.f); - float r35 = 0.9f * (r17 + 0.6f*(1.f - r17)); // cap35/cap47 - d = (r17 + tF * (r35 - r17)); - } - else - { // non-defrost: cap based on 17 - 47 slope - tF = tdbM17 / (47.f - 17.f); - d = (r17 + tF * (1.f - r17)); - } - float cap47Est = dsnLoad / d; - - // finalize cap47 result using secant() - // fan power and defrost included - double cap47 = cap47Est; - double f1 = DBL_MIN; - int ret = secant( rs_CallCalcCapHt, this, dsnLoad, 20.f, - cap47, f1, // x1, f1 - cap47+100.); // x2 (f2) - if (ret != 0) - { rc = err( ERR, "RSYS '%s': Cap47 for design load fail (return code=%d)", Name(), ret); - cap47 = cap47Est; - } - rs_cap47 = cap47; // redundant? - - rs_DefaultCapNomsIf(); // update nominal capacities (no calc effect) +RC RSYS::rs_SizeHtASHP( // size ASHP + float dsnLoad, // required output, Btuh + float tdbOut) // outdoor dry-bulb temp, F - return rc; +// sets rs_cap47 (and dependent members) such that +// heating capacity at tdbOut matches design load -} // RSYS::rs_SizeHtASHP +{ + rs_tdbOut = tdbOut; + RC rc = RCOK; + + // calculate estimated cap47 ignoring defrost and fan power + // use standard straight line fits solved for cap47 + float r17 = min(rs_CapRat1747(), 1.f); // cap17/cap47 + float tdbM17 = tdbOut - 17.f; + float tF; + float d; + if (rs_HasDefrost() && tdbOut > 17.f && + tdbOut < 45.f) { // defrost regime: cap based on 17 - 35 slope + // pkg ASHP does not defrost + tF = tdbM17 / (35.f - 17.f); + float r35 = 0.9f * (r17 + 0.6f * (1.f - r17)); // cap35/cap47 + d = (r17 + tF * (r35 - r17)); + } else { // non-defrost: cap based on 17 - 47 slope + tF = tdbM17 / (47.f - 17.f); + d = (r17 + tF * (1.f - r17)); + } + float cap47Est = dsnLoad / d; + + // finalize cap47 result using secant() + // fan power and defrost included + double cap47 = cap47Est; + double f1 = DBL_MIN; + int ret = secant(rs_CallCalcCapHt, this, dsnLoad, 20.f, cap47, f1, // x1, f1 + cap47 + 100.); // x2 (f2) + if (ret != 0) { + rc = err(ERR, "RSYS '%s': Cap47 for design load fail (return code=%d)", + Name(), ret); + cap47 = cap47Est; + } + rs_cap47 = cap47; // redundant? + + rs_DefaultCapNomsIf(); // update nominal capacities (no calc effect) + + return rc; + +} // RSYS::rs_SizeHtASHP //------------------------------------------------------------------------------- -void RSYS::rs_DefaultCapNomsIf() // nominal capacities +void RSYS::rs_DefaultCapNomsIf() // nominal capacities // update nominal heating and cooling capacities // sets rs_capNomH and rs_capNomC if not fixed by user input // rs_capNomH and rs_capNomC are reporting only { -// uses non-NaN values if available, else arbitrary default -// autosized rs_capXXX are initially NaN -// does NOT set FsVAL field status, tricky to know when values are final. + // uses non-NaN values if available, else arbitrary default + // autosized rs_capXXX are initially NaN + // does NOT set FsVAL field status, tricky to know when values are final. - if (!IsVal( RSYS_CAPNOMH)) - rs_capNomH = rs_IsASHP() - ? ifNotNaN( rs_cap47, 18000.f) - : ifNotNaN( rs_capH, 18000.f); + if (!IsVal(RSYS_CAPNOMH)) + rs_capNomH = + rs_IsASHP() ? ifNotNaN(rs_cap47, 18000.f) : ifNotNaN(rs_capH, 18000.f); - if (!IsVal( RSYS_CAPNOMC)) - rs_capNomC = ifNotNaN( rs_cap95, 18000.f); + if (!IsVal(RSYS_CAPNOMC)) + rs_capNomC = ifNotNaN(rs_cap95, 18000.f); -} // RSYS::rs_DefaultCapNomsIf() +} // RSYS::rs_DefaultCapNomsIf() //------------------------------------------------------------------------------- -void RSYS::rs_SetModeAndSpeedF( // set mode / clear prior-step results - int rsModeNew, // new mode - float speedF /*=1.f*/) // new speed +void RSYS::rs_SetModeAndSpeedF( // set mode / clear prior-step results + int rsModeNew, // new mode + float speedF /*=1.f*/) // new speed // uses rs_mode to skip unneeded { -#if defined( _DEBUG) - if (speedF < 0.f || speedF > 1.f) - printf("\nrs_SetModeAndClear() speedF=%0.2f", speedF); +#if defined(_DEBUG) + if (speedF < 0.f || speedF > 1.f) + printf("\nrs_SetModeAndClear() speedF=%0.2f", speedF); #endif - rs_speedF = speedF; - rs_mode = rsModeNew; -} // RSYS::rs_SetModeAndSpeedF + rs_speedF = speedF; + rs_mode = rsModeNew; +} // RSYS::rs_SetModeAndSpeedF //----------------------------------------------------------------------------- void RSYS::rs_ClearSubhrResults( - int options /*= 0*/) // option bits - // all 0: full clear (e.g. subhr beg) - // 1: limited clear for speed retry -{ - rs_amf = 0.; - rs_amfReq[0] = rs_amfReq[1] = 0.; - rs_znLoad[0] = rs_znLoad[1] = 0.; - rs_asRet.as_Init(); - rs_asIn.as_Init(); - rs_twbIn = 0.; - rs_asOut.as_Init(); - rs_asSup.as_Init(); - rs_asOutAux.as_Init(); - rs_asSupAux.as_Init(); - - if (options & 1) - return; - - // all modes - rs_loadF = rs_PLR = rs_runF = rs_speedF = rs_runFAux = rs_PLF = rs_capSenNetFS = 0.f; - rs_outSen = rs_outLat = rs_outFan = rs_outAux = rs_outDefrost - = rs_outSenTot = rs_inPrimary = rs_inFan = rs_inAux = rs_inDefrost = 0.; - - // heating - rs_effHt = rs_COPHtAdj = rs_tCoilEW = 0.f; - rs_capHt = rs_capHtMin = rs_capDfHt = rs_capDfHtMin = 0.f; - - // cooling - rs_tdbCoilIn = 0.f; - rs_twbCoilIn = 0.f; - rs_SHR = 0.f; - rs_fCondCap = 0.f; - rs_capTotCt = rs_capSenCt = rs_capLatCt = 0.f; - rs_fCondSEER = rs_fCondEER = 0.f; - rs_SEERnf = rs_EERnf = rs_EERt = rs_effCt = 0.f; - -} // RSYS::rs_ClearSubhrResults + int options /*= 0*/) // option bits + // all 0: full clear (e.g. subhr beg) + // 1: limited clear for speed retry +{ + rs_amf = 0.; + rs_amfReq[0] = rs_amfReq[1] = 0.; + rs_znLoad[0] = rs_znLoad[1] = 0.; + rs_asRet.as_Init(); + rs_asIn.as_Init(); + rs_twbIn = 0.; + rs_asOut.as_Init(); + rs_asSup.as_Init(); + rs_asOutAux.as_Init(); + rs_asSupAux.as_Init(); + + if (options & 1) + return; + + // all modes + rs_loadF = rs_PLR = rs_runF = rs_speedF = rs_runFAux = rs_PLF = + rs_capSenNetFS = 0.f; + rs_outSen = rs_outLat = rs_outFan = rs_outAux = rs_outDefrost = rs_outSenTot = + rs_inPrimary = rs_inFan = rs_inAux = rs_inDefrost = 0.; + + // heating + rs_effHt = rs_COPHtAdj = rs_tCoilEW = 0.f; + rs_capHt = rs_capHtMin = rs_capDfHt = rs_capDfHtMin = 0.f; + + // cooling + rs_tdbCoilIn = 0.f; + rs_twbCoilIn = 0.f; + rs_SHR = 0.f; + rs_fCondCap = 0.f; + rs_capTotCt = rs_capSenCt = rs_capLatCt = 0.f; + rs_fCondSEER = rs_fCondEER = 0.f; + rs_SEERnf = rs_EERnf = rs_EERt = rs_effCt = 0.f; + +} // RSYS::rs_ClearSubhrResults //---------------------------------------------------------------------------- -int RSYS::rs_IsModeAvailable( // mode availability - int rsMode) const // desired mode (rsmHEAT, rsmCOOL, rsmOAV) +int RSYS::rs_IsModeAvailable( // mode availability + int rsMode) const // desired mode (rsmHEAT, rsmCOOL, rsmOAV) // returns 1 iff RSYS can operate in desired mode // 0 if not (mode fixed and different) // -1 if not (system off) { - int ret; - if (rs_mode != rsmOFF) - ret = rs_mode == rsMode; // RSYS mode already known - // OK iff desired mode is same - else - { - switch (rs_modeCtrl) - { - case C_RSYSMODECTRLCH_HEAT: - ret = rsMode == rsmHEAT && rs_CanHeat(); - break; - - case C_RSYSMODECTRLCH_COOL: - ret = rsMode == rsmCOOL && rs_CanCool(); - break; - - case C_RSYSMODECTRLCH_AUTO: - // control is auto and system is currently off - ret = (rsMode == rsmHEAT && rs_CanHeat()) - || (rsMode == rsmCOOL && rs_CanCool()) - || (rsMode == rsmOAV && rs_CanOAV()); - break; - - case C_RSYSMODECTRLCH_OFF: - default: - ret = -1; // off: no can do - } - } - - return ret; -} // RSYS::rs_IsModeAvailable + int ret; + if (rs_mode != rsmOFF) + ret = rs_mode == rsMode; // RSYS mode already known + // OK iff desired mode is same + else { + switch (rs_modeCtrl) { + case C_RSYSMODECTRLCH_HEAT: + ret = rsMode == rsmHEAT && rs_CanHeat(); + break; + + case C_RSYSMODECTRLCH_COOL: + ret = rsMode == rsmCOOL && rs_CanCool(); + break; + + case C_RSYSMODECTRLCH_AUTO: + // control is auto and system is currently off + ret = (rsMode == rsmHEAT && rs_CanHeat()) || + (rsMode == rsmCOOL && rs_CanCool()) || + (rsMode == rsmOAV && rs_CanOAV()); + break; + + case C_RSYSMODECTRLCH_OFF: + default: + ret = -1; // off: no can do + } + } + + return ret; +} // RSYS::rs_IsModeAvailable //---------------------------------------------------------------------------- -void RSYS::rs_EnteringAirState() // RSYS entering air state +void RSYS::rs_EnteringAirState() // RSYS entering air state // rs_mode s/b set by caller (and not rsmOFF) // rs_amf s/b set by caller and > 0 @@ -5478,272 +5447,263 @@ void RSYS::rs_EnteringAirState() // RSYS entering air state // rs_twbIn = corresponding wet bulb temp, F { - if (rs_mode == rsmOAV) - // outdoor air vent: set state from possibly modified outdoor conditions - // ignore possibility of impossible humidity ratio - rs_asRet.as_Set( rs_OAVTdbInlet, Top.wOSh); - - else - { ZNR* zp; - AIRFLOW afRet; // air flow at return duct inlet - // magically mixed from all zones - // served by this system - RLUPC( ZrB, zp, rs_IsZoneServed( zp)) - { // determine return air state - // mix zone states weighted by prior flow - // use area as proxy if prior flow not known - double amfX = rs_mode == rs_modeLs - ? zp->zn_rsAmfRetLs - : zp->i.znArea; - // return grille temp = zone air temp from last step - afRet.af_AccumDry( amfX, zp->tzls, zp->wzls); -#if defined( _DEBUG) - if ((afRet.as_tdb < 30. || afRet.as_tdb > 110.f) && !rs_IsAutoSizing()) - orWarn("dubious return temp (%.1f F)", afRet.as_tdb); -#endif - } - rs_asRet.as_Set( afRet); // state = flow w/o amf - } - - // adjust state for return duct - int iDS = rs_Dsi( 1); // idx of ductseg - rs_asIn = iDS > 0 - ? DsR[ iDS].ds_CalcFL( rs_asRet, rs_amf) - : rs_asRet; - - -#if defined( _DEBUG) - if (rs_mode != rsmOAV - && (rs_asIn.as_tdb < 30. || rs_asIn.as_tdb > 110.f)) - { if (!Top.isWarmup) - orWarn( "Dubious asIn tdb (%0.2f)", rs_asIn.as_tdb); - // repeat return duct calc (debug aid) - // int iDS = rs_Dsi( 1); above - if (iDS > 0) - rs_asIn = DsR[ iDS].ds_CalcFL( rs_asRet, rs_amf); - } + if (rs_mode == rsmOAV) + // outdoor air vent: set state from possibly modified outdoor conditions + // ignore possibility of impossible humidity ratio + rs_asRet.as_Set(rs_OAVTdbInlet, Top.wOSh); + + else { + ZNR *zp; + AIRFLOW afRet; // air flow at return duct inlet + // magically mixed from all zones + // served by this system + RLUPC(ZrB, zp, rs_IsZoneServed(zp)) { // determine return air state + // mix zone states weighted by prior flow + // use area as proxy if prior flow not known + double amfX = rs_mode == rs_modeLs ? zp->zn_rsAmfRetLs : zp->i.znArea; + // return grille temp = zone air temp from last step + afRet.af_AccumDry(amfX, zp->tzls, zp->wzls); +#if defined(_DEBUG) + if ((afRet.as_tdb < 30. || afRet.as_tdb > 110.f) && !rs_IsAutoSizing()) + orWarn("dubious return temp (%.1f F)", afRet.as_tdb); #endif + } + rs_asRet.as_Set(afRet); // state = flow w/o amf + } - if (IsSet( RSYS_RHINTEST)) - { rs_asIn.as_SetWFromRh( rs_rhInTest); // testing aid: set w from rh (tdb unchanged) - rs_rhIn = rs_rhInTest; - } - else - rs_rhIn = rs_asIn.as_RelHum(); // else calc rh - rs_twbIn = rs_asIn.as_Twb(); + // adjust state for return duct + int iDS = rs_Dsi(1); // idx of ductseg + rs_asIn = iDS > 0 ? DsR[iDS].ds_CalcFL(rs_asRet, rs_amf) : rs_asRet; -} // RSYS::rs_EnteringAirState +#if defined(_DEBUG) + if (rs_mode != rsmOAV && (rs_asIn.as_tdb < 30. || rs_asIn.as_tdb > 110.f)) { + if (!Top.isWarmup) + orWarn("Dubious asIn tdb (%0.2f)", rs_asIn.as_tdb); + // repeat return duct calc (debug aid) + // int iDS = rs_Dsi( 1); above + if (iDS > 0) + rs_asIn = DsR[iDS].ds_CalcFL(rs_asRet, rs_amf); + } +#endif + + if (IsSet(RSYS_RHINTEST)) { + rs_asIn.as_SetWFromRh( + rs_rhInTest); // testing aid: set w from rh (tdb unchanged) + rs_rhIn = rs_rhInTest; + } else + rs_rhIn = rs_asIn.as_RelHum(); // else calc rh + rs_twbIn = rs_asIn.as_Twb(); + +} // RSYS::rs_EnteringAirState //---------------------------------------------------------------------------- -int RSYS::rs_SupplyAirState( // current conditioning capabilities - int rsMode, // desired mode (do not call with rsMode == rsmOFF) - float speedF /*=1.f*/) // speed fraction - // sets rs_speedF to max( speedF, min +int RSYS::rs_SupplyAirState( // current conditioning capabilities + int rsMode, // desired mode (do not call with rsMode == rsmOFF) + float speedF /*=1.f*/) // speed fraction + // sets rs_speedF to max( speedF, min // NOTE: many side effects -- changes rs_mode, clears prior step, ... // returns 3: mode is available, rs_asSup set // 2: mode is being actively autosized, rs_asSup set -// 1: mode is inactive (another mode is being actively autosized) -// 0: mode not available -{ - // TODO: not returning right value! - - float speedFWas = rs_speedF; - - // mode availability - rs_amf = 0.; - if (rs_IsModeAvailable(rsMode) <= 0) - return 0; - - int rsAvail = rs_SetAmf(rsMode, speedF); - if (rsAvail < 2) - return rsAvail; - - if (rs_mode == rsmOFF || speedF != speedFWas) // if was off or speed has changed - { // first zone requesting this mode during this step - rs_SetModeAndSpeedF(rsMode, speedF); - - // determine entering air state - // does return duct calcs, uses rs_amf - rs_EnteringAirState(); - - rs_asOut = rs_asIn; // init entering state - - int auszMode = rs_IsAutoSizing(); - if (rs_mode == rsmCOOL) - { ++rs_calcCount[1]; - rs_CoolingOutletAirState(auszMode); - } - else if (rs_mode == rsmHEAT) - { ++rs_calcCount[0]; - // rs_asOut = rs_asIn; // above - rs_HeatingOutletAirState( auszMode); - } - else - { // OAV: add fan heat - // rs_asOut = rs_asIn; // above - rs_asOut.as_AddQSen2( rs_fanHeatOAV, rs_amf); - } - - // outlet air state rs_asOut now known for all cases - // calc supply air state at zone(s) - rs_asSup = rs_asOut; - rs_SupplyDSEAndDucts(rs_asSup); - - } - // else supply air state known - - return rsAvail; -} // RSYS::rs_SupplyAirState +// 1: mode is inactive (another mode is being actively +//autosized) 0: mode not available +{ + // TODO: not returning right value! + + float speedFWas = rs_speedF; + + // mode availability + rs_amf = 0.; + if (rs_IsModeAvailable(rsMode) <= 0) + return 0; + + int rsAvail = rs_SetAmf(rsMode, speedF); + if (rsAvail < 2) + return rsAvail; + + if (rs_mode == rsmOFF || + speedF != speedFWas) // if was off or speed has changed + { // first zone requesting this mode during this step + rs_SetModeAndSpeedF(rsMode, speedF); + + // determine entering air state + // does return duct calcs, uses rs_amf + rs_EnteringAirState(); + + rs_asOut = rs_asIn; // init entering state + + int auszMode = rs_IsAutoSizing(); + if (rs_mode == rsmCOOL) { + ++rs_calcCount[1]; + rs_CoolingOutletAirState(auszMode); + } else if (rs_mode == rsmHEAT) { + ++rs_calcCount[0]; + // rs_asOut = rs_asIn; // above + rs_HeatingOutletAirState(auszMode); + } else { // OAV: add fan heat + // rs_asOut = rs_asIn; // above + rs_asOut.as_AddQSen2(rs_fanHeatOAV, rs_amf); + } + + // outlet air state rs_asOut now known for all cases + // calc supply air state at zone(s) + rs_asSup = rs_asOut; + rs_SupplyDSEAndDucts(rs_asSup); + } + // else supply air state known + + return rsAvail; +} // RSYS::rs_SupplyAirState //---------------------------------------------------------------------------- -float RSYS::rs_GetAmfFullSpeed( int rsMode) const -{ - float amf; - switch (rsMode) - { - case rsmHEAT: amf = rs_amfH; break; - case rsmCOOL: amf = rs_amfC; break; - case rsmOAV: amf = rs_amfOAV; break; - default: amf = 0.; - } - return amf; -} // RSYS::rs_GetAmfFullSpeed +float RSYS::rs_GetAmfFullSpeed(int rsMode) const { + float amf; + switch (rsMode) { + case rsmHEAT: + amf = rs_amfH; + break; + case rsmCOOL: + amf = rs_amfC; + break; + case rsmOAV: + amf = rs_amfOAV; + break; + default: + amf = 0.; + } + return amf; +} // RSYS::rs_GetAmfFullSpeed //------------------------------------------------------------------------------ -int RSYS::rs_SetAmf( // set rs_amf - int rsMode, - float speedF) - // returns 3: mode is available, rs_amf set per speedF - // 2: mode is being actively autosized, rs_amf = full speed - // 1: mode is inactive (another mode is being actively autosized) - // 0: no amf available, rs_amf = 0 -{ - // set mode-specific full speed air flow - // must be set even if mode not changed - // due to possible speedF modifications - rs_amf = rs_GetAmfFullSpeed(rsMode); - if (rs_amf < .0001) - { rs_amf = 0.; - return 0; - } - - int rsAvail = 3; - int auszMode = rs_IsAutoSizing(); - if (auszMode != rsmOFF) - { // autosizing underway - if (auszMode != rsMode) - return 1; // another mode is being autosized - rsAvail = 2; // current mode now being autosized - } - - if (rsMode != rsmHEAT || !rs_IsCHDHW()) - rs_amf *= speedF; // assume air flow scales with capacity - else - { if (rs_capHt == 0.f) - rs_CurCapHtCHDHW(); - float avf; - float fanPwr; // unused - rs_pCHDHW->hvt_BlowerAVFandPower(speedF * rs_capHt, avf, fanPwr); - rs_amf = AVFtoAMF(avf); +int RSYS::rs_SetAmf( // set rs_amf + int rsMode, float speedF) +// returns 3: mode is available, rs_amf set per speedF +// 2: mode is being actively autosized, rs_amf = full speed +// 1: mode is inactive (another mode is being actively +//autosized) 0: no amf available, rs_amf = 0 +{ + // set mode-specific full speed air flow + // must be set even if mode not changed + // due to possible speedF modifications + rs_amf = rs_GetAmfFullSpeed(rsMode); + if (rs_amf < .0001) { + rs_amf = 0.; + return 0; + } + + int rsAvail = 3; + int auszMode = rs_IsAutoSizing(); + if (auszMode != rsmOFF) { // autosizing underway + if (auszMode != rsMode) + return 1; // another mode is being autosized + rsAvail = 2; // current mode now being autosized + } + + if (rsMode != rsmHEAT || !rs_IsCHDHW()) + rs_amf *= speedF; // assume air flow scales with capacity + else { + if (rs_capHt == 0.f) + rs_CurCapHtCHDHW(); + float avf; + float fanPwr; // unused + rs_pCHDHW->hvt_BlowerAVFandPower(speedF * rs_capHt, avf, fanPwr); + rs_amf = AVFtoAMF(avf); #if 0 if (rs_capHt < 34000.) printf("\nLow %.0f speedF=%0.3f avf=%0.f", rs_capHt, speedF, avf); #endif - } - return rsAvail; + } + return rsAvail; -} // RSYS::rs_SetAmf +} // RSYS::rs_SetAmf //---------------------------------------------------------------------------- -float RSYS::rs_SupplyDSEAndDucts( // apply supply duct and DSE losses - AIRSTATE& as, // air state - // call: state at RSYS outlet - // return: state at room register - float amf /*=-1*/) // dry air mass flow rate, lbm/hr - // default: rs_amf +float RSYS::rs_SupplyDSEAndDucts( // apply supply duct and DSE losses + AIRSTATE &as, // air state + // call: state at RSYS outlet + // return: state at room register + float amf /*=-1*/) // dry air mass flow rate, lbm/hr + // default: rs_amf // returns room register air mass flow rate, lbm/hr (after leakage if any) { - if (amf < 0.f) - amf = rs_amf; - - // DSE: discard (1-DSE) of heat/moisture added by system - int iHC = rs_DsHC(); // returns nz iff cooling - float DSE = iHC ? rs_DSEC : rs_DSEH; - if (DSE > 0.f) - { if (DSE < 1.f) - as.as_MixF( 1.f - DSE, rs_asIn); - // else leave as unchanged (no duct losses) - } - else - { // supply duct (if any) to get supply state - int iDS = rs_Dsi( 0, iHC); // retrieve supply duct idx - if (iDS > 0) // if supply duct present - { as = DsR[iDS].ds_CalcFL(as, amf); - amf *= (1.f - DsR[iDS].ds_leakF); - } - } - return amf; -} // RSYS_SupplyDSEAndDucts + if (amf < 0.f) + amf = rs_amf; + + // DSE: discard (1-DSE) of heat/moisture added by system + int iHC = rs_DsHC(); // returns nz iff cooling + float DSE = iHC ? rs_DSEC : rs_DSEH; + if (DSE > 0.f) { + if (DSE < 1.f) + as.as_MixF(1.f - DSE, rs_asIn); + // else leave as unchanged (no duct losses) + } else { // supply duct (if any) to get supply state + int iDS = rs_Dsi(0, iHC); // retrieve supply duct idx + if (iDS > 0) // if supply duct present + { + as = DsR[iDS].ds_CalcFL(as, amf); + amf *= (1.f - DsR[iDS].ds_leakF); + } + } + return amf; +} // RSYS_SupplyDSEAndDucts //---------------------------------------------------------------------------- -void RSYS::rs_SupplyDSEAndDuctsRev( // reverse supply duct and DSE losses - AIRSTATE& as) // air state - // call: state at room register - // return: state at RSYS outlet -{ - int iHC = rs_DsHC(); // returns nz iff cooling - float DSE = iHC ? rs_DSEC : rs_DSEH; - if (DSE > 0.f) - { // distribution system efficiency - // 1-DSE is discarded - if (DSE < 1.f) - as.as_MixF( -(1.f - DSE)/DSE, rs_asIn); - // else leave as unchanged (no duct losses) - } - else - { // supply duct (if any) - int iDS = rs_Dsi( 0); - if (iDS > 0) // if supply duct present - { AIRSTATE asSup( as); - as = DsR[ iDS].ds_CalcFLRev( asSup); - } - } -} // RSYS_SupplyDSEAndDuctsRev +void RSYS::rs_SupplyDSEAndDuctsRev( // reverse supply duct and DSE losses + AIRSTATE &as) // air state + // call: state at room register + // return: state at RSYS outlet +{ + int iHC = rs_DsHC(); // returns nz iff cooling + float DSE = iHC ? rs_DSEC : rs_DSEH; + if (DSE > 0.f) { // distribution system efficiency + // 1-DSE is discarded + if (DSE < 1.f) + as.as_MixF(-(1.f - DSE) / DSE, rs_asIn); + // else leave as unchanged (no duct losses) + } else { // supply duct (if any) + int iDS = rs_Dsi(0); + if (iDS > 0) // if supply duct present + { + AIRSTATE asSup(as); + as = DsR[iDS].ds_CalcFLRev(asSup); + } + } +} // RSYS_SupplyDSEAndDuctsRev //---------------------------------------------------------------------------- -double RSYS::rs_ZoneAirRequest( // air quantity needed by zone - double znSupReq, // dry-air mass flow rate required at supply register, lbm/hr - int iAux /*=0*/) // 0 = primary mode (compressor) - // 1 = aux mode (aux alone or main+aux (ASHP heating only) +double RSYS::rs_ZoneAirRequest( // air quantity needed by zone + double + znSupReq, // dry-air mass flow rate required at supply register, lbm/hr + int iAux /*=0*/) // 0 = primary mode (compressor) + // 1 = aux mode (aux alone or main+aux (ASHP heating only) // each zone needing conditioning requests air // returns RSYS (not supply register) amf needed to provide zone requirement { - // handle impossible requests - // supplyDT = tz - tSup - if (znSupReq > 1.e10) // znSupReq = DBL_MAX if supplyDT is tiny - znSupReq = rs_amf; - else if (znSupReq < 0.) - // reverse flow - // caused by "flipped" supply DT - // due to e.g. big duct losses - znSupReq = -#if 1 // attempt to fix small load sizing, 7-16-2021 - 2. * rs_amf; // request "a lot" + // handle impossible requests + // supplyDT = tz - tSup + if (znSupReq > 1.e10) // znSupReq = DBL_MAX if supplyDT is tiny + znSupReq = rs_amf; + else if (znSupReq < 0.) + // reverse flow + // caused by "flipped" supply DT + // due to e.g. big duct losses + znSupReq = +#if 1 // attempt to fix small load sizing, 7-16-2021 + 2. * rs_amf; // request "a lot" #else -#if 1 // unknown date - Top.tp_autoSizing +#if 1 // unknown date + Top.tp_autoSizing #else - Top.tp_pass1A + Top.tp_pass1A #endif - ? 1. // autosize warmup: ignore - : 2. * rs_amf; // simulation: request "a lot" + ? 1. // autosize warmup: ignore + : 2. * rs_amf; // simulation: request "a lot" #endif - // air required at system - double znAmfSys = znSupReq / rs_ducts[ rs_DsHC()].ductLkXF[ 0]; - rs_amfReq[ iAux] += znAmfSys; // all-zone total at system + // air required at system + double znAmfSys = znSupReq / rs_ducts[rs_DsHC()].ductLkXF[0]; + rs_amfReq[iAux] += znAmfSys; // all-zone total at system - // load at system, Btuh - rs_znLoad[ iAux] += - (iAux ? rs_asOutAux : rs_asOut).as_QSenDiff2(rs_asIn, znAmfSys); + // load at system, Btuh + rs_znLoad[iAux] += + (iAux ? rs_asOutAux : rs_asOut).as_QSenDiff2(rs_asIn, znAmfSys); -#if 0 && defined( _DEBUG) +#if 0 && defined(_DEBUG) if (iAux == 1) { const char* msg = fabs(rs_znLoad[0] - rs_znLoad[1]) > 1.f @@ -5758,466 +5718,451 @@ double RSYS::rs_ZoneAirRequest( // air quantity needed by zone rs_amfReq[ iAux], iAux); #endif #endif - return znAmfSys; -} // RSYS::rs_ZoneAirRequest + return znAmfSys; +} // RSYS::rs_ZoneAirRequest //----------------------------------------------------------------------------- -#if defined( _DEBUG) +#if defined(_DEBUG) #define RSYSITERCOUNT -#if defined( RSYSITERCOUNT) +#if defined(RSYSITERCOUNT) // dev aid statistic re minimizing aux heating iterations -int rsysPartAuxCount = 0; // # of part-load auxiliary substeps -int rsysIterCount = 0; // count secant method call-backs +int rsysPartAuxCount = 0; // # of part-load auxiliary substeps +int rsysIterCount = 0; // count secant method call-backs #endif #endif //----------------------------------------------------------------------------- -RC RSYS::rs_AllocateZoneAir() // finalize zone air flows -// +RC RSYS::rs_AllocateZoneAir() // finalize zone air flows +// { - if (rs_mode == rsmOAV) - return RCOK; // OAV: allocation done by rs_OAVAttempt - -#if defined( _DEBUG) - if (rs_speedF < 0.f || rs_speedF > 1.f) - printf("\nrs_speedF = %0.f", rs_speedF); -#endif - RC rc = RCOK; - - ASSERT(rs_fxCap[0] == 0.f && rs_fxCap[1] == 0.f); - if (rs_amfReq[ 0] > 0.) // if any air requested (by any zone) - rs_fxCap[ 0] = rs_amf / rs_amfReq[ 0]; // >1 = excess capacity - // else rs_fxCap = 0 - double fSize = min( rs_fxCap[ 0], 1.); // limit to available - - // bAux: aux possible and needed - // note: rs_capAuxH>0 possible for ASHP *only* (but can be =0 for ASHP) - bool bAux = rs_mode == rsmHEAT && rs_capAuxH > 0.f - && (rs_effHt == 0.f || (fSize > 0. && fSize < 1.)); - - if (bAux && rs_ctrlAuxH != C_AUXHEATCTRL_CYCLE) - { // check that aux will be helpful - // _CYCLE: any rs_capAuxH > 0 helps (> 0.f check is above) - // _LO, _ALT: aux supply temp must be greater than primary - if (rs_asSupAux.as_tdb <= rs_asSup.as_tdb) - { orWarn("Aux heat supply temperature (%0.1f F) <= primary supply temperature (%0.1f F)." - "\n Aux heat is not helpful. rscapAuxH should be increased.", - rs_asSupAux.as_tdb, rs_asSup.as_tdb); - // bAux = false; no: real controls would run aux even if not helpful - } - } + if (rs_mode == rsmOAV) + return RCOK; // OAV: allocation done by rs_OAVAttempt - ZNR* zp; - if (!bAux) - { // aux not available or not needed - if (rs_IsVCMode( rs_mode) && fSize == 1. && !rs_IsAutoSizing()) - { // variable capacity with excess capacity - // find intermediate speed +#if defined(_DEBUG) + if (rs_speedF < 0.f || rs_speedF > 1.f) + printf("\nrs_speedF = %0.f", rs_speedF); +#endif + RC rc = RCOK; + + ASSERT(rs_fxCap[0] == 0.f && rs_fxCap[1] == 0.f); + if (rs_amfReq[0] > 0.) // if any air requested (by any zone) + rs_fxCap[0] = rs_amf / rs_amfReq[0]; // >1 = excess capacity + // else rs_fxCap = 0 + double fSize = min(rs_fxCap[0], 1.); // limit to available + + // bAux: aux possible and needed + // note: rs_capAuxH>0 possible for ASHP *only* (but can be =0 for ASHP) + bool bAux = rs_mode == rsmHEAT && rs_capAuxH > 0.f && + (rs_effHt == 0.f || (fSize > 0. && fSize < 1.)); + + if (bAux && + rs_ctrlAuxH != C_AUXHEATCTRL_CYCLE) { // check that aux will be helpful + // _CYCLE: any rs_capAuxH > 0 helps (> 0.f check is above) + // _LO, _ALT: aux supply temp must be greater than primary + if (rs_asSupAux.as_tdb <= rs_asSup.as_tdb) { + orWarn("Aux heat supply temperature (%0.1f F) <= primary supply " + "temperature (%0.1f F)." + "\n Aux heat is not helpful. rscapAuxH should be increased.", + rs_asSupAux.as_tdb, rs_asSup.as_tdb); + // bAux = false; no: real controls would run aux even if not helpful + } + } + + ZNR *zp; + if (!bAux) { // aux not available or not needed + if (rs_IsVCMode(rs_mode) && fSize == 1. && + !rs_IsAutoSizing()) { // variable capacity with excess capacity + // find intermediate speed #if 0 float tSupUseful; bool bUseful = rs_IsSupplyAirTempUseful(rs_mode, rs_asSup.as_tdb, tSupUseful); if (!bUseful) printf("\nNot useful"); #endif - rc |= rs_FindRequiredSpeedF(); - // now rs_speedFMin <= rs_speedF < 1 - fSize = min(rs_fxCap[0], 1.); - } - RLUPC(ZrB, zp, rs_IsZoneServed(zp)) - zp->zn_SetRSYSAmf(fSize, 0); - } - else if (rs_amfReq[ 1] > rs_amf) - { // full aux insufficient - // allocate shortfall across zones - rs_fxCap[ 1] = rs_amf / rs_amfReq[ 1]; // x/0 impossible - - // finalize supply conditions - // recalc duct loss - // WHY: duct temps must be current re surrounding zone xfer - rs_asSup = rs_asOut = rs_asOutAux; - rs_SupplyDSEAndDucts( rs_asSup); -#if defined( _DEBUG) - // result s/b same as initial calc! - if (rs_asSup != rs_asSupAux) - printf( "rs_asSupAux mismatch!\n"); -#endif - rs_runFAux = 1.; // aux is at full cap - if (rs_ctrlAuxH == C_AUXHEATCTRL_LO) - rs_effHt = 0.f; // force compressor off - // rs_speedF = 0.; // dicey: fan is at full speed - RLUPC( ZrB, zp, rs_IsZoneServed( zp)) - zp->zn_SetRSYSAmf( rs_fxCap[ 1], 1); // tell zones how much they get - } - else if (rs_effHt == 0.f || rs_ctrlAuxH == C_AUXHEATCTRL_LO) - { // compressor is unavailable - // meet load with aux alone -#if 0 && defined( _DEBUG) + rc |= rs_FindRequiredSpeedF(); + // now rs_speedFMin <= rs_speedF < 1 + fSize = min(rs_fxCap[0], 1.); + } + RLUPC(ZrB, zp, rs_IsZoneServed(zp)) + zp->zn_SetRSYSAmf(fSize, 0); + } else if (rs_amfReq[1] > rs_amf) { // full aux insufficient + // allocate shortfall across zones + rs_fxCap[1] = rs_amf / rs_amfReq[1]; // x/0 impossible + + // finalize supply conditions + // recalc duct loss + // WHY: duct temps must be current re surrounding zone xfer + rs_asSup = rs_asOut = rs_asOutAux; + rs_SupplyDSEAndDucts(rs_asSup); +#if defined(_DEBUG) + // result s/b same as initial calc! + if (rs_asSup != rs_asSupAux) + printf("rs_asSupAux mismatch!\n"); +#endif + rs_runFAux = 1.; // aux is at full cap + if (rs_ctrlAuxH == C_AUXHEATCTRL_LO) + rs_effHt = 0.f; // force compressor off + // rs_speedF = 0.; // dicey: fan is at full speed + RLUPC(ZrB, zp, rs_IsZoneServed(zp)) + zp->zn_SetRSYSAmf(rs_fxCap[1], 1); // tell zones how much they get + } else if (rs_effHt == 0.f || + rs_ctrlAuxH == C_AUXHEATCTRL_LO) { // compressor is unavailable + // meet load with aux alone +#if 0 && defined(_DEBUG) if (Top.jDay == 336) printf("\nHit %s", Top.dateStr.CStr()); #endif - rs_effHt = 0.f; // force compressor off (for _LO case) - rs_fxCap[ 1] = rs_amf / rs_amfReq[ 1]; // x/0 impossible - rs_runFAux = 1.f / rs_fxCap[ 1]; -#if defined( _DEBUG) - if (ifBracket( 0.f, rs_runFAux, 1.f)) - printf( "rs_AllocateZoneAir: rs_runFAux > 1\n"); -#endif - - // finalize supply conditions - // recalc duct loss - // WHY: duct temps must be current re surrounding zone xfer - rs_asSup = rs_asOut = rs_asOutAux; - rs_SupplyDSEAndDucts( rs_asSup); -#if defined( _DEBUG) - // result s/b same as initial calc! - if (rs_asSup != rs_asSupAux) - printf( "rs_asSupAux mismatch!\n"); -#endif - RLUPC( ZrB, zp, rs_IsZoneServed( zp)) - zp->zn_SetRSYSAmfFromTSup(); - } - else - { // partial aux required - // C_AUXHEATCTRL_CYCLE: full compressor + cycling aux - // C_AUXHEATCTRL_ALT: compressor / aux alternate - // find supply temp that satisfies all zones at full volume - // Use secant method to find inverse of 1/amf = f( tSup) - // 1/amf is linear-ish with tSup, reduces iterations -#if defined( RSYSITERCOUNT) - rsysPartAuxCount++; -#endif -#if 0 && defined( _DEBUG) + rs_effHt = 0.f; // force compressor off (for _LO case) + rs_fxCap[1] = rs_amf / rs_amfReq[1]; // x/0 impossible + rs_runFAux = 1.f / rs_fxCap[1]; +#if defined(_DEBUG) + if (ifBracket(0.f, rs_runFAux, 1.f)) + printf("rs_AllocateZoneAir: rs_runFAux > 1\n"); +#endif + + // finalize supply conditions + // recalc duct loss + // WHY: duct temps must be current re surrounding zone xfer + rs_asSup = rs_asOut = rs_asOutAux; + rs_SupplyDSEAndDucts(rs_asSup); +#if defined(_DEBUG) + // result s/b same as initial calc! + if (rs_asSup != rs_asSupAux) + printf("rs_asSupAux mismatch!\n"); +#endif + RLUPC(ZrB, zp, rs_IsZoneServed(zp)) + zp->zn_SetRSYSAmfFromTSup(); + } else { // partial aux required + // C_AUXHEATCTRL_CYCLE: full compressor + cycling aux + // C_AUXHEATCTRL_ALT: compressor / aux alternate + // find supply temp that satisfies all zones at full volume + // Use secant method to find inverse of 1/amf = f( tSup) + // 1/amf is linear-ish with tSup, reduces iterations +#if defined(RSYSITERCOUNT) + rsysPartAuxCount++; +#endif +#if 0 && defined(_DEBUG) if (Top.jDay == 336) printf("\nHit %s", Top.dateStr.CStr()); #endif - double tSup = max(rs_asSup.as_tdb, rs_tSupLs); // use last result as guess - double amfX = DBL_MIN; - double amfXTarg = 1. / rs_amf; // f = target function value - int ret = secant( - [](void* pO, double& tSup) - { // returns 1/reqAMF, 1.e10 iff reqAMF = 0 -#if defined( RSYSITERCOUNT) - rsysIterCount++; -#endif - double amf = ((RSYS*)pO)->rs_AmfRequired(tSup); - return amf != 0. ? 1. / amf : 1.e10; - }, - 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); - } -#if defined( _DEBUG) - // check tSup -- should be between noAux and fullAux temps - double tSupX = bracket(rs_asSup.as_tdb, tSup, rs_asSupAux.as_tdb); - if (tSupX != tSup) - { - printf("rs_AllocateZoneAir: tSup out of range\n"); - tSup = tSupX; - } + double tSup = max(rs_asSup.as_tdb, rs_tSupLs); // use last result as guess + double tSup_old = tSup; + double amfX = DBL_MIN; + double amfXTarg = 1. / rs_amf; // f = target function value + int ret = secant( + [](void *pO, double &tSup) { // returns 1/reqAMF, 1.e10 iff reqAMF = 0 +#if defined(RSYSITERCOUNT) + rsysIterCount++; +#endif + double amf = ((RSYS *)pO)->rs_AmfRequired(tSup); + return amf != 0. ? 1. / amf : 1.e10; + }, + 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); + tSup = tSup_old; + } +#if defined(_DEBUG) + // check tSup -- should be between noAux and fullAux temps + double tSupX = bracket(rs_asSup.as_tdb, tSup, rs_asSupAux.as_tdb); + if (tSupX != tSup) { + printf("rs_AllocateZoneAir: tSup out of range\n"); + tSup = tSupX; + } +#endif + AIRSTATE asOutNoAux(rs_asOut); + rs_asSup.as_tdb = tSup; // required supply air state + rs_asOut = rs_asSup; + rs_SupplyDSEAndDuctsRev(rs_asOut); + +#if defined(_DEBUG) + // reverse calc, should match supply + AIRSTATE asSupX(rs_asOut); + rs_SupplyDSEAndDucts(asSupX); + if (asSupX != rs_asSup) + printf("rs_AllocateZoneAir: inconsistent air state\n"); +#endif + double qAux; + if (rs_ctrlAuxH == C_AUXHEATCTRL_ALT) { // aux alternates with primary + rs_runFAux = (rs_asOut.as_tdb - asOutNoAux.as_tdb) / + (rs_asOutAux.as_tdb - asOutNoAux.as_tdb); +#if defined(_DEBUG) + if (rs_runFAux < 0.f) + printf("\nrs_runFAux < 0"); + if (rs_runFAux > 1.f) + printf("\nrs_runFAux > 1"); #endif - AIRSTATE asOutNoAux(rs_asOut); - rs_asSup.as_tdb = tSup; // required supply air state - rs_asOut = rs_asSup; - rs_SupplyDSEAndDuctsRev(rs_asOut); - -#if defined( _DEBUG) - // reverse calc, should match supply - AIRSTATE asSupX(rs_asOut); - rs_SupplyDSEAndDucts(asSupX); - if (asSupX != rs_asSup) - printf("rs_AllocateZoneAir: inconsistent air state\n"); -#endif - double qAux; - if (rs_ctrlAuxH == C_AUXHEATCTRL_ALT) - { // aux alternates with primary - rs_runFAux = (rs_asOut.as_tdb - asOutNoAux.as_tdb) / (rs_asOutAux.as_tdb - asOutNoAux.as_tdb); -#if defined( _DEBUG) - if (rs_runFAux < 0.f) - printf("\nrs_runFAux < 0"); - if (rs_runFAux > 1.f) - printf("\nrs_runFAux > 1"); -#endif - float fFan = rs_capAuxH / (rs_capAuxH + rs_fanHeatH); - qAux = rs_runFAux * rs_capAuxH * fFan; - } - else - { // aux cycles: determine added heat rqd in addition to prim - qAux = rs_asOut.as_QSenDiff2(asOutNoAux, rs_amf); + float fFan = rs_capAuxH / (rs_capAuxH + rs_fanHeatH); + qAux = rs_runFAux * rs_capAuxH * fFan; + } else { // aux cycles: determine added heat rqd in addition to prim + qAux = rs_asOut.as_QSenDiff2(asOutNoAux, rs_amf); - if (qAux < 0.) - { -#if defined( _DEBUG) - if (qAux < -.1) - printf("rs_AllocateZoneAir: qAux < 0 (%.2f)\n", qAux); + if (qAux < 0.) { +#if defined(_DEBUG) + if (qAux < -.1) + printf("rs_AllocateZoneAir: qAux < 0 (%.2f)\n", qAux); #endif - qAux = 0.; - } - rs_runFAux = qAux / rs_capAuxH; - } -#if defined( _DEBUG) - if (ifBracket( 0.f, rs_runFAux, 1.f)) - printf( "rs_AllocateZoneAir: rs_runFAux > 1\n"); + qAux = 0.; + } + rs_runFAux = qAux / rs_capAuxH; + } +#if defined(_DEBUG) + if (ifBracket(0.f, rs_runFAux, 1.f)) + printf("rs_AllocateZoneAir: rs_runFAux > 1\n"); #endif - RLUPC( ZrB, zp, rs_IsZoneServed( zp)) - zp->zn_SetRSYSAmfFromTSup(); - } // end partial aux + RLUPC(ZrB, zp, rs_IsZoneServed(zp)) + zp->zn_SetRSYSAmfFromTSup(); + } // end partial aux - return rc; -} // RSYS::rs_AllocateZoneAir + return rc; +} // RSYS::rs_AllocateZoneAir //---------------------------------------------------------------------------- -#if 0 && defined( _DEBUG) +#if 0 && defined(_DEBUG) #define TRACEFINDSPEEDF #endif //---------------------------------------------------------------------------- -RC RSYS::rs_FindRequiredSpeedF() // find VC intermediate speed +RC RSYS::rs_FindRequiredSpeedF() // find VC intermediate speed // at call: RSYS in full speed state (excess capacity, rs_fxCap[ 0]==1) // returns RCBAD iff search fail (unexpected) // else RCOK and RSYS at intermediate speed state // if rs_fxCap[ 0] == 1, pegged at rs_speedFMin (must cycle below min) { -#if defined( _DEBUG) - if (rs_fxCap[0] < 1.f) - printf("\nrs_AllocateZoneAirVC: rs_fxCap[ 0] < 1"); +#if defined(_DEBUG) + if (rs_fxCap[0] < 1.f) + printf("\nrs_AllocateZoneAirVC: rs_fxCap[ 0] < 1"); #endif - float fxCapFullSpd = rs_fxCap[0]; // save full speed cap rat - RC rc = RCOK; + float fxCapFullSpd = rs_fxCap[0]; // save full speed cap rat + RC rc = RCOK; - assert(!rs_IsAutoSizing()); + assert(!rs_IsAutoSizing()); - // try minimum speed - rc |= rs_TotalAirRequestForSpeedF( rs_speedFMin); + // try minimum speed + rc |= rs_TotalAirRequestForSpeedF(rs_speedFMin); - // note: rs_speedF > rs_speedFMin is possible - // rs_TotalAirRequestForSpeedF() increases speed to avoid useless supply temp + // note: rs_speedF > rs_speedFMin is possible + // rs_TotalAirRequestForSpeedF() increases speed to avoid useless supply temp - if (rc || rs_fxCap[0] >= 1.f) - return rc; // error or rs_speedFMin is sufficient + if (rc || rs_fxCap[0] >= 1.f) + return rc; // error or rs_speedFMin is sufficient - // rs_speedFMin not sufficient, search for intermediate speed + // rs_speedFMin not sufficient, search for intermediate speed - float fxCapMinSpd = rs_fxCap[0]; + float fxCapMinSpd = rs_fxCap[0]; - float speedFEst = rs_speedF + (1.f - rs_speedF) * (1.f - fxCapMinSpd) / (fxCapFullSpd - fxCapMinSpd); + float speedFEst = rs_speedF + (1.f - rs_speedF) * (1.f - fxCapMinSpd) / + (fxCapFullSpd - fxCapMinSpd); - double speedF, f1; + double speedF, f1; #if 1 - speedF = min(1., speedFEst * 1.2); - f1 = DBL_MIN; + speedF = min(1., speedFEst * 1.2); + f1 = DBL_MIN; #elif 0 - if (speedFEst > 0.5f * rs_speedF + 0.5f) - { - speedF = 1.; - f1 = fxCapFullSpd; - } - else - { - speedF = rs_speedF; - f1 = fxCapMinSpd; - } + if (speedFEst > 0.5f * rs_speedF + 0.5f) { + speedF = 1.; + f1 = fxCapFullSpd; + } else { + speedF = rs_speedF; + f1 = fxCapMinSpd; + } #elif 0 - speedF = rs_speedFMin; - f1 = fxCapMinSpd; + speedF = rs_speedFMin; + f1 = fxCapMinSpd; #else - speedF = double( 0.5f * rs_speedFMin + 0.5f); - f1 = DBL_MIN; -#endif - -#if defined( TRACEFINDSPEEDF) - printf("\nStart speedFMin=%0.4f fxCapMinSpd=%0.4f fxCapFullSpd=%0.4f speedFEst=%0.4f", - rs_speedFMin, fxCapMinSpd, fxCapFullSpd, speedFEst); -#endif - - // search for speedF that produces fxCap = 1. - // this RSYS is left in the corresponding state via rc_TotalAirRequestForSpeed() calls - // (secant results not directly returned) - int ret = secant([](void* pO, double& speedF) { return ((RSYS*)pO)->rs_FxCapForSpeedF(speedF); }, - this, - 1., .0005, // target=rs_fxCap[0]=1 within .0005 - speedF, f1, // x1, f1 - speedFEst, DBL_MIN); // x2, f2 (not known) - if (ret) // if secant failed (unexpected) + speedF = double(0.5f * rs_speedFMin + 0.5f); + f1 = DBL_MIN; +#endif + +#if defined(TRACEFINDSPEEDF) + printf("\nStart speedFMin=%0.4f fxCapMinSpd=%0.4f fxCapFullSpd=%0.4f " + "speedFEst=%0.4f", + rs_speedFMin, fxCapMinSpd, fxCapFullSpd, speedFEst); +#endif + + // search for speedF that produces fxCap = 1. + // this RSYS is left in the corresponding state via + // rc_TotalAirRequestForSpeed() calls (secant results not directly returned) + int ret = secant( + [](void *pO, double &speedF) { + return ((RSYS *)pO)->rs_FxCapForSpeedF(speedF); + }, + this, 1., .0005, // target=rs_fxCap[0]=1 within .0005 + speedF, f1, // x1, f1 + speedFEst, DBL_MIN); // x2, f2 (not known) + if (ret) // if secant failed (unexpected) #if 0 rc |= rs_FindSpeedFForTSup(80.); #else - // leave RSYS is speedFEst state (= semi-decent result) - rc |= rs_TotalAirRequestForSpeedF(speedFEst); + // leave RSYS is speedFEst state (= semi-decent result) + rc |= rs_TotalAirRequestForSpeedF(speedFEst); #endif -#if defined( TRACEFINDSPEEDF) - if (ret) - printf("\nrs_FindRequiredSpeedF: secant ret=%d rsMode=%d", ret, rs_mode); - if (rs_speedF < rs_speedFMin || rs_speedF > 1.f) - printf("\nrs_FindRequiredSpeedF bad rs_speedF=%0.2f", rs_speedF); - printf("\nDone speedF=%0.4f fxCap=%0.4f", rs_speedF, rs_fxCap[0]); +#if defined(TRACEFINDSPEEDF) + if (ret) + printf("\nrs_FindRequiredSpeedF: secant ret=%d rsMode=%d", ret, rs_mode); + if (rs_speedF < rs_speedFMin || rs_speedF > 1.f) + printf("\nrs_FindRequiredSpeedF bad rs_speedF=%0.2f", rs_speedF); + printf("\nDone speedF=%0.4f fxCap=%0.4f", rs_speedF, rs_fxCap[0]); #endif - return rc; + return rc; -} // RSYS::rs_FindRequiredSpeedF() +} // RSYS::rs_FindRequiredSpeedF() //----------------------------------------------------------------------------- -bool RSYS::rs_IsSupplyAirTempUseful( // determine utility of supply air - int rsMode, // mode of interest - float tSup, // proposed supply temp - float& tSupUseful) const // returned: temp that would be useful +bool RSYS::rs_IsSupplyAirTempUseful( // determine utility of supply air + int rsMode, // mode of interest + float tSup, // proposed supply temp + float &tSupUseful) const // returned: temp that would be useful // returns true iff proposed supply air temp // sets rs_TSupLimit { - bool bRet = false; - float tspMaxH = -999.f; - float tspMinC = 999.f; - ZNR* zp; - RLUPC(ZrB, zp, rs_IsZoneServed(zp)) - { - setToMax(tspMaxH, zp->zn_tzspH); - setToMin(tspMinC, zp->zn_tzspC); - } - if (rsMode == rsmHEAT) - { - tSupUseful = tspMaxH + .1f; - bRet = tSup >= tSupUseful; - } - else - { - tSupUseful = tspMinC - .1f; - bRet = tSup <= tSupUseful; - } - return bRet; -} // RSYS::rs_IsSupplyAirTempUseful + bool bRet = false; + float tspMaxH = -999.f; + float tspMinC = 999.f; + ZNR *zp; + RLUPC(ZrB, zp, rs_IsZoneServed(zp)) { + setToMax(tspMaxH, zp->zn_tzspH); + setToMin(tspMinC, zp->zn_tzspC); + } + if (rsMode == rsmHEAT) { + tSupUseful = tspMaxH + .1f; + bRet = tSup >= tSupUseful; + } else { + tSupUseful = tspMinC - .1f; + bRet = tSup <= tSupUseful; + } + return bRet; +} // RSYS::rs_IsSupplyAirTempUseful //----------------------------------------------------------------------------- -RC RSYS::rs_TotalAirRequestForSpeedF( // all-zone air request at speedF - float speedF, // speed to attempt - int options /*=0*/) // option bits - // 1: report zn_AirRequest errors +RC RSYS::rs_TotalAirRequestForSpeedF( // all-zone air request at speedF + float speedF, // speed to attempt + int options /*=0*/) // option bits + // 1: report zn_AirRequest errors // returns RCOK iff valid result (rs_fxCap[ 0] set) // else RCBAD, rs_fxCap[ 0] = 0 { - RC rc = RCOK; - - if (speedF != rs_speedF) - { - rs_ClearSubhrResults(1); // init for speed re-try - /* int ret = */ rs_SupplyAirState(rs_mode, speedF); + RC rc = RCOK; - float tSupUseful; - if (!rs_IsSupplyAirTempUseful(rs_mode, rs_asSup.as_tdb, tSupUseful)) - { // adjust rs_speedF to produce useful supply air temp - rs_FindSpeedFForTSup(tSupUseful); - // printf("\nSpeedF = %0.3f", rs_speedF); + if (speedF != rs_speedF) { + rs_ClearSubhrResults(1); // init for speed re-try + /* int ret = */ rs_SupplyAirState(rs_mode, speedF); - } - ZNR* zp; - RLUPC(ZrB, zp, rs_IsZoneServed(zp)) - rc |= zp->zn_AirRequest(this, options); + float tSupUseful; + if (!rs_IsSupplyAirTempUseful( + rs_mode, rs_asSup.as_tdb, + tSupUseful)) { // adjust rs_speedF to produce useful supply air temp + rs_FindSpeedFForTSup(tSupUseful); + // printf("\nSpeedF = %0.3f", rs_speedF); + } + ZNR *zp; + RLUPC(ZrB, zp, rs_IsZoneServed(zp)) + rc |= zp->zn_AirRequest(this, options); - if (rc || rs_amfReq[0] <= 0.) - { -#if defined( _DEBUG) - printf("\nrs_TotalAirRequestForSpeed: rs_amfReq[ 0] <= 0."); + if (rc || rs_amfReq[0] <= 0.) { +#if defined(_DEBUG) + printf("\nrs_TotalAirRequestForSpeed: rs_amfReq[ 0] <= 0."); #endif - rs_fxCap[0] = 0.f; - rc = RCBAD; - } - else - rs_fxCap[0] = rs_amf / rs_amfReq[0]; // >1 = excess capacity - } + rs_fxCap[0] = 0.f; + rc = RCBAD; + } else + rs_fxCap[0] = rs_amf / rs_amfReq[0]; // >1 = excess capacity + } - return rc; -} // RSYS::rs_TotalAirRequestForSpeedF + return rc; +} // RSYS::rs_TotalAirRequestForSpeedF //----------------------------------------------------------------------------- -double RSYS::rs_FxCapForSpeedF( // call-back fcn for secant method - double& speedF) // trial speedF (may be returned modified) +double RSYS::rs_FxCapForSpeedF( // call-back fcn for secant method + double &speedF) // trial speedF (may be returned modified) // returns required rs_fxCap[ 0] (= rs_amf / amfReq), >1 = excess capacity { -#if defined( _DEBUG) - double speedFWas = speedF; - // float rsSpeedFWas = rs_speedF; -#endif - speedF = bracket(double(rs_speedFMin), speedF, 1.); -#if defined( _DEBUG) - if (speedF != speedFWas) - printf("\nrs_FxCapForSpeedF limit: speedFWas=%0.4f speedF=%0.4f", - speedFWas, speedF); +#if defined(_DEBUG) + double speedFWas = speedF; + // float rsSpeedFWas = rs_speedF; #endif - - // options: report bad supply temps iff near solution - // (when far from solution errors are common and not meaningful - int arOptions = abs(rs_fxCap[0] - 1.f) < .01f; - - /*RC rc =*/ rs_TotalAirRequestForSpeedF(float(speedF), arOptions); - - // if rc != RCOK, rs_fxCap[0] is 0 - -#if defined( TRACEFINDSPEEDF) - static double speedFLastCall = 0.; - static float fxLastCall = 0.; - bool bDup = speedF != speedFLastCall && rs_fxCap[0] == fxLastCall; - printf("\n speedF=%0.4f f=%0.4f tSup=%0.3f rs_amf=%0.3f rs_amfReq=%0.3f (speedFLastCall=%0.4f rsSpeedFWas=%0.4f)%s", - speedF, rs_fxCap[0], rs_asSup.as_tdb, rs_amf, rs_amfReq[ 0], speedFLastCall, rsSpeedFWas, bDup ? " Dup" : ""); - if (bDup) - { - rs_speedF = rsSpeedFWas; - rs_TotalAirRequestForSpeedF(float(speedF), arOptions); - } - - if (rc) - printf("\nrs_FxCapForSpeedF: rs_TotalAirRequestForSpeedF() fail"); - speedFLastCall = speedF; - fxLastCall = rs_fxCap[0]; + speedF = bracket(double(rs_speedFMin), speedF, 1.); +#if defined(_DEBUG) + if (speedF != speedFWas) + printf("\nrs_FxCapForSpeedF limit: speedFWas=%0.4f speedF=%0.4f", + speedFWas, speedF); +#endif + + // options: report bad supply temps iff near solution + // (when far from solution errors are common and not meaningful + int arOptions = abs(rs_fxCap[0] - 1.f) < .01f; + + /*RC rc =*/rs_TotalAirRequestForSpeedF(float(speedF), arOptions); + + // if rc != RCOK, rs_fxCap[0] is 0 + +#if defined(TRACEFINDSPEEDF) + static double speedFLastCall = 0.; + static float fxLastCall = 0.; + bool bDup = speedF != speedFLastCall && rs_fxCap[0] == fxLastCall; + printf("\n speedF=%0.4f f=%0.4f tSup=%0.3f rs_amf=%0.3f rs_amfReq=%0.3f " + "(speedFLastCall=%0.4f rsSpeedFWas=%0.4f)%s", + speedF, rs_fxCap[0], rs_asSup.as_tdb, rs_amf, rs_amfReq[0], + speedFLastCall, rsSpeedFWas, bDup ? " Dup" : ""); + if (bDup) { + rs_speedF = rsSpeedFWas; + rs_TotalAirRequestForSpeedF(float(speedF), arOptions); + } + + if (rc) + printf("\nrs_FxCapForSpeedF: rs_TotalAirRequestForSpeedF() fail"); + speedFLastCall = speedF; + fxLastCall = rs_fxCap[0]; #else - // ignore rc, return rs_fxCap[ 0] = 0 + // ignore rc, return rs_fxCap[ 0] = 0 #endif - return double(rs_fxCap[0]); -} // RSYS::rs_FxCapForSpeedF + return double(rs_fxCap[0]); +} // RSYS::rs_FxCapForSpeedF //----------------------------------------------------------------------------- -RC RSYS::rs_FindSpeedFForTSup( // find speedF that will deliver specified supply temp - double tSup) -{ - RC rc = RCOK; - - double speedFLo = rs_speedFMin; - double speedFHi = 1.; - double f1 = DBL_MIN; - - // search for speedF that produces fxCap = 1. -// this RSYS is left in the corresponding state via rc_TotalAirRequestForSpeed() calls -// (secant results not directly returned) - [[maybe_unused]] int ret = secant([](void* pO, double& speedF) { return ((RSYS*)pO)->rs_TSupForSpeedF(speedF); }, - this, - tSup, .005, // target=tSup within .005 degF - speedFLo, f1, // x1, f1 - speedFHi, DBL_MIN); // x2, f2 - - return rc; - -} // RSYS::rs_FindSpeedFForTSup +RC RSYS::rs_FindSpeedFForTSup( // find speedF that will deliver specified supply + // temp + double tSup) { + RC rc = RCOK; + + double speedFLo = rs_speedFMin; + double speedFHi = 1.; + double f1 = DBL_MIN; + + // search for speedF that produces fxCap = 1. + // this RSYS is left in the corresponding state via + // rc_TotalAirRequestForSpeed() calls (secant results not directly returned) + [[maybe_unused]] int ret = secant( + [](void *pO, double &speedF) { + return ((RSYS *)pO)->rs_TSupForSpeedF(speedF); + }, + this, tSup, .005, // target=tSup within .005 degF + speedFLo, f1, // x1, f1 + speedFHi, DBL_MIN); // x2, f2 + + return rc; + +} // RSYS::rs_FindSpeedFForTSup //----------------------------------------------------------------------------- -double RSYS::rs_TSupForSpeedF( - double& speedF) -{ -#if defined( _DEBUG) - double speedFWas = speedF; - // float rsSpeedFWas = rs_speedF; +double RSYS::rs_TSupForSpeedF(double &speedF) { +#if defined(_DEBUG) + double speedFWas = speedF; + // float rsSpeedFWas = rs_speedF; #endif - speedF = bracket(double(rs_speedFMin), speedF, 1.); -#if defined( _DEBUG) - if (speedF != speedFWas) - printf("\nrs_TSupForSpeedF limit: speedFWas=%0.4f speedF=%0.4f", - speedFWas, speedF); + speedF = bracket(double(rs_speedFMin), speedF, 1.); +#if defined(_DEBUG) + if (speedF != speedFWas) + printf("\nrs_TSupForSpeedF limit: speedFWas=%0.4f speedF=%0.4f", speedFWas, + speedF); #endif - // rs_ClearSubhrResults(1); // init for speed re-try - /*int ret = */ rs_SupplyAirState(rs_mode, speedF); + // rs_ClearSubhrResults(1); // init for speed re-try + /*int ret = */ rs_SupplyAirState(rs_mode, speedF); - return rs_asSup.as_tdb; + return rs_asSup.as_tdb; -} // RSYS::rs_TSupForSpeedF +} // RSYS::rs_TSupForSpeedF //----------------------------------------------------------------------------- -#if 0 // incomplete/unused ideas +#if 0 // incomplete/unused ideas void RSYS::rs_AvgZoneCond() { const ZNR* zp; @@ -6263,22 +6208,22 @@ void RSYS::rs_HeatDelivered( // heat delivered to zone(s) by this RSYS } // RSYS::rs_SenHeatDelivered #endif //---------------------------------------------------------------------------- -double RSYS::rs_AmfRequired( // find all-zone total AMF required for tSup - double tSup) // proposed supply air temp, F -{ - double rsAmf = 0.; - ZNR* zp; - RLUPC( ZrB, zp, rs_IsZoneServed( zp) && zp->zn_hcMode != RSYS::rsmOFF) - { double znAmf = zp->zn_AmfHvacCR( zp->zn_tzsp, tSup); -#if defined( _DEBUG) - if (znAmf < 0.) - printf( "Zone '%s': Neg amf\n", zp->Name()); -#endif - rsAmf += znAmf; - } - float ductLkXF = rs_ducts[rs_DsHC()].ductLkXF[0]; - return rsAmf / ductLkXF; -} // RSYS::rs_AmfRequired +double RSYS::rs_AmfRequired( // find all-zone total AMF required for tSup + double tSup) // proposed supply air temp, F +{ + double rsAmf = 0.; + ZNR *zp; + RLUPC(ZrB, zp, rs_IsZoneServed(zp) && zp->zn_hcMode != RSYS::rsmOFF) { + double znAmf = zp->zn_AmfHvacCR(zp->zn_tzsp, tSup); +#if defined(_DEBUG) + if (znAmf < 0.) + printf("Zone '%s': Neg amf\n", zp->Name()); +#endif + rsAmf += znAmf; + } + float ductLkXF = rs_ducts[rs_DsHC()].ductLkXF[0]; + return rsAmf / ductLkXF; +} // RSYS::rs_AmfRequired //---------------------------------------------------------------------------- #if 0 x unused idea @@ -6302,161 +6247,161 @@ x } // RSYS::rs_ACReqCap95 RC RSYS::rs_FinalizeSh() // rs_speedF known { - // determine run fraction - // Note: rs_runF modified below for ASHP C_AUXHEATCTRL_ALT - double amfRun = min( rs_amfReq[ 0], rs_amf); - if (amfRun < .0001) - rs_mode = rsmOFF; // no air actually delivered - if (rs_mode == rsmOFF) - rs_SetModeAndSpeedF( rsmOFF, 0.f); - // rs_runF = 0.f; // rs_ClearSubhrResults - else if (rs_amf > 0.) - // rs_SetModeAndSpeedF() called in rs_SupplyAirState - rs_runF = amfRun / rs_amf; - // else rs_runF = 0.f + // determine run fraction + // Note: rs_runF modified below for ASHP C_AUXHEATCTRL_ALT + double amfRun = min(rs_amfReq[0], rs_amf); + if (amfRun < .0001) + rs_mode = rsmOFF; // no air actually delivered + if (rs_mode == rsmOFF) + rs_SetModeAndSpeedF(rsmOFF, 0.f); + // rs_runF = 0.f; // rs_ClearSubhrResults + else if (rs_amf > 0.) + // rs_SetModeAndSpeedF() called in rs_SupplyAirState + rs_runF = amfRun / rs_amf; + // else rs_runF = 0.f #if 0 // restore if needed rs_AvgZoneCond(); #endif - // record peaks or calc energy - // Probably unnecessary for autosizing - // TODO: could skip accounting if autoSizing - // BUT may be side effects - float runFFan = 0.f; // subhour fan run fraction - if (rs_mode == rsmCOOL) - { - rs_outSen = rs_runF * rs_capSenCt; // average output w/o fan (= gross cooling), Btuh (< 0) - rs_outLat = rs_runF * rs_capLatCt; - rs_outFan = rs_inFan = rs_runF * rs_speedF * rs_fanHeatC; // all fan heat to air - runFFan = rs_runF; - rs_outSenTot = rs_outSen + rs_outFan; // net cooling output, Btuh (generally < 0) - - if (rs_IsFanCoil()) - { // rs_inPrimary = 0 - - } - else if (!rs_IsVCClg() || rs_speedF == 1.f || rs_speedF <= rs_speedFMin) - { // cycling possible - // not variable capacity - // or variable capacity below min speed - rs_PLF = 1.f - rs_CdC * (1.f - rs_runF); - rs_inPrimary = fabs(rs_outSen / max(.01f, rs_effCt * rs_PLF)); // compressor power, Btuh - } - else - { - // variable capacity: intermediate speed + // record peaks or calc energy + // Probably unnecessary for autosizing + // TODO: could skip accounting if autoSizing + // BUT may be side effects + float runFFan = 0.f; // subhour fan run fraction + if (rs_mode == rsmCOOL) { + rs_outSen = + rs_runF * + rs_capSenCt; // average output w/o fan (= gross cooling), Btuh (< 0) + rs_outLat = rs_runF * rs_capLatCt; + rs_outFan = rs_inFan = + rs_runF * rs_speedF * rs_fanHeatC; // all fan heat to air + runFFan = rs_runF; + rs_outSenTot = + rs_outSen + rs_outFan; // net cooling output, Btuh (generally < 0) + + if (rs_IsFanCoil()) { // rs_inPrimary = 0 + + } else if (!rs_IsVCClg() || rs_speedF == 1.f || + rs_speedF <= rs_speedFMin) { // cycling possible + // not variable capacity + // or variable capacity below min speed + rs_PLF = 1.f - rs_CdC * (1.f - rs_runF); + rs_inPrimary = fabs( + rs_outSen / max(.01f, rs_effCt * rs_PLF)); // compressor power, Btuh + } else { + // variable capacity: intermediate speed +#if defined(_DEBUG) + if (fabs(rs_runF - 1.f) > 0.001f) + printf("\nUnexpected rs_runF"); +#endif + rs_PLF = 1.f; + rs_inPrimary = + fabs(rs_outSen / max(.01f, rs_effCt)); // compressor power, Btuh + } + + if (rs_pMtrElec) { + rs_pMtrElec->H.clg += + rs_inPrimary * Top.tp_subhrDur; // compressor energy for step, Btu + rs_pMtrElec->H.fanC += rs_inFan * Top.tp_subhrDur; #if defined(_DEBUG) - if (fabs(rs_runF - 1.f) > 0.001f) - printf("\nUnexpected rs_runF"); + if (!rs_IsFanCoil() && rs_pMtrElec->H.clg < .01f && + rs_pMtrElec->H.fanC > .01f) + printf("\nFanC > 0"); +#endif + } + } else if (rs_mode == rsmHEAT) { + if (rs_IsHP()) { // rs_runFAux known at this point + if (rs_effHt == 0.f) // if compressor is off + { // rs_inPrimary = 0.; + // rs_COPHtAdj = 0.f; + runFFan = rs_runFAux; + rs_outFan = rs_runFAux * rs_fanHeatH; + rs_outAux = rs_runFAux * rs_capAuxH; + rs_runF = rs_PLF = 0.f; + rs_capHt = rs_capHtMin = rs_capDfHt = rs_capDfHtMin = + 0.f; // clear values for reports + // aux may be on, handled below + } else { + float fFanHeatPrim; // run fraction for both fan and primary + if (rs_runFAux > 0.f && + rs_ctrlAuxH == + C_AUXHEATCTRL_ALT) { // aux/primary alternate: primary runs when + // aux does not + rs_runF = 1.f - rs_runFAux; + fFanHeatPrim = rs_runF; // fan heat allocation to primary + runFFan = 1.f; +#if defined(_DEBUG) + if (rs_speedF != 1.f) + printf("\nUnexpected rs_speedF = %0.3f", rs_speedF); #endif - rs_PLF = 1.f; - rs_inPrimary = fabs(rs_outSen / max(.01f, rs_effCt)); // compressor power, Btuh - } + } else { + runFFan = rs_runF; + fFanHeatPrim = 1.f; + } - if (rs_pMtrElec) - { rs_pMtrElec->H.clg += rs_inPrimary * Top.tp_subhrDur; // compressor energy for step, Btu - rs_pMtrElec->H.fanC += rs_inFan * Top.tp_subhrDur; -#if defined( _DEBUG) - if (!rs_IsFanCoil() && rs_pMtrElec->H.clg < .01f && rs_pMtrElec->H.fanC > .01f) - printf("\nFanC > 0"); -#endif - } - } - else if (rs_mode == rsmHEAT) - { if (rs_IsHP()) - { // rs_runFAux known at this point - if (rs_effHt == 0.f) // if compressor is off - { // rs_inPrimary = 0.; - // rs_COPHtAdj = 0.f; - runFFan = rs_runFAux; - rs_outFan = rs_runFAux * rs_fanHeatH; - rs_outAux = rs_runFAux * rs_capAuxH; - rs_runF = rs_PLF = 0.f; - rs_capHt = rs_capHtMin = rs_capDfHt = rs_capDfHtMin = 0.f; // clear values for reports - // aux may be on, handled below - } - else - { - float fFanHeatPrim; // run fraction for both fan and primary - if (rs_runFAux > 0.f && rs_ctrlAuxH == C_AUXHEATCTRL_ALT) - { // aux/primary alternate: primary runs when aux does not - rs_runF = 1.f - rs_runFAux; - fFanHeatPrim = rs_runF; // fan heat allocation to primary - runFFan = 1.f; -#if defined( _DEBUG) - if (rs_speedF != 1.f) - printf("\nUnexpected rs_speedF = %0.3f", rs_speedF); -#endif - } - else - { runFFan = rs_runF; - fFanHeatPrim = 1.f; - } - - rs_capSenNetFS = rs_capHt - rs_capDfHt; // full-speed net capacity - - double outTot = rs_runF * rs_CapHtCurSpeedF(); // total primary output (incl fan and defrost), Btuh - - rs_outAux = rs_runFAux * rs_capAuxH; - - rs_outFan = min( outTot+rs_outAux, runFFan * rs_speedF * rs_fanHeatH); // fan output, Btuh - // insurance: limit to total output - rs_outSen = outTot - rs_outFan*fFanHeatPrim; // compressor sensible output (note defrost adjustment below) - // rs_outLat = 0.; // total latent output - -#if defined( _DEBUG) - // some checking if aux is running - if (rs_runFAux > 0.f) - { - if (rs_ctrlAuxH == C_AUXHEATCTRL_LO) - { - if (rs_runF > 0.f) - printf("\nAux LO: compressor running"); - } - else if (rs_ctrlAuxH == C_AUXHEATCTRL_ALT) - { - if (abs( rs_runFAux + rs_runF - 1.f) > 0.0001f) - printf("\nAux alt: bad runF+runFAux"); - } - else - { - if (rs_runF != 1.f || rs_speedF != 1.f) - printf("\nAux cycle: not full speed"); - } - } -#endif - if (!rs_IsASHPVC() || rs_speedF == 1.f) - { // single speed or variable speed at max - // variable speed can cycle at max due to C_AUXHEATCTRL_ALT - rs_outDefrost = rs_runF * rs_capDfHt; - rs_outSen -= rs_outDefrost; - rs_PLF = 1.f - rs_CdH * (1.f - rs_runF); - rs_COPHtAdj = rs_effHt * rs_PLF; // full load efficiency modified by PLF - rs_inPrimary = rs_outSen / rs_COPHtAdj; - ASSERT(rs_speedF == 1.f); - } - else if (rs_speedF <= rs_speedFMin) - { // variable capacity: cycling at min speed - rs_outDefrost = rs_runF * rs_capDfHtMin; - rs_outSen -= rs_outDefrost; - rs_PLF = 1.f - rs_CdH * (1.f - rs_runF); - rs_COPHtAdj = rs_inpHtMin <= 0.f - ? 2.5f // rs_inpHtMin can be 0 during autosize - : rs_fEffH * rs_capHtMin * rs_PLF / rs_inpHtMin; - rs_inPrimary = rs_outSen / rs_COPHtAdj; - } - else - { // variable capacity: intermediate speed - rs_outDefrost = rs_CapDfHtCurSpeedF(); - rs_outSen -= rs_outDefrost; - rs_inPrimary = rs_InpHtCurSpeedF() / rs_fEffH; - rs_COPHtAdj = rs_inPrimary <= 0.f - ? 2.5f // rs_inPrimary can be 0 during autosize - : rs_outSen / rs_inPrimary; - rs_PLF = 1.f; - } + rs_capSenNetFS = rs_capHt - rs_capDfHt; // full-speed net capacity + + double outTot = + rs_runF * rs_CapHtCurSpeedF(); // total primary output (incl fan and + // defrost), Btuh + + rs_outAux = rs_runFAux * rs_capAuxH; + + rs_outFan = min(outTot + rs_outAux, + runFFan * rs_speedF * + rs_fanHeatH); // fan output, Btuh + // insurance: limit to total output + rs_outSen = outTot - + rs_outFan * fFanHeatPrim; // compressor sensible output + // (note defrost adjustment below) + // rs_outLat = 0.; + // // total latent output + +#if defined(_DEBUG) + // some checking if aux is running + if (rs_runFAux > 0.f) { + if (rs_ctrlAuxH == C_AUXHEATCTRL_LO) { + if (rs_runF > 0.f) + printf("\nAux LO: compressor running"); + } else if (rs_ctrlAuxH == C_AUXHEATCTRL_ALT) { + if (abs(rs_runFAux + rs_runF - 1.f) > 0.0001f) + printf("\nAux alt: bad runF+runFAux"); + } else { + if (rs_runF != 1.f || rs_speedF != 1.f) + printf("\nAux cycle: not full speed"); + } + } +#endif + if (!rs_IsASHPVC() || + rs_speedF == 1.f) { // single speed or variable speed at max + // variable speed can cycle at max due to C_AUXHEATCTRL_ALT + rs_outDefrost = rs_runF * rs_capDfHt; + rs_outSen -= rs_outDefrost; + rs_PLF = 1.f - rs_CdH * (1.f - rs_runF); + rs_COPHtAdj = + rs_effHt * rs_PLF; // full load efficiency modified by PLF + rs_inPrimary = rs_outSen / rs_COPHtAdj; + ASSERT(rs_speedF == 1.f); + } else if (rs_speedF <= + rs_speedFMin) { // variable capacity: cycling at min speed + rs_outDefrost = rs_runF * rs_capDfHtMin; + rs_outSen -= rs_outDefrost; + rs_PLF = 1.f - rs_CdH * (1.f - rs_runF); + rs_COPHtAdj = rs_inpHtMin <= 0.f + ? 2.5f // rs_inpHtMin can be 0 during autosize + : rs_fEffH * rs_capHtMin * rs_PLF / rs_inpHtMin; + rs_inPrimary = rs_outSen / rs_COPHtAdj; + } else { // variable capacity: intermediate speed + rs_outDefrost = rs_CapDfHtCurSpeedF(); + rs_outSen -= rs_outDefrost; + rs_inPrimary = rs_InpHtCurSpeedF() / rs_fEffH; + rs_COPHtAdj = rs_inPrimary <= 0.f + ? 2.5f // rs_inPrimary can be 0 during autosize + : rs_outSen / rs_inPrimary; + rs_PLF = 1.f; + } #if 0 x Not maintained, 8-2020 x#undef ASHP_COMPAREMP // define to enable Micropas comparison code for ASHP @@ -6477,402 +6422,384 @@ x#endif x x rs_inPrimary = rs_outSen / (rs_effHt * rs_PLF); #endif - } - - rs_inAux = rs_outAux / (rs_effAuxH * rs_fEffAuxHBackup); - rs_inDefrost = rs_outDefrost / (rs_effAuxH * rs_fEffAuxHDefrost); - - if (rs_pMtrAux) - rs_pMtrAux->H.hpBU += (rs_inAux + rs_inDefrost) * Top.tp_subhrDur; - } - else if (rs_IsCHDHW()) - { // cycling - // pump power - rs_outSenTot = rs_runF * rs_speedF * rs_capHt; - float avf; - float fanPwr; - rs_pCHDHW->hvt_BlowerAVFandPower(rs_outSenTot, avf, fanPwr); - // if (rs_runF < 1.) cycle? - - rs_outFan = rs_runF * fanPwr * Top.tp_subhrDur * BtuperWh; - rs_outSen = max(0., rs_outSenTot - rs_outFan); // net -> gross - runFFan = rs_runF; - - // flow (gpm) needed for gross output - float waterVolFlow = rs_pCHDHW->hvt_WaterVolFlow(rs_outSen, rs_tCoilEW); - float vol = rs_runF * waterVolFlow * Top.tp_subhrDur * 60.f; - - // return temp based on gross (coil) output - float tRet = rs_tCoilEW - rs_outSen * Top.tp_subhrDur / (vol * waterRhoCp); - - // apply draw to DHWSYS - DHWSYS* pWS = rs_GetCHDHWSYS(); - pWS->ws_AccumCHDHWFlowSh(vol, tRet); - - // rs_inPrimary = 0.; - } - else - { // non-ASHP, non-CHDHW - rs_capSenNetFS = rs_capHt; // net capacity, Btuh - double outTot = rs_runF * rs_capHt; // total output (incl fan), Btuh - // rs_outLat = 0.; // total latent output - rs_outFan = min( outTot, rs_runF * rs_fanHeatH); // fan output, Btuh - // insurance: limit to total output - runFFan = rs_runF; - rs_outSen = outTot - rs_outFan; - rs_PLF = 1.f; - if (rs_IsFanCoil()) - ; // nothing needed, energy supplied externally - else - { // not fancoil, not CHDHW, not AHSP - rs_inPrimary = rs_outSen / rs_effHt; - } - } - - if (rs_pMtrHeat) - rs_pMtrHeat->H.htg += rs_inPrimary * Top.tp_subhrDur; - rs_inFan = rs_outFan; // fan input, Btuh (in = out, all fan heat into air) - if (rs_pMtrElec) - rs_pMtrElec->H.fanH += rs_inFan * Top.tp_subhrDur; - - rs_outSenTot = rs_outSen + rs_outDefrost + rs_outAux + rs_outFan; - } - else if (rs_mode == rsmOAV) - { runFFan = rs_runF; - rs_outSenTot = rs_outFan = rs_inFan = rs_fanHeatOAV * rs_runF; - if (rs_pMtrElec) - rs_pMtrElec->H.fanC += rs_inFan * Top.tp_subhrDur; - } - // else if (rs_Mode == rsmOFF) - - if (rs_capSenNetFS != 0.f) - { rs_loadF = (rs_outSen + rs_outFan) / rs_capSenNetFS; - rs_PLR = rs_znLoad[0] / rs_capSenNetFS; -#if defined( _DEBUG) - if (rs_loadF < 0.f) - printf("\nNeg rs_loadF"); + } + + rs_inAux = rs_outAux / (rs_effAuxH * rs_fEffAuxHBackup); + rs_inDefrost = rs_outDefrost / (rs_effAuxH * rs_fEffAuxHDefrost); + + if (rs_pMtrAux) + rs_pMtrAux->H.hpBU += (rs_inAux + rs_inDefrost) * Top.tp_subhrDur; + } else if (rs_IsCHDHW()) { // cycling + // pump power + rs_outSenTot = rs_runF * rs_speedF * rs_capHt; + float avf; + float fanPwr; + rs_pCHDHW->hvt_BlowerAVFandPower(rs_outSenTot, avf, fanPwr); + // if (rs_runF < 1.) cycle? + + rs_outFan = rs_runF * fanPwr * Top.tp_subhrDur * BtuperWh; + rs_outSen = max(0., rs_outSenTot - rs_outFan); // net -> gross + runFFan = rs_runF; + + // flow (gpm) needed for gross output + float waterVolFlow = rs_pCHDHW->hvt_WaterVolFlow(rs_outSen, rs_tCoilEW); + float vol = rs_runF * waterVolFlow * Top.tp_subhrDur * 60.f; + + // return temp based on gross (coil) output + float tRet = + rs_tCoilEW - rs_outSen * Top.tp_subhrDur / (vol * waterRhoCp); + + // apply draw to DHWSYS + DHWSYS *pWS = rs_GetCHDHWSYS(); + pWS->ws_AccumCHDHWFlowSh(vol, tRet); + + // rs_inPrimary = 0.; + } else { // non-ASHP, non-CHDHW + rs_capSenNetFS = rs_capHt; // net capacity, Btuh + double outTot = rs_runF * rs_capHt; // total output (incl fan), Btuh + // rs_outLat = 0.; // total + // latent output + rs_outFan = min( + outTot, rs_runF * rs_fanHeatH); // fan output, Btuh + // insurance: limit to total output + runFFan = rs_runF; + rs_outSen = outTot - rs_outFan; + rs_PLF = 1.f; + if (rs_IsFanCoil()) + ; // nothing needed, energy supplied externally + else { // not fancoil, not CHDHW, not AHSP + rs_inPrimary = rs_outSen / rs_effHt; + } + } + + if (rs_pMtrHeat) + rs_pMtrHeat->H.htg += rs_inPrimary * Top.tp_subhrDur; + rs_inFan = rs_outFan; // fan input, Btuh (in = out, all fan heat into air) + if (rs_pMtrElec) + rs_pMtrElec->H.fanH += rs_inFan * Top.tp_subhrDur; + + rs_outSenTot = rs_outSen + rs_outDefrost + rs_outAux + rs_outFan; + } else if (rs_mode == rsmOAV) { + runFFan = rs_runF; + rs_outSenTot = rs_outFan = rs_inFan = rs_fanHeatOAV * rs_runF; + if (rs_pMtrElec) + rs_pMtrElec->H.fanC += rs_inFan * Top.tp_subhrDur; + } + // else if (rs_Mode == rsmOFF) + + if (rs_capSenNetFS != 0.f) { + rs_loadF = (rs_outSen + rs_outFan) / rs_capSenNetFS; + rs_PLR = rs_znLoad[0] / rs_capSenNetFS; +#if defined(_DEBUG) + if (rs_loadF < 0.f) + printf("\nNeg rs_loadF"); #endif - } + } - // parasitic consumption - if (rs_pMtrElec) - rs_pMtrElec->H.aux += rs_parElec * Top.tp_subhrDur * BtuperWh; - if (rs_pMtrFuel) - rs_pMtrFuel->H.aux += rs_parFuel * Top.tp_subhrDur; - - if (runFFan > 0.f) - { // duct conduction and leakage to adjacent zones - for (int iSR=0; iSR<2; iSR++) - { int iDS = rs_Dsi( iSR); - if (iDS>0) - DsR[ iDS].ds_FinalizeSh( runFFan); - } - } + // parasitic consumption + if (rs_pMtrElec) + rs_pMtrElec->H.aux += rs_parElec * Top.tp_subhrDur * BtuperWh; + if (rs_pMtrFuel) + rs_pMtrFuel->H.aux += rs_parFuel * Top.tp_subhrDur; - return RCOK; + if (runFFan > 0.f) { // duct conduction and leakage to adjacent zones + for (int iSR = 0; iSR < 2; iSR++) { + int iDS = rs_Dsi(iSR); + if (iDS > 0) + DsR[iDS].ds_FinalizeSh(runFFan); + } + } -} // RSYS::rs_FinalizeSh + return RCOK; + +} // RSYS::rs_FinalizeSh //============================================================================ -static RC loadsIzxSh1() // interzone transfers, part 1 +static RC loadsIzxSh1() // interzone transfers, part 1 // does all non-AirNet IZXFERs + infil AirNet // accumulates zone .zn_qIzShXAnSh's, assumed pre-0'd. { - RC rc=RCOK; - - if (Top.tp_airNetActive && Top.tp_pAirNet) // if any AirNet - { rc = Top.tp_pAirNet->an_Calc(0); // find all-zone pressure balance - if (rc == RCNOP) // if nothing done (no zones) - rc = RCOK; // treat as OK - } - - IZXRAT* ize; - RLUP( IzxR, ize) - { if (ize->iz_IsAirNet()) - ize->iz_ZoneXfers( 0); // accum heat flows to zone - else - ize->iz_Calc(); // calc non-AirNet transfers - // accum to ZNR.zn_qIzXAnSh's - } - - ZNR* zp; - RLUPC( ZrB, zp, !zp->zn_IsConvRad()) - zp->zn_qIzSh = zp->zn_qIzXAnSh - + zp->zn_AnAmfCpT( 0) - zp->zn_AnAmfCp( 0)*zp->tzls; - // else zn_qIzXAnSh used in CR heat balance eqns elsewhere - - return rc; -} // ::loadsIzxSh1 + RC rc = RCOK; + + if (Top.tp_airNetActive && Top.tp_pAirNet) // if any AirNet + { + rc = Top.tp_pAirNet->an_Calc(0); // find all-zone pressure balance + if (rc == RCNOP) // if nothing done (no zones) + rc = RCOK; // treat as OK + } + + IZXRAT *ize; + RLUP(IzxR, ize) { + if (ize->iz_IsAirNet()) + ize->iz_ZoneXfers(0); // accum heat flows to zone + else + ize->iz_Calc(); // calc non-AirNet transfers + // accum to ZNR.zn_qIzXAnSh's + } + + ZNR *zp; + RLUPC(ZrB, zp, !zp->zn_IsConvRad()) + zp->zn_qIzSh = + zp->zn_qIzXAnSh + zp->zn_AnAmfCpT(0) - zp->zn_AnAmfCp(0) * zp->tzls; + // else zn_qIzXAnSh used in CR heat balance eqns elsewhere + + return rc; +} // ::loadsIzxSh1 //----------------------------------------------------------------------------- -static RC loadsIzxSh2() // interzone transfers, part 2 +static RC loadsIzxSh2() // interzone transfers, part 2 // calcs AirNet vent // accumulates zone .zn_qIzSh's, assumed pre-0'd. { - RC rc=RCOK; - - if (Top.tp_airNetActive && Top.tp_pAirNet) - { rc = Top.tp_pAirNet->an_Calc( 1); // find pressure balance - if (rc == RCNOP) - rc = RCOK; - if (rc == RCOK) - { IZXRAT* ize; - RLUP(IzxR, ize) - { if (ize->iz_IsAirNet()) - ize->iz_ZoneXfers(1); // accum heat flows to zone - } - } - } - -#if defined( DEBUGDUMP) - if (DbDo( dbdAIRNET|dbdIZ)) - { ZNR* zp; - DbPrintf("\n"); - RLUP( ZrB, zp) - DbPrintf( "%s vent: MCpVent=%.2f MCpTVent=%.1f\n", - zp->Name(), - zp->zn_AnAmfCp( 1), zp->zn_AnAmfCpT( 1)); - } -#endif - return rc; -} // ::loadsIzxSh2 + RC rc = RCOK; + + if (Top.tp_airNetActive && Top.tp_pAirNet) { + rc = Top.tp_pAirNet->an_Calc(1); // find pressure balance + if (rc == RCNOP) + rc = RCOK; + if (rc == RCOK) { + IZXRAT *ize; + RLUP(IzxR, ize) { + if (ize->iz_IsAirNet()) + ize->iz_ZoneXfers(1); // accum heat flows to zone + } + } + } + +#if defined(DEBUGDUMP) + if (DbDo(dbdAIRNET | dbdIZ)) { + ZNR *zp; + DbPrintf("\n"); + RLUP(ZrB, zp) + DbPrintf("%s vent: MCpVent=%.2f MCpTVent=%.1f\n", zp->Name(), + zp->zn_AnAmfCp(1), zp->zn_AnAmfCpT(1)); + } +#endif + return rc; +} // ::loadsIzxSh2 //-------------------------------------------------------------------- -static RC loadsXFans() // SIMULATE zone exhaust fans (xfans) -{ - RC rc = RCOK; - ZNR* zp; - RLUP( ZrB, zp) - rc |= zp->zn_XFan(); - return rc; -} // ::loadsXFans +static RC loadsXFans() // SIMULATE zone exhaust fans (xfans) +{ + RC rc = RCOK; + ZNR *zp; + RLUP(ZrB, zp) + rc |= zp->zn_XFan(); + return rc; +} // ::loadsXFans //-------------------------------------------------------------------- -RC ZNR::zn_XFan() // zone exhaust fan calcs (hourly) +RC ZNR::zn_XFan() // zone exhaust fan calcs (hourly) { - float fOn = i.xfanFOn; - i.xfan.fn_puteVf( i.xfan.vfDs*fOn, tzlh, fOn); + float fOn = i.xfanFOn; + i.xfan.fn_puteVf(i.xfan.vfDs * fOn, tzlh, fOn); - if (i.xfan.fn_mtri) - MtrB.p[ i.xfan.fn_mtri].H.fan += i.xfan.p; + if (i.xfan.fn_mtri) + MtrB.p[i.xfan.fn_mtri].H.fan += i.xfan.p; - return RCOK; + return RCOK; -} // ZNR::zn_XFan +} // ZNR::zn_XFan //==================================================================== -static RC loadsSurfaces( // surface runtime simulation - BOO subhrly) // 0 to do hourly masses, 1 to do subhourly surfaces/masses +static RC loadsSurfaces( // surface runtime simulation + BOO subhrly) // 0 to do hourly masses, 1 to do subhourly surfaces/masses // called before zone balances { - RC rc = RCOK; - - // Init zone surface heat xfers to 0, loops below sums to them - ZNR* zp; - RLUP( ZrB, zp) // for zp = ZNR record 1 to .n - { if (subhrly) - { zp->zn_ieMassls = zp->zn_ieMass; // re determination of mass internal energy change - zp->aMassSh = zp->zn_hcATMsSh = zp->zn_hrATMsSh = zp->zn_hcAMsSh = zp->zn_hrAMsSh - = zp->zn_qCondQS = zp->zn_qCondMS = zp->zn_ieMass = 0.; - zp->qSgTotSh = zp->zn_sgTotShTarg.st_tot; - // total solar gain to zone, Btuh (redundant re reporting and bal checks) - // add'l may be added by surfaces, e.g. ASHWAT inward flowing fraction - } - else - { zp->aMassHr = 0.; - zp->qSgTot = zp->zn_sgTotTarg.st_tot; // see qSgTotSh comments just above - } - } - - if (subhrly) - { XSRAT* xr; - TMRSTART( TMR_BC); - RLUP (XsB, xr) - rc |= xr->x.xs_SubhrBC(); - TMRSTOP( TMR_BC); - - TMRSTART( TMR_AWTOT); // total ASHWAT time (incl setup) - // see also TMR_AWCALC - RLUPC(XsB, xr, xr->x.xs_IsASHWAT()) - rc |= xr->x.xs_ASHWAT(); - TMRSTOP( TMR_AWTOT); - - RLUPC(XsB, xr, xr->x.xs_ty != CTMXWALL) - rc |= xr->x.xs_SubhrQS(); - } - - float dur, tDbO; - if (subhrly) - { dur = Top.tp_subhrDur; // interval duration (hours) - tDbO = Top.tDbOShAv; // outdoor temp averaged over time interval - } - else // hourly - { dur = 1.f; - tDbO = Top.tDbOHrAv; - } - - // masses - TMRSTART( TMR_COND); - MSRAT* mse; - RLUPC( MsR, mse, subhrly == mse->isSubhrly) // do matching interval - { if (mse->ms_isFD) - mse->ms_StepFD(); // FD (forward_difference) model is only subhourly - else - mse->ms_StepMX( dur, tDbO); - } // mass loop - TMRSTOP( TMR_COND); - - // kiva instances - TMRSTART(TMR_KIVA); - - BOO kivaIsSubhourly = Top.tp_grndTimeStep == C_TSCH_SH; - - if (subhrly == kivaIsSubhourly) // Only enter if correct interval - { - KIVA* ki; - RLUP(KvR, ki) - { - ki->kv_Step(dur); - } - - // Loop over surfaces to assign weighted values and accummulate to zone balance - XSRAT* xr; - RLUP(XsB, xr) - { - rc |= xr->xr_ApplyKivaResults(); - } - - } - - // Loop over surfaces to accummulate to zone balance - XSRAT* xr; - RLUP(XsB, xr) - { - rc |= xr->xr_KivaZoneAccum(); - } - - TMRSTOP(TMR_KIVA); - - - return rc; -} // loadsSurfaces + RC rc = RCOK; + + // Init zone surface heat xfers to 0, loops below sums to them + ZNR *zp; + RLUP(ZrB, zp) // for zp = ZNR record 1 to .n + { + if (subhrly) { + zp->zn_ieMassls = + zp->zn_ieMass; // re determination of mass internal energy change + zp->aMassSh = zp->zn_hcATMsSh = zp->zn_hrATMsSh = zp->zn_hcAMsSh = + zp->zn_hrAMsSh = zp->zn_qCondQS = zp->zn_qCondMS = zp->zn_ieMass = 0.; + zp->qSgTotSh = zp->zn_sgTotShTarg.st_tot; + // total solar gain to zone, Btuh (redundant re reporting and bal checks) + // add'l may be added by surfaces, e.g. ASHWAT inward flowing fraction + } else { + zp->aMassHr = 0.; + zp->qSgTot = zp->zn_sgTotTarg.st_tot; // see qSgTotSh comments just above + } + } + + if (subhrly) { + XSRAT *xr; + TMRSTART(TMR_BC); + RLUP(XsB, xr) + rc |= xr->x.xs_SubhrBC(); + TMRSTOP(TMR_BC); + + TMRSTART(TMR_AWTOT); // total ASHWAT time (incl setup) + // see also TMR_AWCALC + RLUPC(XsB, xr, xr->x.xs_IsASHWAT()) + rc |= xr->x.xs_ASHWAT(); + TMRSTOP(TMR_AWTOT); + + RLUPC(XsB, xr, xr->x.xs_ty != CTMXWALL) + rc |= xr->x.xs_SubhrQS(); + } + + float dur, tDbO; + if (subhrly) { + dur = Top.tp_subhrDur; // interval duration (hours) + tDbO = Top.tDbOShAv; // outdoor temp averaged over time interval + } else // hourly + { + dur = 1.f; + tDbO = Top.tDbOHrAv; + } + + // masses + TMRSTART(TMR_COND); + MSRAT *mse; + RLUPC(MsR, mse, subhrly == mse->isSubhrly) // do matching interval + { + if (mse->ms_isFD) + mse->ms_StepFD(); // FD (forward_difference) model is only subhourly + else + mse->ms_StepMX(dur, tDbO); + } // mass loop + TMRSTOP(TMR_COND); + + // kiva instances + TMRSTART(TMR_KIVA); + + BOO kivaIsSubhourly = Top.tp_grndTimeStep == C_TSCH_SH; + + if (subhrly == kivaIsSubhourly) // Only enter if correct interval + { + KIVA *ki; + RLUP(KvR, ki) { ki->kv_Step(dur); } + + // Loop over surfaces to assign weighted values and accummulate to zone + // balance + XSRAT *xr; + RLUP(XsB, xr) { rc |= xr->xr_ApplyKivaResults(); } + } + + // Loop over surfaces to accummulate to zone balance + XSRAT *xr; + RLUP(XsB, xr) { rc |= xr->xr_KivaZoneAccum(); } + + TMRSTOP(TMR_KIVA); + + return rc; +} // loadsSurfaces //----------------------------------------------------------------------------- -RC XSURF::xs_SubhrBC() // subhour: surface boundary conditions -{ - RC rc = RCOK; - - // set surface coefficients - double areaEff = xs_IsASHWAT() ? xs_AreaGlazed() : xs_area; - xs_sbcI.sb_SetCoeffs( areaEff, uC); - xs_sbcO.sb_SetCoeffs( areaEff, uC); - if (xs_IsASHWAT()) - xs_pFENAW[ 0]->fa_FrameBC(); - return rc; -} // XSURF::xs_SubbrBC +RC XSURF::xs_SubhrBC() // subhour: surface boundary conditions +{ + RC rc = RCOK; + + // set surface coefficients + double areaEff = xs_IsASHWAT() ? xs_AreaGlazed() : xs_area; + xs_sbcI.sb_SetCoeffs(areaEff, uC); + xs_sbcO.sb_SetCoeffs(areaEff, uC); + if (xs_IsASHWAT()) + xs_pFENAW[0]->fa_FrameBC(); + return rc; +} // XSURF::xs_SubbrBC //----------------------------------------------------------------------------- -RC XSURF::xs_ASHWAT() // subhour calcs for ASHWAT fenestration +RC XSURF::xs_ASHWAT() // subhour calcs for ASHWAT fenestration // do NOT call for if !xs_IsASHWAT() { - RC rc = RCOK; + RC rc = RCOK; - ZNR& z = ZrB[ xs_GetZi(0)]; // zone on inside of surface + ZNR &z = ZrB[xs_GetZi(0)]; // zone on inside of surface - // interior shades: determine fraction closed - float fSC = xs_HasControlledShade() ? z.i.znSC : 0.f; // fraction closed - int bDoFrm = 1; - if (fSC < .999f) - { // shades open - xs_pFENAW[ 0]->fa_Subhr( 1.f - max( fSC, 0.f), bDoFrm); - bDoFrm = 0; // do frame only once! - } - if (fSC > .001f) - { // shades closed - xs_pFENAW[ 1]->fa_Subhr( min( fSC, 1.f), bDoFrm); - } - FPCHECK; + // interior shades: determine fraction closed + float fSC = xs_HasControlledShade() ? z.i.znSC : 0.f; // fraction closed + int bDoFrm = 1; + if (fSC < .999f) { // shades open + xs_pFENAW[0]->fa_Subhr(1.f - max(fSC, 0.f), bDoFrm); + bDoFrm = 0; // do frame only once! + } + if (fSC > .001f) { // shades closed + xs_pFENAW[1]->fa_Subhr(min(fSC, 1.f), bDoFrm); + } + FPCHECK; - return rc; -} // XSURF::xs_ASHWAT + return rc; +} // XSURF::xs_ASHWAT //----------------------------------------------------------------------------- -RC XSURF::xs_SubhrQS() // subhour quick surface -{ - // Only call for quick surfaces! - RC rc = RCOK; - - ZNR& z = ZrB[ xs_GetZi(0)]; // zone on inside of surface - if (xs_IsASHWAT()) - xs_pFENAW[ 0]->fa_FrameQS(); - else - { z.zn_AccumBalTermsQS( xs_area, xs_sbcI, xs_sbcO); - if (xs_sbcO.sb_zi) - { // interzone quick surface: handle adjacent zone - ZNR& zx = ZrB[ xs_sbcO.sb_zi]; - zx.zn_AccumBalTermsQS( xs_area, xs_sbcO, xs_sbcI); - } - } - - FPCHECK; - return rc; -} // XSURF::xs_SubhrQS +RC XSURF::xs_SubhrQS() // subhour quick surface +{ + // Only call for quick surfaces! + RC rc = RCOK; + + ZNR &z = ZrB[xs_GetZi(0)]; // zone on inside of surface + if (xs_IsASHWAT()) + xs_pFENAW[0]->fa_FrameQS(); + else { + z.zn_AccumBalTermsQS(xs_area, xs_sbcI, xs_sbcO); + if (xs_sbcO.sb_zi) { // interzone quick surface: handle adjacent zone + ZNR &zx = ZrB[xs_sbcO.sb_zi]; + zx.zn_AccumBalTermsQS(xs_area, xs_sbcO, xs_sbcI); + } + } + + FPCHECK; + return rc; +} // XSURF::xs_SubhrQS //----------------------------------------------------------------------------- -RC XSURF::xs_EndSubhr() // end-of-subhr calcs for surface -{ - RC rc = RCOK; - - // update adjacent temps from zone heat bal outcome - // (nop if not adjacent to zone) - - xs_sbcI.sb_SetTx(); - xs_sbcO.sb_SetTx(); - - ZNR& z = ZrB[ xs_GetZi( 0)]; - - if (xs_IsASHWAT()) - { xs_pFENAW[ 0]->fa_FrameEndSubhr(); - float fSC = xs_HasControlledShade() ? z.i.znSC : 0.f; // fraction closed - if (fSC < .999f) - xs_pFENAW[ 0]->fa_EndSubhr( 1.f - max( fSC, 0.f)); - if (fSC > .001f) - xs_pFENAW[ 1]->fa_EndSubhr( min( fSC, 1.f)); - } - else if (xs_msi != 0) - { MSRAT& ms = MsR[ xs_msi]; - xs_sbcI.sb_qSrf = ms.ms_pMM->mm_qSrf[ 0]; - xs_sbcO.sb_qSrf = ms.ms_pMM->mm_qSrf[ 1]; - - // accum to zone - // zn_ieMass and zn_qCondMS are experimental value re energy balance checking - // unused? as of 12-21-10 - z.zn_ieMass += ms.ms_InternalEnergy(); - double qSrfA = xs_area*xs_sbcO.sb_qSrf; // conduction at outside surface, Btuh - z.zn_qCondMS += qSrfA; - if (sfAdjZi) - { ZNR& zx = ZrB[ sfAdjZi]; - zx.zn_qCondMS -= qSrfA; - } - - if (Top.isEndHour) - { // last step of hour: store layer boundary temps for probes - MASSMODEL* pMM = ms.ms_pMM; - pMM->mm_FillBoundaryTemps( xs_tLrB, XSMXTLRB); - } - } - else if (xs_IsKiva()) - { - // Kiva accounting? - } - else - z.zn_EndSubhrQS( xs_area, uC, xs_sbcI, xs_sbcO); - - return rc; - -} // XSURF::xs_EndSubhr +RC XSURF::xs_EndSubhr() // end-of-subhr calcs for surface +{ + RC rc = RCOK; + + // update adjacent temps from zone heat bal outcome + // (nop if not adjacent to zone) + + xs_sbcI.sb_SetTx(); + xs_sbcO.sb_SetTx(); + + ZNR &z = ZrB[xs_GetZi(0)]; + + if (xs_IsASHWAT()) { + xs_pFENAW[0]->fa_FrameEndSubhr(); + float fSC = xs_HasControlledShade() ? z.i.znSC : 0.f; // fraction closed + if (fSC < .999f) + xs_pFENAW[0]->fa_EndSubhr(1.f - max(fSC, 0.f)); + if (fSC > .001f) + xs_pFENAW[1]->fa_EndSubhr(min(fSC, 1.f)); + } else if (xs_msi != 0) { + MSRAT &ms = MsR[xs_msi]; + xs_sbcI.sb_qSrf = ms.ms_pMM->mm_qSrf[0]; + xs_sbcO.sb_qSrf = ms.ms_pMM->mm_qSrf[1]; + + // accum to zone + // zn_ieMass and zn_qCondMS are experimental value re energy balance + // checking unused? as of 12-21-10 + z.zn_ieMass += ms.ms_InternalEnergy(); + double qSrfA = + xs_area * xs_sbcO.sb_qSrf; // conduction at outside surface, Btuh + z.zn_qCondMS += qSrfA; + if (sfAdjZi) { + ZNR &zx = ZrB[sfAdjZi]; + zx.zn_qCondMS -= qSrfA; + } + + if (Top.isEndHour) { // last step of hour: store layer boundary temps for + // probes + MASSMODEL *pMM = ms.ms_pMM; + pMM->mm_FillBoundaryTemps(xs_tLrB, XSMXTLRB); + } + } else if (xs_IsKiva()) { + // Kiva accounting? + } else + z.zn_EndSubhrQS(xs_area, uC, xs_sbcI, xs_sbcO); + + return rc; + +} // XSURF::xs_EndSubhr //----------------------------------------------------------------------------- -void XSURF::xs_AfterSubhr() // end-of-subhr calcs for surface +void XSURF::xs_AfterSubhr() // end-of-subhr calcs for surface { - // last step surface temps (w/o *e, re probes) - xs_sbcI.sb_tSrfls = xs_sbcI.sb_tSrf; - xs_sbcO.sb_tSrfls = xs_sbcO.sb_tSrf; -} // XSURF::xs_AfterSubhr + // last step surface temps (w/o *e, re probes) + xs_sbcI.sb_tSrfls = xs_sbcI.sb_tSrf; + xs_sbcO.sb_tSrfls = xs_sbcO.sb_tSrf; +} // XSURF::xs_AfterSubhr //----------------------------------------------------------------------------- -void XSURF::xs_AfterHour() // end-of-hour calcs for surface +void XSURF::xs_AfterHour() // end-of-hour calcs for surface { #if 0 if (xs_msi != 0) @@ -6881,66 +6808,69 @@ void XSURF::xs_AfterHour() // end-of-hour calcs for surface pMM->mm_FillBoundaryTemps( xs_tLrB, XSMXTLRB); } #endif -} // XSURF::xs_AfterHour +} // XSURF::xs_AfterHour //----------------------------------------------------------------------------- RC FC loadsAfterSubhr() -// end-subhour after-exprs/reports stuff for loads: set 'prior interval' variables etc -// if done sooner, probes come out wrong. +// end-subhour after-exprs/reports stuff for loads: set 'prior interval' +// variables etc if done sooner, probes come out wrong. { - RC rc=RCOK; + RC rc = RCOK; - // zones - ZNR* zp; - RLUP( ZrB, zp) - rc |= zp->zn_AfterSubhr(); + // zones + ZNR *zp; + RLUP(ZrB, zp) + rc |= zp->zn_AfterSubhr(); - // surfaces - XSRAT* xr; - RLUP( XsB, xr) - xr->x.xs_AfterSubhr(); + // surfaces + XSRAT *xr; + RLUP(XsB, xr) + xr->x.xs_AfterSubhr(); - // systems - RSYS* rs; - RLUP( RsR, rs) - rc |= rs->rs_AfterSubhr(); + // systems + RSYS *rs; + RLUP(RsR, rs) + rc |= rs->rs_AfterSubhr(); - DUCTSEG* ds; - RLUP( DsR, ds) - ds->ds_AfterSubhr(); + DUCTSEG *ds; + RLUP(DsR, ds) + ds->ds_AfterSubhr(); - return rc; -} // loadsAfterSubhr + return rc; +} // loadsAfterSubhr //------------------------------------------------------------------------------------- RC ZNR::zn_AfterSubhr() -// end-subhour after-exprs/reports stuff for loads: set 'prior interval' variables etc -// if done sooner, probes come out wrong. +// end-subhour after-exprs/reports stuff for loads: set 'prior interval' +// variables etc if done sooner, probes come out wrong. { -#if 0 && defined( _DEBUG) +#if 0 && defined(_DEBUG) if (Top.iHr == 7) printf("\niHr=%d", Top.iHr); #endif - tzlsDelta = tz - tzls; // last subhour t delta: may be used to extrapolate next tz - tzls = tz; // last subhr zone air temp for next subHour - wzlsDelta = wz - wzls; // last subhour w delta: may be used to extrapolate next wz - wzls = wz; // last subhr zone hum rat for next subHour - znXLGainLs = znXLGain; // last subhour excess latent gain: next subhour sensible condensation gain. 5-97. - // pz0 (pressure relative to patm at nomimal z=0) initially 0, then known from - // prior AirNet solution - zn_rho0ls = zn_Rho0(); // last subhour moist air density at tz, wz, pz0 - zn_trls = zn_tr; // last subhour radiant temp - zn_relHumls = zn_relHum; // lass subhour relative humdity - -#if 0 // unused, 9-12 + tzlsDelta = + tz - tzls; // last subhour t delta: may be used to extrapolate next tz + tzls = tz; // last subhr zone air temp for next subHour + wzlsDelta = + wz - wzls; // last subhour w delta: may be used to extrapolate next wz + wzls = wz; // last subhr zone hum rat for next subHour + znXLGainLs = znXLGain; // last subhour excess latent gain: next subhour + // sensible condensation gain. 5-97. + // pz0 (pressure relative to patm at nomimal z=0) initially 0, then known from + // prior AirNet solution + zn_rho0ls = zn_Rho0(); // last subhour moist air density at tz, wz, pz0 + zn_trls = zn_tr; // last subhour radiant temp + zn_relHumls = zn_relHum; // lass subhour relative humdity + +#if 0 // unused, 9-12 x zn_dryAirMassls = zn_dryAirMass; // last subhour dry air mass, lbm x zn_qsHvacls = zn_qsHvac; // last subhour hvac sensible power x zn_qlHvacls = zn_qlHvac; // last subhour hvac latent power #endif - zn_hcAirXls = i.zn_hcAirX; // last subhour air exchange rate - // for convection coefficient models + zn_hcAirXls = i.zn_hcAirX; // last subhour air exchange rate + // for convection coefficient models - // ducts: nothing needed (current step values are used (lagged) at beg - // of next step, then initialized) + // ducts: nothing needed (current step values are used (lagged) at beg + // of next step, then initialized) #if 0 // 7-17-92 FAILS cuz solar gains recalculated hourly only. x // default zone shade closure (former shutter fraction): whether movable window shades open or closed for next subhour. @@ -6952,57 +6882,57 @@ x i.znSC = (float)(zrs->qsMech < 0.); // 1.0 if cooling in previous subhour, x } #endif - zn_sysDepAirIls = zn_ductLkI; // total duct leak flow into this zone + zn_sysDepAirIls = zn_ductLkI; // total duct leak flow into this zone - zn_rsAmfRetLs = zn_rsAmfRet; + zn_rsAmfRetLs = zn_rsAmfRet; - return RCOK; -} // ZNR::zn_AfterSubhr + return RCOK; +} // ZNR::zn_AfterSubhr //------------------------------------------------------------------------------------- RC FC loadsAfterHour() // end-hour after-exprs/reports stuff for loads // set 'prior interval' variables etc: if done sooner, probes come out wrong. { - RC rc=RCOK; + RC rc = RCOK; - // surfaces - XSRAT* xr; - RLUP( XsB, xr) - xr->x.xs_AfterHour(); + // surfaces + XSRAT *xr; + RLUP(XsB, xr) + xr->x.xs_AfterHour(); - // zones - ZNR* zp; - RLUP( ZrB, zp) - rc |= zp->zn_AfterHour(); + // zones + ZNR *zp; + RLUP(ZrB, zp) + rc |= zp->zn_AfterHour(); - // systems - RSYS* rs; - RLUP( RsR, rs) - rc |= rs->rs_AfterHour(); + // systems + RSYS *rs; + RLUP(RsR, rs) + rc |= rs->rs_AfterHour(); - return rc; -} // loadsAfterHour + return rc; +} // loadsAfterHour //----------------------------------------------------------------------------- RC ZNR::zn_AfterHour() // end-hour after-exprs/reports stuff for loads // set 'prior interval' variables etc: if done sooner, probes come out wrong. { - // next hour's "end last hour" zone air temp - tzlh = tzls; // air temp from final step of hour (NOT average here). - // needed? used in main eqn in loadsHourBeg, but .tzls cd be used directly 1-92. + // next hour's "end last hour" zone air temp + tzlh = tzls; // air temp from final step of hour (NOT average here). + // needed? used in main eqn in loadsHourBeg, but .tzls cd be used + // directly 1-92. - zn_trlh = zn_trls; // ditto radiant temp + zn_trlh = zn_trls; // ditto radiant temp - zn_relHumlh = zn_relHumls; // ditto relative humidity - - // end of hour set points - // used re ramped setpoints for autosizing - zn_tzspHlh = zn_tzspH; - zn_tzspDlh = zn_tzspD; - zn_tzspClh = zn_tzspC; + zn_relHumlh = zn_relHumls; // ditto relative humidity + // end of hour set points + // used re ramped setpoints for autosizing + zn_tzspHlh = zn_tzspH; + zn_tzspDlh = zn_tzspD; + zn_tzspClh = zn_tzspC; #if 0 // nHrCool is present but not set: no longer works. x // zone shutter fraction for next hour: determine whether movable window shades open or closed next hour @@ -7012,17 +6942,22 @@ x x // ?? This is *NOT* the full CEC current algorithm. Where is the 1 degF hysteresis? #else // 7-17-92 - // default zone shade closure (former shutter fraction): whether movable window shades open or closed for next hour. - // note this code responds to net cooling, whereas CALRES responded to ANY cooling (zrh->nHrCool, not now implemented). - // subhourly defaulting desirable (rob's opinion); must duplicate loadsHourBeg sg code in loadsSubhr w znSC-change trigger. - if (!znSCF) // not if expression given by user - { - ZNRES_IVL_SUB *zrh = &ZnresB.p[ ss].curr.H; // point zone current subhour results in resrat - i.znSC = (float)(zrh->qsMech < -ABOUT0); // 1.0 if cooling (more than heating) in previous hour, else 0. - // -ABOUT0: no cooling if near 0 so not random when hvac off, 8-92. - } -#endif - return RCOK; -} // ZNR::zn_AfterHour + // default zone shade closure (former shutter fraction): whether movable + // window shades open or closed for next hour. note this code responds to net + // cooling, whereas CALRES responded to ANY cooling (zrh->nHrCool, not now + // implemented). subhourly defaulting desirable (rob's opinion); must + // duplicate loadsHourBeg sg code in loadsSubhr w znSC-change trigger. + if (!znSCF) // not if expression given by user + { + ZNRES_IVL_SUB *zrh = + &ZnresB.p[ss].curr.H; // point zone current subhour results in resrat + i.znSC = (float)(zrh->qsMech < + -ABOUT0); // 1.0 if cooling (more than heating) in previous + // hour, else 0. -ABOUT0: no cooling if near 0 so + // not random when hvac off, 8-92. + } +#endif + return RCOK; +} // ZNR::zn_AfterHour // end of cnloads.cpp From f9385661e293388d296b9d625ed86b93979a24e0 Mon Sep 17 00:00:00 2001 From: Phil Ahrenkiel Date: Thu, 4 Apr 2024 15:45:23 -0600 Subject: [PATCH 04/21] Define tempF tolerance. --- src/cnglob.h | 872 ++++++++++++++++++++++++++++-------------------- src/cnloads.cpp | 56 ++-- src/nummeth.cpp | 690 ++++++++++++++++++-------------------- 3 files changed, 867 insertions(+), 751 deletions(-) diff --git a/src/cnglob.h b/src/cnglob.h index dca3448fb..aada55f3e 100644 --- a/src/cnglob.h +++ b/src/cnglob.h @@ -6,27 +6,30 @@ // cnglob.h: Global definitions for CSE: include first in all files. /////////////////////////////////////////////////////////////////////////////// -#define GLOB // ifndef GLOB may be used to prevent duplicate compilation +#define GLOB // ifndef GLOB may be used to prevent duplicate compilation - -#include "cndefns.h" // configuration definitions - // contains preprocessor #xxx only: used in cnrecs.def +#include "cndefns.h" // configuration definitions + // contains preprocessor #xxx only: used in cnrecs.def // TODO (MP) enable warnings -#if CSE_OS==CSE_COMPILER_MSVC -#pragma warning( disable: 4793) // do not warn on 'vararg' causes native code generation ?C9? -#define _CRT_SECURE_NO_DEPRECATE // do not warn on "insecure" CRT functions (strcpy, ) ?C9? -#pragma warning( disable: 4996) // do not warn on ISO deprecated functions (stricmp, ) ?C9? -#pragma warning( disable: 4244 4305) // do not warn on double->float conversion -#pragma warning( disable: 4065) // do not warn if only 'default' in switch +#if CSE_OS == CSE_COMPILER_MSVC +#pragma warning(disable : 4793) // do not warn on 'vararg' causes native code + // generation ?C9? +#define _CRT_SECURE_NO_DEPRECATE // do not warn on "insecure" CRT functions + // (strcpy, ) ?C9? +#pragma warning(disable : 4996) // do not warn on ISO deprecated functions + // (stricmp, ) ?C9? +#pragma warning(disable : 4244 4305) // do not warn on double->float conversion +#pragma warning(disable : 4065) // do not warn if only 'default' in switch #else -#define _countof(array) (sizeof(array) / sizeof(array[0])) // Defines MSC macro -#endif // CSE_COMPILER_MSVC +#define _countof(array) (sizeof(array) / sizeof(array[0])) // Defines MSC macro +#endif // CSE_COMPILER_MSVC /*------------------------- Enhanced declarations --------------------------*/ -// following can't be in dtypes.h (without added conditionals) cuz of 16 or 32 bit size -typedef long long LLI; // signed 64 bit integer +// following can't be in dtypes.h (without added conditionals) cuz of 16 or 32 +// bit size +typedef long long LLI; // signed 64 bit integer typedef unsigned long long ULLI; /*---------------------- Windows definitions -------------------------------*/ @@ -48,61 +51,63 @@ typedef unsigned int BOOL; typedef unsigned short WORD; typedef char TCHAR; typedef unsigned char BYTE; -typedef void* PVOID; +typedef void *PVOID; typedef PVOID HANDLE; typedef HANDLE HWND; typedef HANDLE HINSTANCE; typedef HINSTANCE HMODULE; #endif -#define LOCAL static // for file-local functions: a clearer word than "static" -#define STATIC static // for local data; static un-doable for (former) debugging -#define REFDATA static // For readonly, pak-specific data which could be overlayed -#define CDEC // use compiler defaults for linkage, 3-16-10 -#define FC // eliminating all 32-bit fastcalls fixes rampant bad bcc32 4.0 compiles 3-3-94. - // tried -Od and -rd compile options at same time, found not necessary. - // gave up on trying to change individual FC's only b4 getting cul.cpp to complete imput compilation 3-94. +#define LOCAL static // for file-local functions: a clearer word than "static" +#define STATIC static // for local data; static un-doable for (former) debugging +#define REFDATA \ + static // For readonly, pak-specific data which could be overlayed +#define CDEC // use compiler defaults for linkage, 3-16-10 +#define FC // eliminating all 32-bit fastcalls fixes rampant bad bcc32 4.0 + // compiles 3-3-94. tried -Od and -rd compile options at same time, + // found not necessary. gave up on trying to change individual FC's + // only b4 getting cul.cpp to complete imput compilation 3-94. /*---------------------- return codes / error codes ------------------------*/ - // forward-ref types -struct XFILE; // Extended IO packet set up in dm by xfopen, used as arg to othr xiopak/xiochar calls -struct FILEINFO; // file info struct, dospak to xiopak -struct XFNINFO; // for xfScanFileName info return +struct XFILE; // Extended IO packet set up in dm by xfopen, used as arg to othr + // xiopak/xiochar calls +struct FILEINFO; // file info struct, dospak to xiopak +struct XFNINFO; // for xfScanFileName info return struct VROUTINFO; struct VROUTINFO5; struct STBK; struct CULT; // universal #includes -#undef LOGWIN // define to display screen messages to window (re WINorDLL) - // (not maintained, 9-12) +#undef LOGWIN // define to display screen messages to window (re WINorDLL) + // (not maintained, 9-12) -#include -#include +#include +#include +#include +#include #include +#include #include #include -#include -#include +#include #include -#include -#include -#if defined( USE_STDLIB) -#include +#if defined(USE_STDLIB) #include -#include -#include -#include #include +#include +#include +#include +#include #define WVect std::vector -typedef std::map< int, std::string> WMapIntStr; +typedef std::map WMapIntStr; typedef std::string WStr; #endif -#if defined( NODTYPES) +#if defined(NODTYPES) // fixed subset of dtypes.h // avoid circular use of dtypes.h in e.g. rcdef typedef short SI; @@ -117,66 +122,96 @@ typedef long LI; typedef unsigned long ULI; #endif typedef unsigned char UCH; -typedef struct { SI year; SI month; SI mday; SI wday; } IDATE; -typedef struct { SI hour; SI min; SI sec; } ITIME; +typedef struct { + SI year; + SI month; + SI mday; + SI wday; +} IDATE; +typedef struct { + SI hour; + SI min; + SI sec; +} ITIME; typedef short DOY; typedef unsigned SHOY; -typedef struct { SI year; SI month; SI mday; SI wday; SI hour; SI min; SI sec; } IDATETIME; +typedef struct { + SI year; + SI month; + SI mday; + SI wday; + SI hour; + SI min; + SI sec; +} IDATETIME; typedef time_t LDATETIME; #else -#include // portable data types. File auto generated by rcdef - // NOTE: < > required re rcdef build +#include // portable data types. File auto generated by rcdef + // NOTE: < > required re rcdef build #endif -typedef SI RC; // Return Code explenations on the return code section +typedef SI RC; // Return Code explenations on the return code section // RC Return Codes: integers returned by fcns to indicate call outcome // 0 indicates all OK so RC can be cumulatively or'd -const RC RCOK=0; // fcn succeeded. code assumes 0 value. -const RC RCFATAL = -1; // fcn encountered fatal error; current process should be abandoned. - // note (RCFATAL | x) == RCFATAL, code may assume -const RC RCBAD = -2; // fcn failed -- general -const RC RCBAD2 = -4; // alternate failure return, for use where needed; note RCBAD2|RCBAD == RCBAD. -// values 1 - 16384 reserved, overlap with MH_xxxx definitions (see msghans.h and ...) - -// values > 16384 reserved for local definition and use, e.g. RCUNSET in cueval.h -const RC RCUNSET = 16385; // returned by e.g. cuEvalxx if unset data accessed - // (eg by probe of a RAT member) - // -- caller may want to treat this error differently. -const RC RCCANNOT = 16386; // return code for "cannot do it, try another way" (see tryImInProbe()) -const RC RCNOP = 16390; // fcn did nothing (due to e.g. specific input values) -const RC RCEOF = 16391; // indicates end of input when returned by ppRead, ppM, ppC, etc -#define CSE_E(f) { rc = (f); if (rc) return rc; } // if fcn f returns error, return it to caller. Note RCOK is 0. -#define CSE_EF(f) { if (f) return RCBAD; } // "fast" variant: return RCBAD for any non-RCOK +const RC RCOK = 0; // fcn succeeded. code assumes 0 value. +const RC RCFATAL = + -1; // fcn encountered fatal error; current process should be abandoned. + // note (RCFATAL | x) == RCFATAL, code may assume +const RC RCBAD = -2; // fcn failed -- general +const RC RCBAD2 = -4; // alternate failure return, for use where needed; note + // RCBAD2|RCBAD == RCBAD. +// values 1 - 16384 reserved, overlap with MH_xxxx definitions (see +// msghans.h and ...) + +// values > 16384 reserved for local definition and use, e.g. RCUNSET in +// cueval.h +const RC RCUNSET = 16385; // returned by e.g. cuEvalxx if unset data accessed + // (eg by probe of a RAT member) + // -- caller may want to treat this error differently. +const RC RCCANNOT = 16386; // return code for "cannot do it, try another way" + // (see tryImInProbe()) +const RC RCNOP = 16390; // fcn did nothing (due to e.g. specific input values) +const RC RCEOF = + 16391; // indicates end of input when returned by ppRead, ppM, ppC, etc +#define CSE_E(f) \ + { \ + rc = (f); \ + if (rc) \ + return rc; \ + } // if fcn f returns error, return it to caller. Note RCOK is 0. +#define CSE_EF(f) \ + { \ + if (f) \ + return RCBAD; \ + } // "fast" variant: return RCBAD for any non-RCOK // SEC additional system error codes -- in addition to c library errno values. // must not conflict with errno values. See rmkerr:secMsg and xiopak.cpp. -typedef SI SEC; // system error code data type (matches type of msc c library "errno") -const SEC SECOK = -1; // system error code for no error -const SEC SECEOF = -2; // system error code for EOF (none def by MSC). -const SEC SECOTHER = -3; // system error code indicating that an error - // was signalled but errno was not set -const SEC SECBADFN = -4; // bad file name, detected in our code (not library). - // msc at least has no such msg. added by rob 1-88. */ -const SEC SECBADRV = -5; // bad drv letter (xiopak:chdir, likely other uses) -const SEC SECBADPATH = 2; // No file or directory - -typedef SI MH; // message handle, used in calls to err() and msg(), identifier for msgs in msgtab:msgTbl[] - - - +typedef SI + SEC; // system error code data type (matches type of msc c library "errno") +const SEC SECOK = -1; // system error code for no error +const SEC SECEOF = -2; // system error code for EOF (none def by MSC). +const SEC SECOTHER = -3; // system error code indicating that an error + // was signalled but errno was not set +const SEC SECBADFN = + -4; // bad file name, detected in our code (not library). + // msc at least has no such msg. added by rob 1-88. */ +const SEC SECBADRV = -5; // bad drv letter (xiopak:chdir, likely other uses) +const SEC SECBADPATH = 2; // No file or directory + +typedef SI MH; // message handle, used in calls to err() and msg(), identifier + // for msgs in msgtab:msgTbl[] // min and max (converted to template fcns, 10-12) #undef min #undef max -template< typename T, typename TX> inline T min(T a, TX b) -{ - return a < b ? a : b; +template inline T min(T a, TX b) { + return a < b ? a : b; } -template< typename T, typename TX> inline T max(T a, TX b) -{ - return a > b ? a : b; +template inline T max(T a, TX b) { + return a > b ? a : b; } inline double min(double a, double b, double c) { return min(min(a, b), c); } inline double max(double a, double b, double c) { return max(max(a, b), c); } @@ -189,259 +224,343 @@ inline double max(double a, double b, double c, double d, double e, double f) { /*------------- Error Actions and Options ("erOp" arguments) --------------*/ /*--- error reporting and handling options, for rmkerr and its many callers. */ - // error action: controls error:err response to error -const int IGN = 0x0001; // ignore: no message, silently continue execution -const int INF = 0x0004; // info -const int ERR = 0x0000; // error: msg to screen, error file, and err report if open, continue execution. - // 0, often omitted. +// error action: controls error:err response to error +const int IGN = 0x0001; // ignore: no message, silently continue execution +const int INF = 0x0004; // info +const int ERR = 0x0000; // error: msg to screen, error file, and err report if + // open, continue execution. + // 0, often omitted. const int REG = ERR; -const int WRN = 0x0002; // issue msg as ERR, continue -const int ABT = 0x0003; // issue msg, abort program. +const int WRN = 0x0002; // issue msg as ERR, continue +const int ABT = 0x0003; // issue msg, abort program. // const int KEYP = 0x0002; // keypress bit: on in WRN/ABT, off in IGN/INF/ERR - // eliminated 2-23-2023 -const int ERAMASK = 0x0007; // mask to clear all option/app bits for testing for IGN/WRN/ABT. general use. - -const int PROGERR = 0x0008; // program error bit: on in PWRN/PABT (removing makes WRN/ABT), off in others. -const int PERR = ERR | PROGERR; // program error (different msg wording) ERR -const int PWRN = WRN | PROGERR; // program error WRN -const int PABT = ABT | PROGERR; // program error ABT -const int NOPREF = 0x0010; // do not add "Error: "/"Program Error: " prefix to message text. 2-94. -const int SYSMSG = 0x0020; // Do GetLastError(), add system error msg -const int SHOFNLN = 0x0040; // show input file name and line # (orMsg) -const int RTMSG = 0x0080; // runtime format (include day/hour info in msg) (orMsg) -const int IGNX = 0x0100; // "extra ignore" -- no msg, do not return RCBAD (orMsg) +// eliminated 2-23-2023 +const int ERAMASK = 0x0007; // mask to clear all option/app bits for testing for + // IGN/WRN/ABT. general use. + +const int PROGERR = 0x0008; // program error bit: on in PWRN/PABT (removing + // makes WRN/ABT), off in others. +const int PERR = ERR | PROGERR; // program error (different msg wording) ERR +const int PWRN = WRN | PROGERR; // program error WRN +const int PABT = ABT | PROGERR; // program error ABT +const int NOPREF = 0x0010; // do not add "Error: "/"Program Error: " prefix to + // message text. 2-94. +const int SYSMSG = 0x0020; // Do GetLastError(), add system error msg +const int SHOFNLN = 0x0040; // show input file name and line # (orMsg) +const int RTMSG = + 0x0080; // runtime format (include day/hour info in msg) (orMsg) +const int IGNX = + 0x0100; // "extra ignore" -- no msg, do not return RCBAD (orMsg) const int ERRRT = ERR | RTMSG; const int WRNRT = WRN | RTMSG; -enum class MSGTY { msgtyUNKNOWN = 0, msgtyERROR, msgtyWARNING, msgtyINFO, msgtyDEBUG }; +enum class MSGTY { + msgtyUNKNOWN = 0, + msgtyERROR, + msgtyWARNING, + msgtyINFO, + msgtyDEBUG +}; // options for rmkErr:screen() and :logit() remark display -const int NONL = 0x1000; // do NOT force remark to the beginning of its own line (default: start each message on new line) -const int DASHES = 0x2000; // separate this remark from preceding & following message with ------------ -const int NOSCRN = 0x4000; // logit: do not also display on screen -const int QUIETIF = 0x8000; // screen: do not display if zn screenQuiet (rmkErr.cpp) +const int NONL = 0x1000; // do NOT force remark to the beginning of its own line + // (default: start each message on new line) +const int DASHES = 0x2000; // separate this remark from preceding & following + // message with ------------ +const int NOSCRN = 0x4000; // logit: do not also display on screen +const int QUIETIF = + 0x8000; // screen: do not display if zn screenQuiet (rmkErr.cpp) // options for errI() isWarn -const int NOERRFILE = 0x1000; // do NOT write message to error file (override default behavior) -const int NOREPORT = 0x2000; // do NOT write message to report file (override default behavior) -// const int NOSCRN = 0x4000; // do NOT write message to screen (override default behavior) - +const int NOERRFILE = + 0x1000; // do NOT write message to error file (override default behavior) +const int NOREPORT = + 0x2000; // do NOT write message to report file (override default behavior) +// const int NOSCRN = 0x4000; // do NOT write message to screen (override +// default behavior) // application option bits in same argument word as error action. // Options for specific functions are defined in other .h files in terms of // the following ---------- users include ------ -const int EROP1 = 0x010000; // vrpak.h dmpak.h xiopak.h [ratpak.h] -const int EROP2 = 0x020000; // vrpak.h dmpak.h [xiopak.h] sytb.cpp -const int EROP3 = 0x040000; // vrpak.h wfpak.h [xiopak.h] -const int EROP4 = 0x080000; // vrpak.h wfpak.h -const int EROP5 = 0x100000; // vrpak.h wfpak.h -const int EROP6 = 0x200000; // vrpak.h yacam.h -const int EROP7 = 0x400000; // pgpak.h [ratpak] -const int EROMASK = 0xff0000; // mask for application option bits +const int EROP1 = 0x010000; // vrpak.h dmpak.h xiopak.h [ratpak.h] +const int EROP2 = 0x020000; // vrpak.h dmpak.h [xiopak.h] sytb.cpp +const int EROP3 = 0x040000; // vrpak.h wfpak.h [xiopak.h] +const int EROP4 = 0x080000; // vrpak.h wfpak.h +const int EROP5 = 0x100000; // vrpak.h wfpak.h +const int EROP6 = 0x200000; // vrpak.h yacam.h +const int EROP7 = 0x400000; // pgpak.h [ratpak] +const int EROMASK = 0xff0000; // mask for application option bits /*----------------------------- constants ----------------------------------*/ #define TRUE 1 #define FALSE 0 /*--------------------- Definitions for CSE headers ------------------------*/ -typedef void* DMP; // Dynamic memory block pointer: ptr to any type, record struct, etc, of caller's -const int defaultCpl = 78; // default chars/line, used when Top.repCpl not available - // see getCpl() - +typedef void *DMP; // Dynamic memory block pointer: ptr to any type, record + // struct, etc, of caller's +const int defaultCpl = + 78; // default chars/line, used when Top.repCpl not available + // see getCpl() + /*----------------------------- CSE headers --------------------------------*/ -#include "dmpak.h" // Uses EROP1, EROP2, RC, DMP, IGN and ABT +#include "dmpak.h" // Uses EROP1, EROP2, RC, DMP, IGN and ABT +#include "psychro.h" #include "rmkerr.h" #include "strpak.h" -#include "psychro.h" -#include "vecpak.h" // Uses min and max definitions +#include "vecpak.h" // Uses min and max definitions /*---------------------- global macros and defines -------------------------*/ // useful constants -constexpr double kPi=3.14159265358979323846; -constexpr double k2Pi=2.*kPi; -constexpr double k4Pi=4.*kPi; -constexpr double kPiOver2=kPi/2.; -constexpr double g0Std = 32.2; // standard acceleration of gravity (g0 or "little g"), ft/sec2 -constexpr double sigmaSB = 0.1714e-8; // stefan-boltzman constant, Btuh/ft2-R4 -constexpr double sigmaSB4 = sigmaSB*4.; // 4 * ditto -constexpr double sigmaSBSI = 5.669e-8; // stefan-boltzman constant, W/m2-K4 -constexpr double tAbs0F // 0 F in Rankine -#if defined( CZM_COMPARE) - = 460.; +constexpr double kPi = 3.14159265358979323846; +constexpr double k2Pi = 2. * kPi; +constexpr double k4Pi = 4. * kPi; +constexpr double kPiOver2 = kPi / 2.; +constexpr double g0Std = + 32.2; // standard acceleration of gravity (g0 or "little g"), ft/sec2 +constexpr double sigmaSB = 0.1714e-8; // stefan-boltzman constant, Btuh/ft2-R4 +constexpr double sigmaSB4 = sigmaSB * 4.; // 4 * ditto +constexpr double sigmaSBSI = 5.669e-8; // stefan-boltzman constant, W/m2-K4 +constexpr double tAbs0F // 0 F in Rankine +#if defined(CZM_COMPARE) + = 460.; #else - = 459.67; + = 459.67; #endif - +constexpr double tol_tF = 1.e-12; // temperature tolerance (degF) /*------------------------------- math -------------------------------------*/ // error handling extern UINT doControlFP(); #if CSE_OS == CSE_OS_WINDOWS && CSE_ARCH == 32 -#define FPCHECK __asm { fwait } // trap pending FP exception (if any) - // re searching for FP exceptions +#define FPCHECK __asm { fwait } // trap pending FP exception (if any) + // re searching for FP exceptions #else #define FPCHECK ; #endif // floating point compare const double ABOUT0 = 0.001; -template< typename T> inline int fAboutEqual(T a, T b) { return fabs(a-b) < ABOUT0; } -template< typename T> inline T frDiff( T a, T b, T tolAbs=T(.00001)) -{ T d=abs(a-b); - return d<=max( tolAbs, T( 1e-30)) ? T( 0.) : T(2.)*d/(abs( a)+abs( b)); +template inline int fAboutEqual(T a, T b) { + return fabs(a - b) < ABOUT0; } -#define ABSCHANGE(is,was) (fabs((is)-(was))) // absolute change of float -#define RELCHANGE(is,was) (fabs( ((is)-(was)) / (fabs(is)+.1f) )) // relative change of float as fraction, w /0 protection +template inline T frDiff(T a, T b, T tolAbs = T(.00001)) { + T d = abs(a - b); + return d <= max(tolAbs, T(1e-30)) ? T(0.) : T(2.) * d / (abs(a) + abs(b)); +} +#define ABSCHANGE(is, was) (fabs((is) - (was))) // absolute change of float +#define RELCHANGE(is, was) \ + (fabs(((is) - (was)) / \ + (fabs(is) + \ + .1f))) // relative change of float as fraction, w /0 protection // setToXxx: formerly non-template floor( a, b) and ceil( a, b), 10-12 // setToMax: a = max( a, b) -template< typename T, typename TX> inline T setToMax( T &a, TX b) -{ if (b > a) a = T( b); return a; } +template inline T setToMax(T &a, TX b) { + if (b > a) + a = T(b); + return a; +} // setToMin: a = min( a, b) -template< typename T, typename TX> inline T setToMin( T &a, TX b) -{ if (b < a) a = T( b); return a; } +template inline T setToMin(T &a, TX b) { + if (b < a) + a = T(b); + return a; +} // setToMinOrMax: if doMin setToMin( a, b) else setToMax( a, b) -template< typename T, typename TX> inline T setToMinOrMax( T &a, TX b, int doMin) -{ if ((b > a)^doMin) a = T( b); return a; } +template +inline T setToMinOrMax(T &a, TX b, int doMin) { + if ((b > a) ^ doMin) + a = T(b); + return a; +} // return -1, 0, or 1 per value of v -template< typename T> T xsign( T v) { return v==0 ? 0 : v>0 ? 1 : -1; } +template T xsign(T v) { return v == 0 ? 0 : v > 0 ? 1 : -1; } // return -1 if <, 0 if == , or 1 if > -template< typename T> int LTEQGT( T v1, T v2) { return v1>v2 ? 1 : v1 int LTEQGT(T v1, T v2) { + return v1 > v2 ? 1 : v1 < v2 ? -1 : 0; +} // bracket: limit value betw vMin and vMax -template< typename T> inline T bracket( T vMin, T v, T vMax) -{ return v < vMin ? vMin : v > vMax ? vMax : v; } +template inline T bracket(T vMin, T v, T vMax) { + return v < vMin ? vMin : v > vMax ? vMax : v; +} // ifBracket: limit value, return 1 iff changed -template< typename T> inline int ifBracket( T vMin, T& v, T vMax) -{ if (v < vMin) { v = vMin; return 1; } - if (v > vMax) { v = vMax; return 1; } - return 0; +template inline int ifBracket(T vMin, T &v, T vMax) { + if (v < vMin) { + v = vMin; + return 1; + } + if (v > vMax) { + v = vMax; + return 1; + } + return 0; } // debugging aid: warn if limits invoked -template< typename T> inline T bracketWarn( T vMin, T v, T vMax) -{ T vx = bracket( vMin, v, vMax); - if (vx != v) - warn( "bracketWarn: hit limit\n"); - return vx; +template inline T bracketWarn(T vMin, T v, T vMax) { + T vx = bracket(vMin, v, vMax); + if (vx != v) + warn("bracketWarn: hit limit\n"); + return vx; +} +template inline void tswap(T v1, T v2) { + T vx = v1; + v1 = v2; + v2 = vx; } -template< typename T> inline void tswap( T v1, T v2) -{ T vx = v1; v1 = v2; v2 = vx; } // snap to specified value -template< typename T> int snap( T& v, T vSnap, double tol=1e-10) -{ if (fabs(v-vSnap) int snap(T &v, T vSnap, double tol = 1e-10) { + if (fabs(v - vSnap) < tol) { + v = vSnap; + return 1; + } + return 0; +} // snap to -1, 0, or 1 -template< typename T> inline T snapTrig( T& v, double tol=1e-10) -{ return snap( v, T( 0), tol) || snap( v, T( 1), tol) || snap( v, T( -1), tol); } -template< typename T> int snapRAD( T& v, double tol=.1) -{ return snap( v, T( 0), tol) - || v < T( 0) ? snap( v, T( -kPiOver2), tol) || snap( v, T( -kPi), tol) - : snap( v, T( kPiOver2), tol) || snap( v, T( kPi), tol); +template inline T snapTrig(T &v, double tol = 1e-10) { + return snap(v, T(0), tol) || snap(v, T(1), tol) || snap(v, T(-1), tol); } -template< typename T> int snapDEG( T& v, double tol=.1) -{ return snap( v, T( 0), tol) - || v < T( 0) ? snap( v, T( -90), tol) || snap( v, T( -180), tol) - : snap( v, T( 90), tol) || snap( v, T( 180), tol); +template int snapRAD(T &v, double tol = .1) { + return snap(v, T(0), tol) || v < T(0) + ? snap(v, T(-kPiOver2), tol) || snap(v, T(-kPi), tol) + : snap(v, T(kPiOver2), tol) || snap(v, T(kPi), tol); +} +template int snapDEG(T &v, double tol = .1) { + return snap(v, T(0), tol) || v < T(0) + ? snap(v, T(-90), tol) || snap(v, T(-180), tol) + : snap(v, T(90), tol) || snap(v, T(180), tol); } // round v to nearest rv (e.g. roundNearest( 14., 5.) -> 15. -template< typename T> inline void roundNearest( T &v, T rv) -{ v = round( v/rv)*rv; } +template inline void roundNearest(T &v, T rv) { + v = round(v / rv) * rv; +} // round double to integer (no overflow protection!) -inline int iRound( double a) { return int( a > 0. ? a+.5 : a-.5 ); } +inline int iRound(double a) { return int(a > 0. ? a + .5 : a - .5); } // return v if not nan, else alternative value -template< typename T> inline T ifNotNaN( T v, T vForNaN=0) -{ return isnan( v) ? vForNaN : v; } +template inline T ifNotNaN(T v, T vForNaN = 0) { + return isnan(v) ? vForNaN : v; +} //----------------------------------------------------------------------------- // access to interval data // returns ref to array mbr for C_IVLCH_H/D/M/Y -template< typename T> T& IvlData( T* ivlData, int ivl) -{ -#if defined( _DEBUG) - return ivlData[ bracketWarn( 0, ivl-C_IVLCH_Y, IVLDATADIM-1) ]; +template T &IvlData(T *ivlData, int ivl) { +#if defined(_DEBUG) + return ivlData[bracketWarn(0, ivl - C_IVLCH_Y, IVLDATADIM - 1)]; #else - return ivlData[ ivl-C_IVLCH_Y]; + return ivlData[ivl - C_IVLCH_Y]; #endif -} // IvlData -#if 0 // idea +} // IvlData +#if 0 // idea 0 template< typename T> void SetIvlData( T* ivlData, T v, int ivl1, ivl2) 0 { VSet( ivlData+ivl1-C_IVLCH_Y, ivl2-ivl1+1, v); 0 } #endif -/*---------------------------- dept of NANs: unset, nandles, nchoices ---------------------------*/ +/*---------------------------- dept of NANs: unset, nandles, nchoices + * ---------------------------*/ -// a NANDLE is a 32-bit quantity that is not a valid IEEE float number nor an expected string pointer value, -// used to indicate unset data and to specify the "expression number" of runtime expression entered for the variable -// or whose value is to be stored in the variable later. More discussion in exman.h, exman.cpp. -// A NANDLE contains 0xFF80 in the hi word and 0 for unset or ffff for autosizing or expression number 1-16383 in the low word. -// CAUTION: highly system dependent. CAUTION: keep distinct from NCHOICEs (below) +// a NANDLE is a 32-bit quantity that is not a valid IEEE float number nor an +// expected string pointer value, used to indicate unset data and to specify the +// "expression number" of runtime expression entered for the variable +// or whose value is to be stored in the variable later. More discussion in +// exman.h, exman.cpp. +// A NANDLE contains 0xFF80 in the hi word and 0 for unset or ffff for +// autosizing or expression number 1-16383 in the low word. CAUTION: highly +// system dependent. CAUTION: keep distinct from NCHOICEs (below) // type to hold a NANDLE or a datum -typedef uint32_t NANDAT; // 32 bit unsigned integer -#define NANDLE(h) (static_cast(0xff800000 + h)) // "expr n" ref for float/int (or SI in 4 bytes). h = 1..16383. -#define UNSET (NANDLE(0)) // "unset" value for float/int. cast as desired. -#define ASING (NANDLE(0xffff)) // may be stored in values to be determined by autosizing 6-95 -template inline NANDAT AsNANDAT(T& v) { return *reinterpret_cast(&v); } -#define ISUNSET( v) (AsNANDAT( v)==UNSET) // true iff v is UNSET -#define ISASING( v) (AsNANDAT( v)==ASING) // true iff v is "to be autosized" -#define ISNANDLE( v) ((AsNANDAT( v) & 0xffff0000L)==0xff800000L) // true iff v is ref to non-constant expr (or unset) -#define ISNUM(v) ((AsNANDAT(v) & 0x7f800000L) != 0x7f800000L) // true iff float v is number (not UNSET, NANDLE, NCHOICE or other NAN) -#define ISNANDLEP(pV) ((*(reinterpret_cast(pV)) & 0xffff0000L)==0xff800000L) // test for ptr to ref to non-constant expr (or unset) -#define EXN(v) (AsNANDAT(v) & 0xffff) // extract expression # from nandle - - -// macro to access a float that may contain a NAN: don't let compiler treat as floats til numeric content verified. -// usage: CSE_V x = CSE_V y; if (CSE_V x == CSE_V y) .. where x and y are floats such as CHOICN's. +typedef uint32_t NANDAT; // 32 bit unsigned integer +#define NANDLE(h) \ + (static_cast( \ + 0xff800000 + \ + h)) // "expr n" ref for float/int (or SI in 4 bytes). h = 1..16383. +#define UNSET (NANDLE(0)) // "unset" value for float/int. cast as desired. +#define ASING \ + (NANDLE( \ + 0xffff)) // may be stored in values to be determined by autosizing 6-95 +template inline NANDAT AsNANDAT(T &v) { + return *reinterpret_cast(&v); +} +#define ISUNSET(v) (AsNANDAT(v) == UNSET) // true iff v is UNSET +#define ISASING(v) (AsNANDAT(v) == ASING) // true iff v is "to be autosized" +#define ISNANDLE(v) \ + ((AsNANDAT(v) & 0xffff0000L) == \ + 0xff800000L) // true iff v is ref to non-constant expr (or unset) +#define ISNUM(v) \ + ((AsNANDAT(v) & 0x7f800000L) != \ + 0x7f800000L) // true iff float v is number (not UNSET, NANDLE, NCHOICE or + // other NAN) +#define ISNANDLEP(pV) \ + ((*(reinterpret_cast(pV)) & 0xffff0000L) == \ + 0xff800000L) // test for ptr to ref to non-constant expr (or unset) +#define EXN(v) (AsNANDAT(v) & 0xffff) // extract expression # from nandle + +// macro to access a float that may contain a NAN: don't let compiler treat as +// floats til numeric content verified. usage: CSE_V x = CSE_V y; if (CSE_V x +// == CSE_V y) .. where x and y are floats such as CHOICN's. #define CSE_V *(NANDAT *)& // NCHOICEs are values which can represent one of several choices and which can // be stored in a float and distinguished from all numeric values. 2-92. -// NCHOICEs are used in CHOICN data types, definable thru rcdef.exe per input file cndtypes.def. -// NCHOICES are stored as IEEE NAN's (not-a-number's) in the form 0x7f80 plus the choice number 1-7f in the hi word. -// NCHOICES must be kept distinct from UNSET and NANDLEs (hi word ff8x) (above/exman.h). +// NCHOICEs are used in CHOICN data types, definable thru rcdef.exe per input +// file cndtypes.def. NCHOICES are stored as IEEE NAN's (not-a-number's) in the +// form 0x7f80 plus the choice number 1-7f in the hi word. NCHOICES must be +// kept distinct from UNSET and NANDLEs (hi word ff8x) (above/exman.h). // CAUTION: to test a float's bit pattern, cast to a pointer then to ULI. -// Casting directly to ULI causes its numeric value to be converted, altering the bit pattern +// Casting directly to ULI causes its numeric value to be converted, altering +// the bit pattern -// macro to fetch/store into variable n's hi word. Use w 16-bit flag/choice # dtypes.h C_DTYPE_XXXX constants gen'd by rcdef. +// macro to fetch/store into variable n's hi word. Use w 16-bit flag/choice # +// dtypes.h C_DTYPE_XXXX constants gen'd by rcdef. // usage: float y; CHN(y) = C_ABCNC_X; if (CHN(y)==C_ABCNC_X) ... -#define CHN(n) (*((uint16_t*)&(n)+1)) // access hi word, lvalue use ok. 80x86 DEPENDENT. PCMS<--grep target. +#define CHN(n) \ + (*((uint16_t *)&(n) + 1)) // access hi word, lvalue use ok. 80x86 DEPENDENT. + // PCMS<--grep target. -#define NCNAN 0x7f80 // bits that make nchoice a nan; is combined with choice index 1-7f to form stored value +#define NCNAN \ + 0x7f80 // bits that make nchoice a nan; is combined with choice index 1-7f to + // form stored value // macro to test if n has an NCHOICE value: -#define ISNCHOICE(n) ((AsNANDAT(n) & 0xff800000L)==0x7f800000L) -// macro to generate 32-bit value from 16-bit choice constants, for use where full value needed, as in initialized data +#define ISNCHOICE(n) ((AsNANDAT(n) & 0xff800000L) == 0x7f800000L) +// macro to generate 32-bit value from 16-bit choice constants, for use where +// full value needed, as in initialized data // usage: float y = NCHOICE(C_ABCNC_X); -#define NCHOICE(nck) (NANDAT(static_cast(nck) << 16)) // put in hi word. nck must include 0x7f80. - +#define NCHOICE(nck) \ + (NANDAT(static_cast(nck) \ + << 16)) // put in hi word. nck must include 0x7f80. // ------------------------- Debug aid ASSERT macro ------------------------- // // Issues message and terminates program if argument is false. -// Like compiler library "assert" macro (assert.h), but exits our way so DDE is properly terminated, etc -// (otherwise windows program dissappears from screen but does not actually exit promptly, -// necessitating Windows restart to reRun program.) 8-95. +// Like compiler library "assert" macro (assert.h), but exits our way so DDE is +// properly terminated, etc (otherwise windows program dissappears from screen +// but does not actually exit promptly, necessitating Windows restart to reRun +// program.) 8-95. -#ifdef NDEBUG // (un)def above - #define ASSERT(p) ((void)0) // if ommitting assertion checks +#ifdef NDEBUG // (un)def above +#define ASSERT(p) ((void)0) // if ommitting assertion checks #else - #define ASSERT(p) ((p) ? (void)0 : ourAssertFail( #p, __FILE__, __LINE__)) // function in rmkerr.cpp +#define ASSERT(p) \ + ((p) ? (void)0 \ + : ourAssertFail(#p, __FILE__, __LINE__)) // function in rmkerr.cpp #endif // compile-time assert (ugly but finds errors) -#define STATIC_ASSERT( condition, name ) \ - typedef char assert_failed_ ## name[ (condition) ? 1 : -1 ]; +#define STATIC_ASSERT(condition, name) \ + typedef char assert_failed_##name[(condition) ? 1 : -1]; // check floating point value // calls function that does _isnan, _finite, etc -#if defined( _DEBUG) -extern int CheckFP( double v, const char* tag); -#define CHECKFP( v) CheckFP( v, #v); +#if defined(_DEBUG) +extern int CheckFP(double v, const char *tag); +#define CHECKFP(v) CheckFP(v, #v); #else // compile to nothing in release -#define CHECKFP( v) +#define CHECKFP(v) #endif -#if 0 && !defined( _DEBUG) +#if 0 && !defined(_DEBUG) // Intrinsics // DEBUG build slightly slower (2%?) with no intrinsics and possibly easier to debug // intrinsics @@ -455,133 +574,152 @@ extern int CheckFP( double v, const char* tag); // (did not test, but expect pow-like advantage) // memset 15 19 (does not win) // strlen 12 17 (does not win) -#pragma intrinsic( fabs, abs, labs, pow, fmod, acos, asin, cosh, sinh, tanh) +#pragma intrinsic(fabs, abs, labs, pow, fmod, acos, asin, cosh, sinh, tanh) // release build does not use intrinsics w/o #pragma // changes debug build to alternative call -#pragma intrinsic( sin, cos, tan, atan, atan2, exp, log, log10, sqrt) +#pragma intrinsic(sin, cos, tan, atan, atan2, exp, log, log10, sqrt) /* intrinsics which don't win in size (7-3-89 MSC 5.0): memcpy (24 -> 43), memcmp (24 -> 55), strlen (18 -> 24), strcpy (20 -> 55), strcat (20 -> 64), min, max (error: "not available as intrinsic") */ -#endif // _DEBUG - +#endif // _DEBUG // COMMONLY USED TYPES and defines -typedef USI RCT; // record type type. Needed for RATBASE defn below, for ratpak.h, and rccn.h. -struct SFIR; // small fields-in-record info table (for basAnc.fir, SRD.fir). struct in srd.h. -#define DMPP( p) ((DMP *)DMP( &(p))) -inline void IncP(void** pp, int b) { *pp = (void*)((char*)(*pp) + b); } +typedef USI RCT; // record type type. Needed for RATBASE defn below, for + // ratpak.h, and rccn.h. +struct SFIR; // small fields-in-record info table (for basAnc.fir, SRD.fir). + // struct in srd.h. +#define DMPP(p) ((DMP *)DMP(&(p))) +inline void IncP(void **pp, int b) { *pp = (void *)((char *)(*pp) + b); } class record; -extern class TOPRAT Top; // top-level record universally accessible -typedef USI PSOP; // type for pseudo-code opcodes -- used in sevaral .cpp files and in cuparse.h -namespace Pumbra { class Penumbra; } -namespace Kiva { class Instance; class Aggregator; class Foundation; } -namespace Btwxt { class RegularGridInterpolator; } +extern class TOPRAT Top; // top-level record universally accessible +typedef USI PSOP; // type for pseudo-code opcodes -- used in sevaral .cpp files + // and in cuparse.h +namespace Pumbra { +class Penumbra; +} +namespace Kiva { +class Instance; +class Aggregator; +class Foundation; +} // namespace Kiva +namespace Btwxt { +class RegularGridInterpolator; +} #ifdef WINorDLL - // control of scattered code for returning file names used via a cse() argument. - #define OUTPNAMS // expect to test this in cse.cpp, rmkerr.cpp, vrpak.cpp, etc. 10-6-93 +// control of scattered code for returning file names used via a cse() argument. +#define OUTPNAMS // expect to test this in cse.cpp, rmkerr.cpp, vrpak.cpp, etc. + // 10-6-93 #endif // cse.h. keep after OUTPNAMS define! #ifdef OUTPNAMS - void FC saveAnOutPNam( const char* pNam); // accepts output file name to return to caller +void FC +saveAnOutPNam(const char *pNam); // accepts output file name to return to caller #endif // for init/cleanup procedure args 10-93 -enum CLEANCASE // caution code assumes STARTUP < ENTRY < others. -{ /*STARTUP=1,*/ // possible future: initializing, at DLL entry - ENTRY=2, // initializing at user call to subr package. May follow a previous "fatal" error exit. - DONE, // cleanup from normal completion path, after success or error - CRASH // cleanup after longjmp out after "fatal" error (that terminated program prior to 10-93) - #ifdef DLL - /*, CLOSEDOWN */ // possible future: terminating library, from WEP procedure - #endif +enum CLEANCASE // caution code assumes STARTUP < ENTRY < others. +{ /*STARTUP=1,*/ // possible future: initializing, at DLL entry + ENTRY = 2, // initializing at user call to subr package. May follow a previous + // "fatal" error exit. + DONE, // cleanup from normal completion path, after success or error + CRASH // cleanup after longjmp out after "fatal" error (that terminated + // program prior to 10-93) +#ifdef DLL +/*, CLOSEDOWN */ // possible future: terminating library, from WEP procedure +#endif }; -// stored in place of record subscript for special words entered, where permitted. Code assumes negative. +// stored in place of record subscript for special words entered, where +// permitted. Code assumes negative. // cul.cpp, cucnultx.cpp, cgresult.cpp, etc 1-92 -#define TI_SUM -3 // "sum" entered: use sum of all (zones, etc), not a specific one. -#define TI_ALL -4 // "all" entered: do report, warmest zone control, etc for all records (zones, meters, etc). -#define TI_ALLBUT -5 // "all_but" entered (at start of list of TI's). +#define TI_SUM \ + -3 // "sum" entered: use sum of all (zones, etc), not a specific one. +#define TI_ALL \ + -4 // "all" entered: do report, warmest zone control, etc for all records + // (zones, meters, etc). +#define TI_ALLBUT -5 // "all_but" entered (at start of list of TI's). /*------------------------- COMMONLY USED FUNCTIONS ------------------------*/ // here to reduce need to include files. // cncult.cpp (or stub in e.g. rcdef.cpp) -int getCpl( class TOPRAT** pTp=NULL); // get chars/line - // defaultCpl if Top not yet init & input value unavailable +int getCpl(class TOPRAT **pTp = NULL); // get chars/line + // defaultCpl if Top not yet init & input + // value unavailable // re DLL interrupt (in cse.cpp, stub in rcdef.cpp) int CheckAbort(); // messages.cpp -const char* msg( char* mBuf, MSGORHANDLE mOrH, ...); +const char *msg(char *mBuf, MSGORHANDLE mOrH, ...); // commonly used pow variants -inline float pow2( float v) { return v*v; } -inline double pow2( double v) { return v*v; } -inline float pow3( float v) { return v*v*v; } -inline double pow3( double v) { return v*v*v; } -inline float pow4( float v) { return v*v*v*v; } -inline double pow4( double v) { return v*v*v*v; } -inline float pow033( float v) -{ -#if defined( FASTPOW) - ? +inline float pow2(float v) { return v * v; } +inline double pow2(double v) { return v * v; } +inline float pow3(float v) { return v * v * v; } +inline double pow3(double v) { return v * v * v; } +inline float pow4(float v) { return v * v * v * v; } +inline double pow4(double v) { return v * v * v * v; } +inline float pow033(float v) { +#if defined(FASTPOW) + ? #else - return powf( v, .333f); + return powf(v, .333f); #endif } -inline float DegFtoR( float f) { return float( f+tAbs0F); } -inline double DegFtoR( double f) { return f+tAbs0F; } -inline float DegRtoF( float r) { return float( r-tAbs0F); } -inline double DegRtoF( double r) { return r-tAbs0F; } -inline float DegFtoC( float f) { return float( (f-32.)/1.8); } -inline double DegFtoC( double f) { return (f-32.)/1.8; } -inline float DegCtoF( float c) { return float( c*1.8+32.); } -inline double DegCtoF( double c) { return c*1.8+32.; } -inline float DegCtoK(float c) { return float(c + 273.15); } -inline double DegCtoK(double c) { return c + 273.15; } -inline constexpr float DegFtoK(float f) { return float((f + tAbs0F) / 1.8); } -inline double DegFtoK( double f) { return (f+tAbs0F)/1.8; } -inline float DegKtoF( float k) { return float( k*1.8-tAbs0F); } -inline double DegKtoF( double k) { return k*1.8-tAbs0F; } - - -inline double QRad( double t1, double t2) // radiant power betw 2 black surfaces -{ return sigmaSB*(pow4( DegFtoR( t1))-pow4(DegFtoR( t2))); } +inline float DegFtoR(float f) { return float(f + tAbs0F); } +inline double DegFtoR(double f) { return f + tAbs0F; } +inline float DegRtoF(float r) { return float(r - tAbs0F); } +inline double DegRtoF(double r) { return r - tAbs0F; } +inline float DegFtoC(float f) { return float((f - 32.) / 1.8); } +inline double DegFtoC(double f) { return (f - 32.) / 1.8; } +inline float DegCtoF(float c) { return float(c * 1.8 + 32.); } +inline double DegCtoF(double c) { return c * 1.8 + 32.; } +inline float DegCtoK(float c) { return float(c + 273.15); } +inline double DegCtoK(double c) { return c + 273.15; } +inline constexpr float DegFtoK(float f) { return float((f + tAbs0F) / 1.8); } +inline double DegFtoK(double f) { return (f + tAbs0F) / 1.8; } +inline float DegKtoF(float k) { return float(k * 1.8 - tAbs0F); } +inline double DegKtoF(double k) { return k * 1.8 - tAbs0F; } + +inline double QRad(double t1, double t2) // radiant power betw 2 black surfaces +{ + return sigmaSB * (pow4(DegFtoR(t1)) - pow4(DegFtoR(t2))); +} // Radians <--> degrees -template inline T DEG( T r) { return T( r*180./kPi); } -template inline T RAD( T d) { return T( d*kPi/180.); } +template inline T DEG(T r) { return T(r * 180. / kPi); } +template inline T RAD(T d) { return T(d * kPi / 180.); } // Btuh/ft2-F <--> W/m2-K -const double cfU = 5.678263; // W/m2-K per Btuh/ft2-F -inline float UIPtoSI( float u) { return float( u*cfU); } -inline double UIPtoSI( double u) { return u*cfU; } -inline float USItoIP( float u) { return float( u/cfU); } -inline double USItoIP( double u) { return u/cfU; } +const double cfU = 5.678263; // W/m2-K per Btuh/ft2-F +inline float UIPtoSI(float u) { return float(u * cfU); } +inline double UIPtoSI(double u) { return u * cfU; } +inline float USItoIP(float u) { return float(u / cfU); } +inline double USItoIP(double u) { return u / cfU; } // Irradiance Btuh/ft2 <--> W/m2 const double cfIr = 3.154591; -inline float IrIPtoSI( float ir) { return float( ir*cfIr); } -inline double IrIPtoSI( double ir) { return ir*cfIr; } -inline float IrSItoIP( float ir) { return float( ir/cfIr); } -inline double IrSItoIP( double ir) { return ir/cfIr; } +inline float IrIPtoSI(float ir) { return float(ir * cfIr); } +inline double IrIPtoSI(double ir) { return ir * cfIr; } +inline float IrSItoIP(float ir) { return float(ir / cfIr); } +inline double IrSItoIP(double ir) { return ir / cfIr; } // length ft <--> m -const double cfL = 1./3.28084; // m/ft -inline float LIPtoSI( float l) { return float( l*cfL); } -inline double LIPtoSI( double l) { return l*cfL; } -inline float LSItoIP( float l) { return float( l/cfL); } -inline double LSItoIP( double l) { return l/cfL; } +const double cfL = 1. / 3.28084; // m/ft +inline float LIPtoSI(float l) { return float(l * cfL); } +inline double LIPtoSI(double l) { return l * cfL; } +inline float LSItoIP(float l) { return float(l / cfL); } +inline double LSItoIP(double l) { return l / cfL; } // area ft^2 <--> m^2 -const double cfA = cfL*cfL; // m2/ft2 +const double cfA = cfL * cfL; // m2/ft2 inline float AIPtoSI(float a) { return float(a * cfA); } inline double AIPtoSI(double a) { return a * cfA; } inline float ASItoIP(float a) { return float(a / cfA); } @@ -615,47 +753,52 @@ inline float MFRSItoIP(float mfr) { return mfr * cfMFR; } inline double MFRIPtoSI(double mfr) { return float(mfr / cfMFR); } inline double MFRSItoIP(double mfr) { return mfr / cfMFR; } - // velocity mph <-> m/s -const double cfV = 1./2.23694; // m/s / mph -template< typename T> inline T VIPtoSI(T mph) { return T( mph*cfV); } -template< typename T> inline T VSItoIP(T ms) { return T( ms/cfV); } +const double cfV = 1. / 2.23694; // m/s / mph +template inline T VIPtoSI(T mph) { return T(mph * cfV); } +template inline T VSItoIP(T ms) { return T(ms / cfV); } // air mass flow <--> air volume flow // amf in lbm/hr // cfm in ft3/min std air -inline double AMFtoAVF( double amf) { return amf / (60.*.075); } -inline float AMFtoAVF( float amf) { return amf / (60.f*.075f); } -inline double AVFtoAMF( double avf) { return avf * 60.*.075; } -inline float AVFtoAMF( float avf) { return avf * 60.f*.075f; } +inline double AMFtoAVF(double amf) { return amf / (60. * .075); } +inline float AMFtoAVF(float amf) { return amf / (60.f * .075f); } +inline double AVFtoAMF(double avf) { return avf * 60. * .075; } +inline float AVFtoAMF(float avf) { return avf * 60.f * .075f; } // amf in lbm/s -inline float AMFtoAVF2( float amf) { return amf * 60.f / .075f; } - +inline float AMFtoAVF2(float amf) { return amf * 60.f / .075f; } // Powers of 10 -const int PTENSIZE = 21; // # of pwrs of 10 in Pten array -const double Pten[PTENSIZE] = {1.,10.,1e2,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13,1e14,1e15,1e16,1e17,1e18,1e19,1e20}; -const int NPTENSIZE = 21; // # of negative pwrs of 10 in NPten array -const double NPten[NPTENSIZE] = {1.,.1,.01,1e-3,1e-4,1e-5,1e-6,1e-7,1e-8,1e-9,1e-10,1e-11,1e-12,1e-13,1e-14,1e-15,1e-16,1e-17,1e-18,1e-19,1e-20 }; - -const float FHuge = float(1.E38); // Handy large (not strictly largest) value used for x/0, - // search initializations, etc. Single defn for portability and code clarity. +const int PTENSIZE = 21; // # of pwrs of 10 in Pten array +const double Pten[PTENSIZE] = {1., 10., 1e2, 1e3, 1e4, 1e5, 1e6, + 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, + 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20}; +const int NPTENSIZE = 21; // # of negative pwrs of 10 in NPten array +const double NPten[NPTENSIZE] = { + 1., .1, .01, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, 1e-9, 1e-10, + 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16, 1e-17, 1e-18, 1e-19, 1e-20}; + +const float FHuge = + float(1.E38); // Handy large (not strictly largest) value used for x/0, + // search initializations, etc. Single defn for portability + // and code clarity. // Physical constants #if 1 -const float BtuperWh = 3.41214f; // Btu per Wh -const float BtuperkWh = 3412.14f; // Btu per kWh -const float waterRhoCp = 8.29018282f; // water volumetric weight, lb/gal - // = volumetric heat capacity, Btu/gal-F - // 30 C (85 F) value used, appropriate for DHW calcs +const float BtuperWh = 3.41214f; // Btu per Wh +const float BtuperkWh = 3412.14f; // Btu per kWh +const float waterRhoCp = + 8.29018282f; // water volumetric weight, lb/gal + // = volumetric heat capacity, Btu/gal-F + // 30 C (85 F) value used, appropriate for DHW calcs #else -const float BtuperWh = 3.413f; // Btu per Wh -const float BtuperkWh = 3413.f; // Btu per kWh +const float BtuperWh = 3.413f; // Btu per Wh +const float BtuperkWh = 3413.f; // Btu per kWh const float waterRhoCp = 8.345f; #endif -const float galPerFt3 = 7.48052f; // gal per cubic foot +const float galPerFt3 = 7.48052f; // gal per cubic foot -#if 0 // obsolete +#if 0 // obsolete x const float SrcMultElect = 3.f; // Source multiplier for electrical energy (California CEC value) x const float BtuSrcperWh = 10.239f; /* Btu (source energy) per Wh. Includes California electric x to source conversion factor of 3 (3 * 3.413 = 10.239); @@ -663,20 +806,20 @@ x used to convert electrical consumption to source energy */ #endif // re solar gain distribution (must be known for rccn.h) -enum { socOPEN, socCLSD, socCOUNT }; // internal shade modes (open, closed) -enum { // irrad components - sgcBMXBM, // beam per unit exterior beam - sgcDFXDF, // diffuse per exterior diffuse - sgcDFXBM, // diffuse per exterior beam - sgcCOUNT +enum { socOPEN, socCLSD, socCOUNT }; // internal shade modes (open, closed) +enum { // irrad components + sgcBMXBM, // beam per unit exterior beam + sgcDFXDF, // diffuse per exterior diffuse + sgcDFXBM, // diffuse per exterior beam + sgcCOUNT }; // surface classes e.g. for argument to topSf1(), topSf2() // NOTE: code depends on value order, change with care -enum SFCLASS { sfcNUL, sfcSURF, sfcDOOR, sfcWINDOW, sfcDUCT, sfcPIPE}; +enum SFCLASS { sfcNUL, sfcSURF, sfcDOOR, sfcWINDOW, sfcDUCT, sfcPIPE }; // zone air flow indices -#if 0 // unused, 2-20-2013 +#if 0 // unused, 2-20-2013 x // note: zafCOUNT must equal #define ZAFCOUNT in cndefns.h (re use in cnrecs.def) x enum ZAFTY { zafANINF, zafANVENT, zafINFIL, zafDUCTLKI, zafSYSAIRI, x zafDUCTLKO, zafSYSAIRO, zafDUCTLKILS, zafCOUNT }; @@ -687,33 +830,32 @@ x const int zanSYSAIR = 0x00000008; x const int zanSYSAIRUB = 0x00000010; #endif - -#if 1 || defined( _DEBUG) -#define DEBUGDUMP // define to include DbPrintf() etc code +#if 1 || defined(_DEBUG) +#define DEBUGDUMP // define to include DbPrintf() etc code #endif -bool DbDo( DWORD oMsk, int options=0); +bool DbDo(DWORD oMsk, int options = 0); // debugging option bits // re control of conditional DbPrintf()s // const DWORD dbdALWAYS = 1; // defined in rmkerr.h -const DWORD dbdCULT = 4; // CULT input tables -const DWORD dbdCONSTANTS = 8; // various constants +const DWORD dbdCULT = 4; // CULT input tables +const DWORD dbdCONSTANTS = 8; // various constants const DWORD dbdAIRNET = 16; const DWORD dbdMASS = 32; -const DWORD dbdZM = 64; // main zone model debug print control -const DWORD dbdDUCT = 128; // duct model -const DWORD dbdHPWH = 256; // heat pump water heater (writes to external CSV) -const DWORD dbdRADX = 512; // radiant exchange and SGDIST -const DWORD dbdRCM = 1024; // radiant/convective model details -const DWORD dbdIZ = 2048; // selected interzone info -const DWORD dbdSGDIST = 4096; // solar gain distrib -const DWORD dbdASHWAT = 8192; // ASHWAT call/return -// const DWORD dbdCOMFORT = 16384; // Comfort model values (minimal as of 1-11) -const DWORD dbdSBC = 16384; // surface conditions +const DWORD dbdZM = 64; // main zone model debug print control +const DWORD dbdDUCT = 128; // duct model +const DWORD dbdHPWH = 256; // heat pump water heater (writes to external CSV) +const DWORD dbdRADX = 512; // radiant exchange and SGDIST +const DWORD dbdRCM = 1024; // radiant/convective model details +const DWORD dbdIZ = 2048; // selected interzone info +const DWORD dbdSGDIST = 4096; // solar gain distrib +const DWORD dbdASHWAT = 8192; // ASHWAT call/return +// const DWORD dbdCOMFORT = 16384; // Comfort model values (minimal as of +// 1-11) +const DWORD dbdSBC = 16384; // surface conditions // NOTE: max value = 32768 due to USI limit for expression values // workaround: create additional user-settable values // shift / combine these values in tp_SetDbMask() // 1-2012 - // end of cnglob.h diff --git a/src/cnloads.cpp b/src/cnloads.cpp index 840ba5bdd..f9eee24de 100644 --- a/src/cnloads.cpp +++ b/src/cnloads.cpp @@ -149,7 +149,7 @@ RC ZNR::zn_RddInit() // aMassHr = aMassSh = 0.; object is pre-0'd but may be re-used in // autoSizing // but believe these don't - //need init for start-interval masses + // need init for start-interval masses haMass = 0.; // +='d in ms_rddInit. pre-0'd object may be re-used in autoSizing. znXLGain = znXLGainLs = @@ -526,11 +526,11 @@ RC FC loadsSubhr() // loads stuff for all zones for subhour. Call BEFORE hvac x *sge->sg_targ = 0.f; // zero the target. testing sge->addIt unnecessary. x else x { - x #ifdef SOLAVNEND // undef in cndefns.h (via cnglob.h) 1-18-94: only if - // computing & using end-ivl as well as ivl avg solar - // values - xo float gain = // gain consists of diffuse & - // hour's beam values + x #ifdef SOLAVNEND // undef in cndefns.h (via cnglob.h) 1-18-94: only if + // computing & using end-ivl as well as ivl avg solar + // values + xo float gain = // gain consists of diffuse & + // hour's beam values xo sge->isEndIvl // TRUE to use end-subhr not subhr-avg, 1-95 xo ? Top.radDiffSh * sge->diff[Top.iHrST] // combine weather data @@ -564,11 +564,11 @@ RC FC loadsSubhr() // loads stuff for all zones for subhour. Call BEFORE hvac x else x *sge->sg_targ += gain; // accumulate additional gains into target by adding x x /* *sge->targ is ZrB.p[i].qSgAir or .qSgTotSh, or subhrly - MsR.p[i].outside.sg or .inside.sg, x as targeted in - cgsolar.cpp:sgrGet(). */ - x // note: addIt != 0 probably now 3-90 corresponds to control != - // NULL, but we don't depend on that assumption. - x + MsR.p[i].outside.sg or .inside.sg, x as targeted + in cgsolar.cpp:sgrGet(). */ + x // note: addIt != 0 probably now 3-90 corresponds to control != + // NULL, but we don't depend on that assumption. + x } x } @@ -1596,8 +1596,8 @@ float RSYS::rs_TSupVarFlow(float fAmf, AIRSTATE &asSup) { //---------------------------------------------------------------------------- double ZNR::zn_TAirCR( // zone air temp w/ add'l radiant heat double mCpT, // add'l air heat to zone, Btuh - // either heat added to air ("qAir") - // or mass flow*Tsup (with associated mCp) + // either heat added to air ("qAir") + // or mass flow*Tsup (with associated mCp) double mCp, // add'l air heat rate, Btuh/F double qRad) const // add'l radiant heat to zone, Btuh // returns zone air temp, F @@ -1676,7 +1676,7 @@ ZNR::zn_AmfHvacCR( // sensible hvac air requirements w/ add'l radiant heat // 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); @@ -1690,7 +1690,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); @@ -4919,7 +4919,7 @@ float RSYS::rs_CapHtCurSpeedF() const // heating cap at current speed { // cap = rs_capHtMin // + (rs_capHt - rs_capHtMin) * (rs_speedF - rs_speedFMin) / (1.f - - //rs_speedFMin); + // rs_speedFMin); // and rs_speedFMin = rs_capHtMin / rs_capHt // thus cap reduces to ... return rs_capHt * rs_speedF; @@ -5033,7 +5033,7 @@ float RSYS::rs_CapEffASHP( // performance at current conditions int ashpModel /*=0*/, // alternative model // 0=CSE, 1=MP, 2=ESL, 3=E+ // +0x100: do NOT model defrost heating - // +0x200: ignore rs_fEffH (efficiency adjustment) + // +0x200: ignore rs_fEffH (efficiency adjustment) float fanHRtd /*=-1.f*/, // fan power included in rating, Btuh // default = rs_fanHRtdH float fanHOpr /*=-1.f*/, // operating fan power, Btuh @@ -5505,7 +5505,7 @@ int RSYS::rs_SupplyAirState( // current conditioning capabilities // returns 3: mode is available, rs_asSup set // 2: mode is being actively autosized, rs_asSup set // 1: mode is inactive (another mode is being actively -//autosized) 0: mode not available +// autosized) 0: mode not available { // TODO: not returning right value! @@ -5577,7 +5577,7 @@ int RSYS::rs_SetAmf( // set rs_amf // returns 3: mode is available, rs_amf set per speedF // 2: mode is being actively autosized, rs_amf = full speed // 1: mode is inactive (another mode is being actively -//autosized) 0: no amf available, rs_amf = 0 +// autosized) 0: no amf available, rs_amf = 0 { // set mode-specific full speed air flow // must be set even if mode not changed @@ -5844,7 +5844,7 @@ RC RSYS::rs_AllocateZoneAir() // finalize zone air flows printf("\nHit %s", Top.dateStr.CStr()); #endif double tSup = max(rs_asSup.as_tdb, rs_tSupLs); // use last result as guess - double tSup_old = tSup; + double tSup_prev = tSup; double amfX = DBL_MIN; double amfXTarg = 1. / rs_amf; // f = target function value int ret = secant( @@ -5856,10 +5856,10 @@ RC RSYS::rs_AllocateZoneAir() // finalize zone air flows return amf != 0. ? 1. / amf : 1.e10; }, this, amfXTarg, .0001 * amfXTarg, tSup, amfX, // x1, f1 - rs_asSupAux.as_tdb, 1. / rs_amfReq[1]); // x2, f2 + rs_asSupAux.as_tdb, DBL_MIN); // x2, f2 if (ret != 0) { - warn("RSYS '%s': ASHP aux heat supply temp fail (%d)", Name(), ret); - tSup = tSup_old; + oWarn("ASHP aux heat supply temp fail; restoring previous supply temp"); + tSup = tSup_prev; } #if defined(_DEBUG) // check tSup -- should be between noAux and fullAux temps @@ -6353,11 +6353,11 @@ RC RSYS::rs_FinalizeSh() runFFan * rs_speedF * rs_fanHeatH); // fan output, Btuh // insurance: limit to total output - rs_outSen = outTot - - rs_outFan * fFanHeatPrim; // compressor sensible output - // (note defrost adjustment below) - // rs_outLat = 0.; - // // total latent output + rs_outSen = + outTot - rs_outFan * fFanHeatPrim; // compressor sensible output + // (note defrost adjustment + // below) rs_outLat = 0.; + // // total latent output #if defined(_DEBUG) // some checking if aux is running diff --git a/src/nummeth.cpp b/src/nummeth.cpp index 9822d60a5..1f2e46417 100644 --- a/src/nummeth.cpp +++ b/src/nummeth.cpp @@ -7,7 +7,7 @@ /*------------------------------- INCLUDES --------------------------------*/ #include "cnglob.h" -#include "nummeth.h" // decls for this file +#include "nummeth.h" // decls for this file #if 0 // Eigen experiments 3-29-2023 @@ -42,145 +42,136 @@ int FC solveEigen( // Solve a system of equations using Gauss-Jordan eliminatio return 0; } - #endif //============================================================================== -int FC gaussjb( // Solve a system of equations using Gauss-Jordan elimination - - double* a, // square matrix of coefficients. NOTE: this is a - // a C 2D array, NOT a Numerical Recipes "matrix" - int n, // dimension of a -- e.g. a is a[n,n] - // n *MUST BE* <= MAXN (see code) - double* b, // Set of m right hand side vectors for which - // solutions are sought. m can be 0. - int m, // b is b[n][m] - int invflg /*=0*/) // invert flag - if nz, a-inverse is returned in a - // if 0, a is returned garbage (not generating inverse faster) +int FC gaussjb( // Solve a system of equations using Gauss-Jordan elimination + + double *a, // square matrix of coefficients. NOTE: this is a + // a C 2D array, NOT a Numerical Recipes "matrix" + int n, // dimension of a -- e.g. a is a[n,n] + // n *MUST BE* <= MAXN (see code) + double *b, // Set of m right hand side vectors for which + // solutions are sought. m can be 0. + int m, // b is b[n][m] + int invflg /*=0*/) // invert flag - if nz, a-inverse is returned in a + // if 0, a is returned garbage (not generating inverse + // faster) // returns: // -1: failure: n too large // 0: success (a-inverse in place of a (if invflg nz), b-solutions in b ) // 1: matrix is singular { - static const int MAXN = 1000; // max n supported - int ipiv[ MAXN]; - int indxr[ MAXN]; - int indxc[ MAXN]; - - int i, j, ll, irow = 0, icol = 0, l, k; - double f, big, pivinv; - double *arow, *arcol, *arow2; - double *brow, *brcol, *brow2; - - if (n > MAXN) - return -1; + static const int MAXN = 1000; // max n supported + int ipiv[MAXN]; + int indxr[MAXN]; + int indxc[MAXN]; + + int i, j, ll, irow = 0, icol = 0, l, k; + double f, big, pivinv; + double *arow, *arcol, *arow2; + double *brow, *brcol, *brow2; + + if (n > MAXN) + return -1; + + memset(ipiv, 0, n * sizeof(int)); + + for (i = 0; i < n; i++) /* Main loop */ + { + big = 0.; + arow = a; + for (j = 0; j < n; j++) { + if (*(ipiv + j) != 1) { + arcol = arow; + for (k = 0; k < n; k++) { + if (*(ipiv + k) == 0) { + if ((f = fabs(*arcol)) >= big) { + big = f; + irow = j; + icol = k; + } + } else if (*(ipiv + k) > 1) + return 1; /* singular */ + arcol++; + } + } + arow += n; + } - memset( ipiv, 0, n * sizeof( int) ); + /* Now that the pivot point is found we rotate rows */ + *(ipiv + icol) += 1; + arow = a + irow * n; + brow = b + irow * m; + arow2 = a + icol * n; + brow2 = b + icol * m; + if (irow != icol) { + VSwap(arow, n, arow2, n); + VSwap(brow, n, brow2, m); + } + *(indxr + i) = irow; + *(indxc + i) = icol; + + double *arcol2 = arow2 + icol; + if (*arcol2 == 0.) + return 1; /* singular */ + pivinv = 1. / (*arcol2); + *arcol2 = 1.; + arcol2 = arow2; + double *brcol2 = brow2; + /*lint -e564 suppress "variable arcol2 depends on order of eval" */ + for (l = 0; l < n; l++) + *(arcol2++) *= pivinv; + for (l = 0; l < m; l++) + *(brcol2++) *= pivinv; + /*lint +e564 */ + + arow = a; + brow = b; + for (ll = 0; ll < n; ll++) { + if (ll != icol) { + double dum = *(arow + icol); + *(arow + icol) = 0.; + arcol = arow; + arcol2 = arow2; + brcol = brow; + brcol2 = brow2; + /*lint -e564 suppress "variable arcol depends on order of eval"*/ + for (l = 0; l < n; l++) + *arcol++ -= *(arcol2++) * dum; + for (l = 0; l < m; l++) + *brcol++ -= *(brcol2++) * dum; + /*lint +e654 */ + } + arow += n; + brow += m; + } + } /* End of main loop */ - for (i = 0; i < n; i++) /* Main loop */ - { - big = 0.; - arow = a; - for (j = 0; j < n; j++) - { - if (*(ipiv+j) != 1) - { - arcol = arow; - for (k = 0; k < n; k++) - { - if (*(ipiv+k) == 0) - { - if ((f=fabs(*arcol)) >= big) - { - big = f; - irow = j; - icol = k; - } - } - else if (*(ipiv+k) > 1) - return 1; /* singular */ - arcol++; - } - } - arow += n; - } - - /* Now that the pivot point is found we rotate rows */ - *(ipiv+icol) += 1; - arow = a + irow*n; - brow = b + irow*m; - arow2 = a + icol*n; - brow2 = b + icol*m; - if (irow != icol) - { - VSwap( arow, n, arow2, n); - VSwap( brow, n, brow2, m); - } - *(indxr+i) = irow; - *(indxc+i) = icol; - - double* arcol2 = arow2 + icol; - if (*arcol2 == 0.) - return 1; /* singular */ - pivinv = 1./(*arcol2); - *arcol2 = 1.; - arcol2 = arow2; - double* brcol2 = brow2; - /*lint -e564 suppress "variable arcol2 depends on order of eval" */ - for (l = 0; l < n; l++) - *(arcol2++) *= pivinv; - for (l = 0; l < m; l++) - *(brcol2++) *= pivinv; - /*lint +e564 */ - - arow = a; - brow = b; - for (ll = 0; ll < n; ll++) - { - if (ll != icol) - { - double dum = *(arow+icol); - *(arow+icol) = 0.; - arcol = arow; - arcol2 = arow2; - brcol = brow; - brcol2 = brow2; - /*lint -e564 suppress "variable arcol depends on order of eval"*/ - for (l = 0; l < n; l++) - *arcol++ -= *(arcol2++)*dum; - for (l = 0; l < m; l++) - *brcol++ -= *(brcol2++)*dum; - /*lint +e654 */ - } - arow += n; - brow += m; - } - } /* End of main loop */ - - if ( invflg ) - for (l = n-1; l >= 0; l--) - if ( (irow = *(indxr+l)) != (icol = *(indxc+l)) ) - VSwap( a+irow, n, a+icol, n); + if (invflg) + for (l = n - 1; l >= 0; l--) + if ((irow = *(indxr + l)) != (icol = *(indxc + l))) + VSwap(a + irow, n, a + icol, n); - return 0; + return 0; } //----------------------------------------------------------------------------- -int secant( // 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. - 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 +int secant( // 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. + 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 // convergence = f - eps(Lo) <= f1 <= f + eps @@ -192,275 +183,258 @@ int secant( // find x given f(x) (secant method) // >0= failed to converge, returned value = # of iterations // <0= 0 slope encountered, returned value = -# of iterations { - 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 - { double swap; - swap = x1; x1=x2; x2=swap; - swap = f1; f1=f2; f2=swap; + 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 + { + double swap; + swap = x1; + x1 = x2; + x2 = swap; + swap = f1; + f1 = f2; + f2 = swap; + } + + int i; + for (i = 0; ++i < 20;) // iterate to refine solution + { + if (f1 <= fHi && f1 >= fLo) { + i = 0; // success + break; // done; last *pFunc call ... + // 1st iteration: *pFunc( x2) + swap + // >1st iteration: *pFunc( x1) below } - int i; - for (i=0; ++i < 20; ) // iterate to refine solution - { 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 - 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 (fabs(f1 - f2) < 1.e-20) // if slope is 0 + { + i = -i; // tell caller + break; } - return i; -} // ::secant + + 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 + } + 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 - // may CHANGE x re domain limits etc. - void* pO, // pointer passed to *pFunc, typ object pointer - double f, // f( x) value sought - double eps, // convergence tolerance, hi- or both sides - double& x1, // x 1st guess, - // returned with result - double xMin, // minimum value of x - double xMax) // maximum value of x +int regula( // find x given f(x) (regula-falsi method) + double (*pFunc)(void *pO, double &x), + // function under investigation; note that it + // may CHANGE x re domain limits etc. + void *pO, // pointer passed to *pFunc, typ object pointer + double f, // f( x) value sought + double eps, // convergence tolerance, hi- or both sides + double &x1, // x 1st guess, + // returned with result + double xMin, // minimum value of x + double xMax) // maximum value of x // returns result code, x1 and f1 always best known solution // 0: success // >0= failed to converge, returned value = # of iterations // <0= bad min / max { - double fHi = f + eps; - double fLo = f - eps; - - double f1 = (*pFunc)(pO, x1); - - if (f1 <= fHi && f1 >= fLo) // if 1st guess good - return 0; // success: don't do *pFunc( x2) - // (side effects) - - double fMin = (*pFunc)(pO, xMin); - - if (fMin <= fHi && fMin >= fLo) // if 1st guess good - { - x1 = xMin; - return 0; // success: don't do *pFunc( x2) - // (side effects) - } - - double fMax = (*pFunc)(pO, xMax); - - if (fMax <= fHi && fMax >= fLo) // if 1st guess good - { - x1 = xMax; - return 0; // success: don't do *pFunc( x2) - // (side effects) - } - - // Error if fMin and fMax are the same sign - if ((fMin - f) * (fMax - f) > 0.) - { - return -1; - } - - if ((f1 - f) * (fMin - f) < 0.) - { - xMax = x1; - fMax = f1; - } - else - { - xMin = x1; - fMin = f1; - } - - int i; - for (i = 0; ++i < 20; ) // iterate to refine solution - { - x1 = xMin - (xMax - xMin) * (fMin - f) / (fMax - fMin); - f1 = (*pFunc)(pO, x1); - - if (f1 <= fHi && f1 >= fLo) - { - i = 0; // success - break; // done; last *pFunc call ... - // 1st iteration: *pFunc( x2) + swap - // >1st iteration: *pFunc( x1) below - } - - if ((f1 - f) * (fMin - f) < 0.) - { - xMax = x1; - fMax = f1; - } - else - { - xMin = x1; - fMin = f1; - } + double fHi = f + eps; + double fLo = f - eps; + + double f1 = (*pFunc)(pO, x1); + + if (f1 <= fHi && f1 >= fLo) // if 1st guess good + return 0; // success: don't do *pFunc( x2) + // (side effects) + + double fMin = (*pFunc)(pO, xMin); + + if (fMin <= fHi && fMin >= fLo) // if 1st guess good + { + x1 = xMin; + return 0; // success: don't do *pFunc( x2) + // (side effects) + } + + double fMax = (*pFunc)(pO, xMax); + + if (fMax <= fHi && fMax >= fLo) // if 1st guess good + { + x1 = xMax; + return 0; // success: don't do *pFunc( x2) + // (side effects) + } + + // Error if fMin and fMax are the same sign + if ((fMin - f) * (fMax - f) > 0.) { + return -1; + } + + if ((f1 - f) * (fMin - f) < 0.) { + xMax = x1; + fMax = f1; + } else { + xMin = x1; + fMin = f1; + } + + int i; + for (i = 0; ++i < 20;) // iterate to refine solution + { + x1 = xMin - (xMax - xMin) * (fMin - f) / (fMax - fMin); + f1 = (*pFunc)(pO, x1); + + if (f1 <= fHi && f1 >= fLo) { + i = 0; // success + break; // done; last *pFunc call ... + // 1st iteration: *pFunc( x2) + swap + // >1st iteration: *pFunc( x1) below + } - } - return i; -} // ::regula + if ((f1 - f) * (fMin - f) < 0.) { + xMax = x1; + fMax = f1; + } else { + xMin = x1; + fMin = f1; + } + } + return i; +} // ::regula //============================================================================= /////////////////////////////////////////////////////////////////////////////// // class DGRAPH -- directed graph //============================================================================= -bool DGRAPH::dg_TopologicalSort( // topological sort - std::vector< int>& vSorted) // sorted result ("bottom up" order) - // vSorted[ 0] = deepest vertex - // returns true on success (vSorted filled) - // else false iff cyclic (vSorted[ 0] set to 1st offending vertex) +bool DGRAPH::dg_TopologicalSort( // topological sort + std::vector &vSorted) // sorted result ("bottom up" order) + // vSorted[ 0] = deepest vertex + // returns true on success (vSorted filled) +// else false iff cyclic (vSorted[ 0] set to 1st offending vertex) { - vSorted.clear(); - dg_status.assign(dg_nV, 0); - - // Depth-first search starting from each vertex - for (int iV = 0; iV < dg_nV; iV++) - { if (!dg_TopologicalSortDFS(iV, vSorted)) - return false; // cyclic detected, abandon - } - return true; -} // DGRAPH::dg_TopologicalSort + vSorted.clear(); + dg_status.assign(dg_nV, 0); + + // Depth-first search starting from each vertex + for (int iV = 0; iV < dg_nV; iV++) { + if (!dg_TopologicalSortDFS(iV, vSorted)) + return false; // cyclic detected, abandon + } + return true; +} // DGRAPH::dg_TopologicalSort //----------------------------------------------------------------------------- bool DGRAPH::dg_TopologicalSortDFS( - int iV, // starting vertex - std::vector< int>& vSorted) // returned: updated sorted list - // recursive helper for dg_TopologicalSort() - // returns true iff success - // false if cyclic + int iV, // starting vertex + std::vector &vSorted) // returned: updated sorted list + // recursive helper for dg_TopologicalSort() + // returns true iff success + // false if cyclic { - if (dg_status[iV] == 2) - return true; // already seen - if (dg_status[iV] == 1) - { // cyclic: put offending vertex into vSorted[ 0] - vSorted.clear(); - vSorted.push_back(iV); - return false; - } - - dg_status[iV] = 1; // active vertex - - // Recurs for all the vertices adjacent to this vertex - // (depth first) - for (auto tV : dg_edges[iV]) - { - if (!dg_TopologicalSortDFS(tV, vSorted)) - return false; // cyclic somewhere below here - } - - vSorted.push_back(iV); - dg_status[iV] = 2; // seen - return true; - -} // DGRAPH::dg_topologicalSortDFS + if (dg_status[iV] == 2) + return true; // already seen + if (dg_status[iV] == 1) { // cyclic: put offending vertex into vSorted[ 0] + vSorted.clear(); + vSorted.push_back(iV); + return false; + } + + dg_status[iV] = 1; // active vertex + + // Recurs for all the vertices adjacent to this vertex + // (depth first) + for (auto tV : dg_edges[iV]) { + if (!dg_TopologicalSortDFS(tV, vSorted)) + return false; // cyclic somewhere below here + } + + vSorted.push_back(iV); + dg_status[iV] = 2; // seen + return true; + +} // DGRAPH::dg_topologicalSortDFS //------------------------------------------------------------------------------ -bool DGRAPH::dg_CountRefs( // # of refs to each vertex in tree - int ivRoot, // root vertex - std::vector< int>& vRefCounts) - // WHY: allows identifying duplicate references in a specific subtree - // returns true iff success (vRefCounts[ iV] = refs/visits to each vertex) - // else false (cyclic) +bool DGRAPH::dg_CountRefs( // # of refs to each vertex in tree + int ivRoot, // root vertex + std::vector &vRefCounts) +// WHY: allows identifying duplicate references in a specific subtree +// returns true iff success (vRefCounts[ iV] = refs/visits to each vertex) +// else false (cyclic) { - vRefCounts.assign(dg_nV, 0); - dg_status.assign(dg_nV, 0); + vRefCounts.assign(dg_nV, 0); + dg_status.assign(dg_nV, 0); - return dg_CountRefsDFS(ivRoot, vRefCounts); -} // DGRAPH::dg_CountRefs + return dg_CountRefsDFS(ivRoot, vRefCounts); +} // DGRAPH::dg_CountRefs //----------------------------------------------------------------------------- -bool DGRAPH::dg_CountRefsDFS( - int iV, // current vertex - std::vector< int>& vRefCounts) -{ - if (dg_status[iV] == 1) - { // cyclic: put offending vertex into vRefCounts[ 0] - vRefCounts.clear(); - vRefCounts.push_back(iV); - return false; - } - - ++vRefCounts[iV]; // count current - - for (auto tV : dg_edges[iV]) - { - if (!dg_CountRefsDFS(tV, vRefCounts)) - return false; // cyclic somewhere below here - } - return true; -} // DGRAPH::dg_CountRefsDFS +bool DGRAPH::dg_CountRefsDFS(int iV, // current vertex + std::vector &vRefCounts) { + if (dg_status[iV] == 1) { // cyclic: put offending vertex into vRefCounts[ 0] + vRefCounts.clear(); + vRefCounts.push_back(iV); + return false; + } + + ++vRefCounts[iV]; // count current + + for (auto tV : dg_edges[iV]) { + if (!dg_CountRefsDFS(tV, vRefCounts)) + return false; // cyclic somewhere below here + } + return true; +} // DGRAPH::dg_CountRefsDFS //============================================================================= -template< typename T, size_t NI> class PWLFUNC -{ +template class PWLFUNC { public: - PWLFUNC(T (*pFunc)(T x), T xMin, T xMax); - T operator()( T x) - { - int i = int(x / NI); - if (i < 0 || i >= NI) - return (*pwl_pFunc)(x); - else - return pwl_a[i] + pwl_b[i] * x; - } + PWLFUNC(T (*pFunc)(T x), T xMin, T xMax); + T operator()(T x) { + int i = int(x / NI); + if (i < 0 || i >= NI) + return (*pwl_pFunc)(x); + else + return pwl_a[i] + pwl_b[i] * x; + } private: - T (*pwl_pFunc)(T x); - T pwl_xMin; - T pwl_xMax; - T pwl_a[NI]; - T pwl_b[NI]; - - void pwl_Setup(); + T (*pwl_pFunc)(T x); + T pwl_xMin; + T pwl_xMax; + T pwl_a[NI]; + T pwl_b[NI]; + void pwl_Setup(); }; //------------------------------------------------------------------------------- -template< typename T, size_t NI> PWLFUNC< T, NI>::PWLFUNC( - T(*pFunc)(T x), T xMin, T xMax) - : pwl_pFunc( pFunc), pwl_xMin( xMin), pwl_xMax( xMax) -{ - pwl_Setup(); -} // PWLFUNC::PWLFUNC +template +PWLFUNC::PWLFUNC(T (*pFunc)(T x), T xMin, T xMax) + : pwl_pFunc(pFunc), pwl_xMin(xMin), pwl_xMax(xMax) { + pwl_Setup(); +} // PWLFUNC::PWLFUNC //------------------------------------------------------------------------------- -template< typename T, size_t NI> void PWLFUNC< T, NI>::pwl_Setup() -{ - T xStep = (pwl_xMax - pwl_xMin) / (NI-1); - for (int i = 0; i < NI; i++) - { - T x = pwl_xMin + i*xStep; - pwl_a[i] = (*pwl_pFunc)(x); - } - - +template void PWLFUNC::pwl_Setup() { + T xStep = (pwl_xMax - pwl_xMin) / (NI - 1); + for (int i = 0; i < NI; i++) { + T x = pwl_xMin + i * xStep; + pwl_a[i] = (*pwl_pFunc)(x); + } } // =============================================================================== -static float pow33(float x) { return powf(x, 1.f/3.f); } -static PWLFUNC< float, 11> powk(pow33,0.f, 10.f); +static float pow33(float x) { return powf(x, 1.f / 3.f); } +static PWLFUNC powk(pow33, 0.f, 10.f); #if 0 void test() From b1bdcf96f35c80d8127d3f54393fd93db7c48075 Mon Sep 17 00:00:00 2001 From: Phil Ahrenkiel Date: Thu, 4 Apr 2024 15:49:27 -0600 Subject: [PATCH 05/21] Define tempF tolerance. --- src/cnloads.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cnloads.cpp b/src/cnloads.cpp index f9eee24de..d2bf86cc8 100644 --- a/src/cnloads.cpp +++ b/src/cnloads.cpp @@ -567,7 +567,7 @@ RC FC loadsSubhr() // loads stuff for all zones for subhour. Call BEFORE hvac MsR.p[i].outside.sg or .inside.sg, x as targeted in cgsolar.cpp:sgrGet(). */ x // note: addIt != 0 probably now 3-90 corresponds to control != - // NULL, but we don't depend on that assumption. + // NULL, but we don't depend on that assumption. x } x @@ -5858,7 +5858,7 @@ RC RSYS::rs_AllocateZoneAir() // finalize zone air flows this, amfXTarg, .0001 * amfXTarg, tSup, amfX, // x1, f1 rs_asSupAux.as_tdb, DBL_MIN); // x2, f2 if (ret != 0) { - oWarn("ASHP aux heat supply temp fail; restoring previous supply temp"); + oWarn("ASHP aux heat supply temp fail; restoring previous value"); tSup = tSup_prev; } #if defined(_DEBUG) From 0d0471c5d1101e4eba6a4f0751256b2ed884bb8d Mon Sep 17 00:00:00 2001 From: Phil Ahrenkiel Date: Thu, 4 Apr 2024 16:14:29 -0600 Subject: [PATCH 06/21] Add secant failure reporting. --- src/cnloads.cpp | 10 +- src/envpak.cpp | 648 ++++++++++++++++++++++++------------------------ src/nummeth.cpp | 93 ++++++- 3 files changed, 411 insertions(+), 340 deletions(-) diff --git a/src/cnloads.cpp b/src/cnloads.cpp index d2bf86cc8..2990a0336 100644 --- a/src/cnloads.cpp +++ b/src/cnloads.cpp @@ -567,7 +567,7 @@ RC FC loadsSubhr() // loads stuff for all zones for subhour. Call BEFORE hvac MsR.p[i].outside.sg or .inside.sg, x as targeted in cgsolar.cpp:sgrGet(). */ x // note: addIt != 0 probably now 3-90 corresponds to control != - // NULL, but we don't depend on that assumption. + // NULL, but we don't depend on that assumption. x } x @@ -5730,6 +5730,12 @@ int rsysIterCount = 0; // count secant method call-backs #endif #endif //----------------------------------------------------------------------------- + +double formerLambda(void *pO, double &tSup) { + double amf = ((RSYS *)pO)->rs_AmfRequired(tSup); + return amf != 0. ? 1. / amf : 1.e10; +} + RC RSYS::rs_AllocateZoneAir() // finalize zone air flows // { @@ -5844,7 +5850,7 @@ RC RSYS::rs_AllocateZoneAir() // finalize zone air flows printf("\nHit %s", Top.dateStr.CStr()); #endif double tSup = max(rs_asSup.as_tdb, rs_tSupLs); // use last result as guess - double tSup_prev = tSup; + double tSup_prev = tSup; // retain previous value in case of failure double amfX = DBL_MIN; double amfXTarg = 1. / rs_amf; // f = target function value int ret = secant( diff --git a/src/envpak.cpp b/src/envpak.cpp index 7b57a1adb..846e9a696 100644 --- a/src/envpak.cpp +++ b/src/envpak.cpp @@ -11,309 +11,296 @@ #include #endif -#include // signal SIGINT -#include // FPE_UNDERFLOW FPE_INVALID etc -#include // timeb structure +#include // FPE_UNDERFLOW FPE_INVALID etc +#include // signal SIGINT +#include // timeb structure -#include "lookup.h" // lookws wstable -#include "tdpak.h" // tdldti -#include "xiopak.h" // xchdir +#include "lookup.h" // lookws wstable +#include "tdpak.h" // tdldti +#include "xiopak.h" // xchdir -#include "envpak.h" // declares functions in this file +#include "envpak.h" // declares functions in this file -#if CSE_OS==CSE_OS_MACOS +#if CSE_OS == CSE_OS_MACOS #include // _NSGetExecutablePath #endif /*----------------------- LOCAL FUNCTION DECLARATIONS ---------------------*/ -LOCAL void CDEC enkiint(INT); // no NEAR -- passed to another function +LOCAL void CDEC enkiint(INT); // no NEAR -- passed to another function /*============================ TEST CODE =============================*/ /* #define TEST */ #ifdef TEST -t unmaintained 9-12-89 -t main () -t { -t SI i; -t float x; -t LDATETIME ldt; -t IDATETIME idt; -t -t #ifdef KITEST -t enkiinit(); -t enkimode(KICLEAR+KIBEEP); -t for (i = 0; i < 100; i++) -t { if (enkichk()) -t { printf("\n\nInterrupt...."); -t gcne(); -t } -t printf("Hi %d\n",i); -t } -t enkimode(KILEAVEHIT); -t if (enkichk()) -t printf("Still set\n"); -t else -t printf("Clear\n"); -t -t hello(NULL); -t enkimode(KICLEAR+KIBEEP); -t printf("\nlooping..."); -t for (i = 0; i < 10000; i++) -t { x = 2.; -t x = x*x*x*x*x; -t if (enkichk()) -t { printf("\n\nInterrupt...."); -t gcne(); -t enkimode(KICLEAR+KIBEEP); -t } -t } -t printf("Done."); -t enkimode(KILEAVEHIT); -t if (enkichk()) -t printf("Still set\n"); -t else -t printf("Clear\n"); -t #endif /* KITEST */ -t #ifdef DTTEST -t ldt = ensysldt(); -t ensystd(&idt); -t printf("Time: %ld\n",ldt); -t #endif /* DTTEST */ -t } +t unmaintained 9 - 12 - 89 t main() t { + t SI i; + t float x; + t LDATETIME ldt; + t IDATETIME idt; + t t #ifdef KITEST t enkiinit(); + t enkimode(KICLEAR + KIBEEP); + t for (i = 0; i < 100; i++) t { + if (enkichk()) + t { + printf("\n\nInterrupt...."); + t gcne(); + t + } + t printf("Hi %d\n", i); + t + } + t enkimode(KILEAVEHIT); + t if (enkichk()) t printf("Still set\n"); + t else t printf("Clear\n"); + t t hello(NULL); + t enkimode(KICLEAR + KIBEEP); + t printf("\nlooping..."); + t for (i = 0; i < 10000; i++) t { + x = 2.; + t x = x * x * x * x * x; + t if (enkichk()) t { + printf("\n\nInterrupt...."); + t gcne(); + t enkimode(KICLEAR + KIBEEP); + t + } + t + } + t printf("Done."); + t enkimode(KILEAVEHIT); + t if (enkichk()) t printf("Still set\n"); + t else t printf("Clear\n"); + t #endif /* KITEST */ + t #ifdef DTTEST t ldt = ensysldt(); + t ensystd(&idt); + t printf("Time: %ld\n", ldt); + t #endif /* DTTEST */ + t +} #endif //============================================================================= -WStr enExePath() // full path to current executable +WStr enExePath() // full path to current executable { - WStr t; - char exePath[FILENAME_MAX + 1]; + WStr t; + char exePath[FILENAME_MAX + 1]; #if CSE_OS == CSE_OS_MACOS - uint32_t pathSize = sizeof(exePath); - _NSGetExecutablePath(exePath, &pathSize); - t = exePath; - WStrLower(t); + uint32_t pathSize = sizeof(exePath); + _NSGetExecutablePath(exePath, &pathSize); + t = exePath; + WStrLower(t); #elif CSE_OS == CSE_OS_LINUX - ssize_t len = readlink("/proc/self/exe", exePath, sizeof(exePath) - 1); - if (len == -1) { - errCrit(PABT, "Unable to locate executable."); - } - else { - exePath[len] = '\0'; - t = exePath; - WStrLower(t); - } + ssize_t len = readlink("/proc/self/exe", exePath, sizeof(exePath) - 1); + if (len == -1) { + errCrit(PABT, "Unable to locate executable."); + } else { + exePath[len] = '\0'; + t = exePath; + WStrLower(t); + } #elif CSE_OS == CSE_OS_WINDOWS - if (GetModuleFileName(NULL, exePath, sizeof(exePath)) > 0) - { - t = exePath; - WStrLower(t); - } + if (GetModuleFileName(NULL, exePath, sizeof(exePath)) > 0) { + t = exePath; + WStrLower(t); + } #else #error Missing CSE_OS case #endif - WStrLower(t); - return t; -} // enExePath + WStrLower(t); + return t; +} // enExePath //============================================================================= -WStr enExeInfo( // retrieve build date/time, linker version, etc from exe - WStr exePath, // path to .exe - int& codeSize) // code size (bytes) +WStr enExeInfo( // retrieve build date/time, linker version, etc from exe + WStr exePath, // path to .exe + int &codeSize) // code size (bytes) // returns info as string or "?" if error { - const char* msg = "?"; - time_t timeDateStamp = 0; - codeSize = 0; - WStr linkerVersion; + const char *msg = "?"; + time_t timeDateStamp = 0; + codeSize = 0; + WStr linkerVersion; #if CSE_OS == CSE_OS_WINDOWS - HANDLE hFile = CreateFile( exePath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); - - if (hFile == INVALID_HANDLE_VALUE) - msg = "Cannot open file"; - else - { - HANDLE hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); - if (hFileMapping == 0) - msg = "Cannot create file mapping"; - else - { PIMAGE_DOS_HEADER pDosHeader = - (PIMAGE_DOS_HEADER)MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0); - if (pDosHeader == 0) - msg = "MapViewOfFile() failed"; - else - { if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) - msg = "Invalid EXE file"; - else - { try - { // use try/catch re possible bad e_lfanew, EOF, etc. - PIMAGE_NT_HEADERS pNTHeader = - PIMAGE_NT_HEADERS(ULI(pDosHeader) + pDosHeader->e_lfanew); - if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) - msg = "Not a Portable Executable (PE) EXE"; - else - { - timeDateStamp = pNTHeader->FileHeader.TimeDateStamp; - codeSize = pNTHeader->OptionalHeader.SizeOfCode; - linkerVersion = WStrPrintf( "%d.%d", - pNTHeader->OptionalHeader.MajorLinkerVersion, - pNTHeader->OptionalHeader.MinorLinkerVersion); - } - } - catch (...) - { msg = "Invalid EXE file"; - } - } - UnmapViewOfFile( pDosHeader); - } - CloseHandle( hFileMapping); - } - CloseHandle( hFile); - } + HANDLE hFile = CreateFile(exePath.c_str(), GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + + if (hFile == INVALID_HANDLE_VALUE) + msg = "Cannot open file"; + else { + HANDLE hFileMapping = + CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (hFileMapping == 0) + msg = "Cannot create file mapping"; + else { + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)MapViewOfFile( + hFileMapping, FILE_MAP_READ, 0, 0, 0); + if (pDosHeader == 0) + msg = "MapViewOfFile() failed"; + else { + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) + msg = "Invalid EXE file"; + else { + try { // use try/catch re possible bad e_lfanew, EOF, etc. + PIMAGE_NT_HEADERS pNTHeader = + PIMAGE_NT_HEADERS(ULI(pDosHeader) + pDosHeader->e_lfanew); + if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) + msg = "Not a Portable Executable (PE) EXE"; + else { + timeDateStamp = pNTHeader->FileHeader.TimeDateStamp; + codeSize = pNTHeader->OptionalHeader.SizeOfCode; + linkerVersion = WStrPrintf( + "%d.%d", pNTHeader->OptionalHeader.MajorLinkerVersion, + pNTHeader->OptionalHeader.MinorLinkerVersion); + } + } catch (...) { + msg = "Invalid EXE file"; + } + } + UnmapViewOfFile(pDosHeader); + } + CloseHandle(hFileMapping); + } + CloseHandle(hFile); + } #endif - WStr exeInfo; - if (timeDateStamp == 0) - exeInfo = "? (enExeInfo fail)"; - else - exeInfo = WStrPrintf( "%s (VS %s %d bytes)", - tdldts( timeDateStamp, NULL), - linkerVersion.c_str(), - codeSize); - return exeInfo; -} // enExeInfo + WStr exeInfo; + if (timeDateStamp == 0) + exeInfo = "? (enExeInfo fail)"; + else + exeInfo = + WStrPrintf("%s (VS %s %d bytes)", tdldts(timeDateStamp, NULL), + linkerVersion.c_str(), codeSize); + return exeInfo; +} // enExeInfo //===================================================================== -void FC ensystd( // Return the system date and time as IDATETIME +void FC ensystd( // Return the system date and time as IDATETIME - IDATETIME *dt ) // Pointer to structure to receive date and time - // (dtypes.h struct, described in tdpak.cpp) + IDATETIME *dt) // Pointer to structure to receive date and time + // (dtypes.h struct, described in tdpak.cpp) { -// Get time in LDATETIME format from system, convert to IDATETIME - tdldti( // convert format: gets year/month/mday/wday. tdpak.cpp - ensysldt(), // get time from system as seconds from 1/1/1970 - dt ); // caller's destination -} // ensystd + // Get time in LDATETIME format from system, convert to IDATETIME + tdldti( // convert format: gets year/month/mday/wday. tdpak.cpp + ensysldt(), // get time from system as seconds from 1/1/1970 + dt); // caller's destination +} // ensystd //===================================================================== -LDATETIME FC ensysldt() // Return system date and time as LDATETIME +LDATETIME FC ensysldt() // Return system date and time as LDATETIME { - return (LDATETIME)time(NULL); // seconds since 1/1/1970 (msc library) -} // ensysldt + return (LDATETIME)time(NULL); // seconds since 1/1/1970 (msc library) +} // ensysldt /* *************** Keyboard Interrupt Handling Routines ************ */ -#define KIWAITING 0 // Waiting for a keyboard interrupt -#define KIHIT 1 // KI received but not yet noticed by program -#define KISEEN 2 // KI has been noticed by prog and is being processed. - // Don't bother user further. - // reverts to KIWAITING when enkimode called. -static SI kiflag = KIWAITING; // Interrupt flag used by routines -static SI kimode = 0; // KIBEEP bit on to beep at ^C. +#define KIWAITING 0 // Waiting for a keyboard interrupt +#define KIHIT 1 // KI received but not yet noticed by program +#define KISEEN \ + 2 // KI has been noticed by prog and is being processed. + // Don't bother user further. + // reverts to KIWAITING when enkimode called. +static SI kiflag = KIWAITING; // Interrupt flag used by routines +static SI kimode = 0; // KIBEEP bit on to beep at ^C. //===================================================================== -void FC enkiinit( // Initialize for keyboard interrupt handling +void FC enkiinit( // Initialize for keyboard interrupt handling - SI mode ) // Keyboard interrupt mode, passed to enkimode(). - // Should contain a KICLEAR to properly initialize. + SI mode) // Keyboard interrupt mode, passed to enkimode(). + // Should contain a KICLEAR to properly initialize. { - signal( SIGINT, enkiint); // tell C lib to call enkiint() on ^C - enkimode(mode); // next. save beep bit, clear kiflag. -} // enkiinit + signal(SIGINT, enkiint); // tell C lib to call enkiint() on ^C + enkimode(mode); // next. save beep bit, clear kiflag. +} // enkiinit //===================================================================== -void FC enkimode( // Set keyboard interrupt mode and conditionally reset interrupt flag - - SI mode ) // Keyboard interrupt mode. defines in envpak.h: - // KIBEEP: beep on ^C. Always used 11-12-89. - // KISILENT(0): don't beep. No uses. - // KICLEAR (0): always clear internal control flag - // KILEAVEHIT: do not clear hit not yet seen by program (any real uses? dospak:pause() 11-89... */ +void FC +enkimode( // Set keyboard interrupt mode and conditionally reset interrupt flag + + SI mode) // Keyboard interrupt mode. defines in envpak.h: + // KIBEEP: beep on ^C. Always used 11-12-89. + // KISILENT(0): don't beep. No uses. + // KICLEAR (0): always clear internal control flag + // KILEAVEHIT: do not clear hit not yet seen by program (any real + // uses? dospak:pause() 11-89... */ { - kimode = mode; // save mode (beep bit tested in enkiint) - if (kiflag == KISEEN // if hit has been seen by program - || ((mode & KILEAVEHIT) == 0)) // or not KILEAVEHIT (ie is KICLEAR) - kiflag = KIWAITING; // clear internal control flag -} // enkimode + kimode = mode; // save mode (beep bit tested in enkiint) + if (kiflag == KISEEN // if hit has been seen by program + || ((mode & KILEAVEHIT) == 0)) // or not KILEAVEHIT (ie is KICLEAR) + kiflag = KIWAITING; // clear internal control flag +} // enkimode //===================================================================== -LOCAL void CDEC enkiint( // no NEAR -- passed to another function +LOCAL void CDEC enkiint( // no NEAR -- passed to another function -// Control is transferred here when user issues a keyboard interrupt + // Control is transferred here when user issues a keyboard interrupt - INT /*val*/ ) // MSC runtimes pass an arg. UNUSED ARG. + INT /*val*/) // MSC runtimes pass an arg. UNUSED ARG. { - signal( SIGINT, enkiint); // reactivate at C library level + signal(SIGINT, enkiint); // reactivate at C library level #ifdef ENVDEBUG -0 printf("\nENKIINT called..."); + 0 printf("\nENKIINT called..."); #endif - if (kiflag == KIWAITING) // change "waiting" to "hit" - kiflag = KIHIT; // but leave KISEEN unchanged -#if 0 // sound not supported, 4-12 + if (kiflag == KIWAITING) // change "waiting" to "hit" + kiflag = KIHIT; // but leave KISEEN unchanged +#if 0 // sound not supported, 4-12 x if (kimode & KIBEEP) // if requested when mode set x enbeep(BEEPINT); // beep. sound.cpp. #endif -} // enkiint +} // enkiint //===================================================================== -int enkichk() // Check whether a keyboard interrupt has been received +int enkichk() // Check whether a keyboard interrupt has been received -// Returns non-0 if ^C has occurred and has not yet been cleared by calling enkimode(). +// Returns non-0 if ^C has occurred and has not yet been cleared by calling +// enkimode(). { #if 1 - return CheckAbort(); + return CheckAbort(); #else - // currently not implemented under Windows 2-94. - // to implement, watch keyboard messages in (main) window proc. + // currently not implemented under Windows 2-94. + // to implement, watch keyboard messages in (main) window proc. - return FALSE; // claim no ^C typed + return FALSE; // claim no ^C typed #endif -} // enkichk +} // enkichk //===================================================================== - - /*-------------------------- FUNCTION DECLARATIONS ------------------------*/ // PUBLIC functions called only from msc lib or interrupt code; no prototype // in envpak.h so inadvertent external calls are flagged. 8-31-91 // INT matherr( struct exception *); private math runtime error fcn; -// actual decl in CRT (formerly math.h), now (12/23) not known -// void CDEC fpeErr( INT, INT); // intercepts floating point errors, and integer errors under Borland -#if 0 // Defined but not declated, 5-22 +// actual +// decl in CRT (formerly math.h), now (12/23) not known +// void CDEC fpeErr( INT, INT); // intercepts floating point errors, and +// integer errors under Borland +#if 0 // Defined but not declated, 5-22 void __cdecl fpeErr( INT, INT); // intercepts floating point errors, and integer errors under Borland #endif /*---------------------------- LOCAL VARIABLES ----------------------------*/ // saved by hello() for byebye() -LOCAL char cwdSave[CSE_MAX_PATH] = {0}; // current directory to restore at exit - +LOCAL char cwdSave[CSE_MAX_PATH] = {0}; // current directory to restore at exit /*----------------------------- TEST CODE ----------------------------------*/ #ifdef TEST -t -t main() /* Test routines */ -t{ -t SI sec, i, j; -struct exception tx; -double x; -RC rc; -t -t j = 0; -t dminit(); /* initialize dynamic memory */ -t hello( NULL); -t -t x = sqrt(-1.); -t x = asin(5.); -t -t #if 0 -t x = 0.; -t x = 1./x; -t #else -t i = j; -t i = 1/i; -t #endif -t -t return 0; -t} /* test main */ -#endif /* TEST */ +t t main() /* Test routines */ + t { + t SI sec, i, j; + struct exception tx; + double x; + RC rc; + t t j = 0; + t dminit(); /* initialize dynamic memory */ + t hello(NULL); + t t x = sqrt(-1.); + t x = asin(5.); + t t #if 0 t x = 0.; + t x = 1. / x; + t #else t i = j; + t i = 1 / i; + t #endif t t return 0; + t +} /* test main */ +#endif /* TEST */ //===================================================================== -void FC hello() // initializes envpak, including re library code error exits and user exits. +void FC hello() // initializes envpak, including re library code error exits and + // user exits. // Inits re the floating point and divide by zero interrupts, // and saves the current dir for use at any exit @@ -326,134 +313,135 @@ signal( SIGFPE, // floating point errors, and integer errors (changes 0:0) un // also, our matherr (below) is linked in for C lib math fcn errs. #endif -//--- integer divide by 0 error setup - // _dos_setvect( 0, (void (interrupt *)())iDiv0Err ); // 0: integer divide by 0 - // to ease reporting calling location (in which case aborts, no return). */ + //--- integer divide by 0 error setup + // _dos_setvect( 0, (void (interrupt *)())iDiv0Err ); // 0: integer divide by + // 0 + // to ease reporting calling location (in which case aborts, no return). */ -//--- save current drive/dir to restore at exit - strcpy(cwdSave,xgetdir()); // save current dir (including drive) in static for byebye to use at exit -} // hello + //--- save current drive/dir to restore at exit + strcpy(cwdSave, xgetdir()); // save current dir (including drive) in static + // for byebye to use at exit +} // hello //===================================================================== -void FC byebye( // cleanup and normal or error exit. +void FC byebye( // cleanup and normal or error exit. - int exitCode ) // DOS errorlevel or other code to return - // to parent process or caller of subroutine package or DLL. - // 0 means ok; use 2 or more for errors to reserve 1 for - // possible alternate good exit (eg output file unchanged, ) + int exitCode) // DOS errorlevel or other code to return + // to parent process or caller of subroutine package or DLL. + // 0 means ok; use 2 or more for errors to reserve 1 for + // possible alternate good exit (eg output file unchanged, ) // does not return to caller. { -//--- restore current directory - xchdir( cwdSave, WRN ); // restore drive and dir, xiopak.cpp. - // Ok NOP if "". cwdSave set in hello, just above. - cwdSave[0] = '\0'; // clear saved dir eg for when subr pkg recalled. + //--- restore current directory + xchdir(cwdSave, WRN); // restore drive and dir, xiopak.cpp. + // Ok NOP if "". cwdSave set in hello, just above. + cwdSave[0] = '\0'; // clear saved dir eg for when subr pkg recalled. - throw exitCode; + throw exitCode; -} // byebye +} // byebye //========================================================================== -UINT doControlFP() -{ -#if defined( _DEBUG) && (CSE_COMPILER==CSE_COMPILER_MSVC) - int cw = _controlfp( 0, 0); - cw &= ~(_EM_OVERFLOW | _EM_UNDERFLOW | _EM_ZERODIVIDE); - _controlfp( cw, _MCW_EM); +UINT doControlFP() { +#if defined(_DEBUG) && (CSE_COMPILER == CSE_COMPILER_MSVC) + int cw = _controlfp(0, 0); + cw &= ~(_EM_OVERFLOW | _EM_UNDERFLOW | _EM_ZERODIVIDE); + _controlfp(cw, _MCW_EM); #endif - return 0; -} // doControlFP + return 0; +} // doControlFP //========================================================================== -#if CSE_COMPILER==CSE_COMPILER_MSVC // TODO (MP) Add table for other compilers -INT CDEC matherr( // Handle errors detected in Microsoft/Borland math library +#if CSE_COMPILER == CSE_COMPILER_MSVC // TODO (MP) Add table for other compilers +INT CDEC matherr( // Handle errors detected in Microsoft/Borland math library - struct _exception *x ) // Exception info structure provided by MSVC library + struct _exception *x) // Exception info structure provided by MSVC library -// Calls rmkerr fcns to report error (as a warning), then returns a 1 to prevent error reporting out of library. +// Calls rmkerr fcns to report error (as a warning), then returns a 1 to prevent +// error reporting out of library. { -static WSTABLE /* { SI key, value; } */ table[] = -{ - { DOMAIN, "domain" }, - { SING, "argument singularity" }, - { OVERFLOW, "overflow" }, - { UNDERFLOW, "underflow" }, - { TLOSS, "total loss of significance" }, - { PLOSS, "partial loss of significance" }, - { 32767, "unknown exception type" } -}; - - errCrit( PWRN, // display critical msg - "X0104: matherr exception (%s), possible argument values are " - "arg1=%lg arg2=%lg", - lookws( x->type, table), // exception type text - x->arg1, x->arg2); // POSSIBLE arg values; not reliable since intrinsic fcns do not set these members - - // limit return value to (float)-able range so no subsequent FPE overflow: - // most math fcn calls in products cast result to float. - - if (x->retval > FHuge) // FHuge = 1.E38, cons.cpp - x->retval = FHuge; - else if (x->retval < -FHuge ) - x->retval = -FHuge; - - return 1; // 1 -> consider error corrected, continue -} // matherr + static WSTABLE /* { SI key, value; } */ table[] = { + {DOMAIN, "domain"}, + {SING, "argument singularity"}, + {OVERFLOW, "overflow"}, + {UNDERFLOW, "underflow"}, + {TLOSS, "total loss of significance"}, + {PLOSS, "partial loss of significance"}, + {32767, "unknown exception type"}}; + + errCrit(PWRN, // display critical msg + "X0104: matherr exception (%s), possible argument values are " + "arg1=%lg arg2=%lg", + lookws(x->type, table), // exception type text + x->arg1, x->arg2); // POSSIBLE arg values; not reliable since + // intrinsic fcns do not set these members + + // limit return value to (float)-able range so no subsequent FPE overflow: + // most math fcn calls in products cast result to float. + + if (x->retval > FHuge) // FHuge = 1.E38, cons.cpp + x->retval = FHuge; + else if (x->retval < -FHuge) + x->retval = -FHuge; + + return 1; // 1 -> consider error corrected, continue +} // matherr #endif //========================================================================== -void CDEC fpeErr( // Handle floating point error exceptions - [[maybe_unused]] INT sigfpe, // SIGFPE is passed (currently not used) - INT code ) // code indicating error +void CDEC fpeErr( // Handle floating point error exceptions + [[maybe_unused]] INT sigfpe, // SIGFPE is passed (currently not used) + INT code) // code indicating error // Calls BSG error routines to report error with PABT. // Note: initialization for this (using signal() ) is in hello() (above). { -#if CSE_COMPILER==CSE_COMPILER_MSVC // TODO (MP) Add table for other compilers - static WSTABLE /* { SI key, char *s; } */ table[] = - { - { FPE_ZERODIVIDE, "divide by 0" }, - { FPE_OVERFLOW, "overflow" }, - { FPE_UNDERFLOW, "underflow" }, - { FPE_INVALID, "invalid (NAN or infinity)" }, - { FPE_INEXACT, "inexact" }, - { FPE_SQRTNEG, "negative sqrt" }, - { FPE_DENORMAL, "denormal" }, - { FPE_UNEMULATED, "unemulated" }, - { FPE_STACKOVERFLOW, "stack overflow" }, - { FPE_STACKUNDERFLOW, "stack underflow" }, - { 32767, "unknown error code" } - }; - -// NOTE: CR/TK version contains code which extracts the error address from -// the stack, allowing msg include info about where error occurred. -// Could be adapted for use here if FPE trackdown is a problem. - -// issue message and abort - - errCrit( PABT, // display critical msg - "X0103: floating point exception %d:\n %s", - code, // show code for unknown cases 1-31-94 - lookws( code, table)); +#if CSE_COMPILER == CSE_COMPILER_MSVC // TODO (MP) Add table for other compilers + static WSTABLE /* { SI key, char *s; } */ table[] = { + {FPE_ZERODIVIDE, "divide by 0"}, + {FPE_OVERFLOW, "overflow"}, + {FPE_UNDERFLOW, "underflow"}, + {FPE_INVALID, "invalid (NAN or infinity)"}, + {FPE_INEXACT, "inexact"}, + {FPE_SQRTNEG, "negative sqrt"}, + {FPE_DENORMAL, "denormal"}, + {FPE_UNEMULATED, "unemulated"}, + {FPE_STACKOVERFLOW, "stack overflow"}, + {FPE_STACKUNDERFLOW, "stack underflow"}, + {32767, "unknown error code"}}; + + // NOTE: CR/TK version contains code which extracts the error address from + // the stack, allowing msg include info about where error occurred. + // Could be adapted for use here if FPE trackdown is a problem. + + // issue message and abort + + errCrit(PABT, // display critical msg + "X0103: floating point exception %d:\n %s", + code, // show code for unknown cases 1-31-94 + lookws(code, table)); #endif - // no return - /* DO NOT ATTEMPT TO RETURN and continue program: state of 8087 unknown; - registers probably clobbered in ways that matter. */ -} // fpeErr + // no return + /* DO NOT ATTEMPT TO RETURN and continue program: state of 8087 unknown; + registers probably clobbered in ways that matter. */ +} // fpeErr //--------------------------------------------------------------------------- -int CheckFP( double v, const char* vTag) // check for valid FP value +int CheckFP(double v, const char *vTag) // check for valid FP value // see CHECKFP macro (cnglob.h) // returns 0 if OK, else 1 -{ const char* t; - if (std::isnan( v)) - t = "NAN"; - else if (!std::isfinite( v)) - t = "Inf"; - else - return 0; - printf( "Bad value: %s %s\n", vTag, t); - return 1; -} // ::CheckFP +{ + const char *t; + if (std::isnan(v)) + t = "NAN"; + else if (!std::isfinite(v)) + t = "Inf"; + else + return 0; + printf("Bad value: %s %s\n", vTag, t); + return 1; +} // ::CheckFP //============================================================================ -#if 0 && !defined( __BORLANDC__) // used under microsoft C; borland generates floating point error +#if 0 && !defined(__BORLANDC__) // used under microsoft C; borland generates + // floating point error 0 //========================================================================== 0 void CDEC iDiv0Err( // report integer divide by zero under MSDOS (MicroSoft C) 0 diff --git a/src/nummeth.cpp b/src/nummeth.cpp index 1f2e46417..3b0dca783 100644 --- a/src/nummeth.cpp +++ b/src/nummeth.cpp @@ -157,7 +157,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. @@ -198,13 +198,8 @@ int secant( // find x given f(x) (secant method) if (fabs(f - f1) > fabs(f - f2)) // make point 1 the closer { - double swap; - swap = x1; - x1 = x2; - x2 = swap; - swap = f1; - f1 = f2; - f2 = swap; + std::swap(x1, x2); + std::swap(f1, f2); } int i; @@ -232,6 +227,88 @@ int secant( // find x given f(x) (secant method) } return i; } // ::secant + +//----------------------------------------------------------------------------- +int secant( // screen secant success; report calcuation 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; + double f1_prev = f1; + + int ret = actualSecant(pFunc, pO, f, eps, x1, f1, x2, f2, epsLo); + if (ret == 0) + return ret; + + x1 = x1_prev; + f1 = f1_prev; + + warn("secant failed; target = {%d}", f); + warn("initial: x1 = {%d}, x2 = {%d}, f1 = {%d}, f2 = {%d}", x1, x2, f1, f2); + + 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 + { + warn("begin iter {%i}", i + 1); + warn("before: x1 = {%d}, x2 = {%d}, f1 = {%d}, f2 = {%d}", 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 + break; + } + + double xN = x1 + (x2 - x1) * (f - f1) / (f2 - f1); + warn("during: xN = {%d}", xN); + + // secant method: new guess assuming local linearity. + x2 = x1; // replace older point + f2 = f1; + x1 = xN; + f1 = (*pFunc)(pO, x1); // new value + + warn("after: x1 = {%d}, x2 = {%d}, f1 = {%d}, f2 = {%d}", x1, x2, f1, f2); + } + return i; +} // ::secant + //----------------------------------------------------------------------------- int regula( // find x given f(x) (regula-falsi method) double (*pFunc)(void *pO, double &x), From 290c76f2bf44cedbc8e81c165be7a560562d827c Mon Sep 17 00:00:00 2001 From: Phil Ahrenkiel Date: Fri, 5 Apr 2024 14:44:04 -0600 Subject: [PATCH 07/21] Restore formatting. --- src/cnglob.h | 873 ++-- src/cnloads.cpp | 11082 +++++++++++++++++++++++----------------------- src/envpak.cpp | 648 +-- src/nummeth.cpp | 647 +-- 4 files changed, 6607 insertions(+), 6643 deletions(-) diff --git a/src/cnglob.h b/src/cnglob.h index aada55f3e..9423834fe 100644 --- a/src/cnglob.h +++ b/src/cnglob.h @@ -6,30 +6,27 @@ // cnglob.h: Global definitions for CSE: include first in all files. /////////////////////////////////////////////////////////////////////////////// -#define GLOB // ifndef GLOB may be used to prevent duplicate compilation +#define GLOB // ifndef GLOB may be used to prevent duplicate compilation -#include "cndefns.h" // configuration definitions - // contains preprocessor #xxx only: used in cnrecs.def + +#include "cndefns.h" // configuration definitions + // contains preprocessor #xxx only: used in cnrecs.def // TODO (MP) enable warnings -#if CSE_OS == CSE_COMPILER_MSVC -#pragma warning(disable : 4793) // do not warn on 'vararg' causes native code - // generation ?C9? -#define _CRT_SECURE_NO_DEPRECATE // do not warn on "insecure" CRT functions - // (strcpy, ) ?C9? -#pragma warning(disable : 4996) // do not warn on ISO deprecated functions - // (stricmp, ) ?C9? -#pragma warning(disable : 4244 4305) // do not warn on double->float conversion -#pragma warning(disable : 4065) // do not warn if only 'default' in switch +#if CSE_OS==CSE_COMPILER_MSVC +#pragma warning( disable: 4793) // do not warn on 'vararg' causes native code generation ?C9? +#define _CRT_SECURE_NO_DEPRECATE // do not warn on "insecure" CRT functions (strcpy, ) ?C9? +#pragma warning( disable: 4996) // do not warn on ISO deprecated functions (stricmp, ) ?C9? +#pragma warning( disable: 4244 4305) // do not warn on double->float conversion +#pragma warning( disable: 4065) // do not warn if only 'default' in switch #else -#define _countof(array) (sizeof(array) / sizeof(array[0])) // Defines MSC macro -#endif // CSE_COMPILER_MSVC +#define _countof(array) (sizeof(array) / sizeof(array[0])) // Defines MSC macro +#endif // CSE_COMPILER_MSVC /*------------------------- Enhanced declarations --------------------------*/ -// following can't be in dtypes.h (without added conditionals) cuz of 16 or 32 -// bit size -typedef long long LLI; // signed 64 bit integer +// following can't be in dtypes.h (without added conditionals) cuz of 16 or 32 bit size +typedef long long LLI; // signed 64 bit integer typedef unsigned long long ULLI; /*---------------------- Windows definitions -------------------------------*/ @@ -51,63 +48,61 @@ typedef unsigned int BOOL; typedef unsigned short WORD; typedef char TCHAR; typedef unsigned char BYTE; -typedef void *PVOID; +typedef void* PVOID; typedef PVOID HANDLE; typedef HANDLE HWND; typedef HANDLE HINSTANCE; typedef HINSTANCE HMODULE; #endif -#define LOCAL static // for file-local functions: a clearer word than "static" -#define STATIC static // for local data; static un-doable for (former) debugging -#define REFDATA \ - static // For readonly, pak-specific data which could be overlayed -#define CDEC // use compiler defaults for linkage, 3-16-10 -#define FC // eliminating all 32-bit fastcalls fixes rampant bad bcc32 4.0 - // compiles 3-3-94. tried -Od and -rd compile options at same time, - // found not necessary. gave up on trying to change individual FC's - // only b4 getting cul.cpp to complete imput compilation 3-94. +#define LOCAL static // for file-local functions: a clearer word than "static" +#define STATIC static // for local data; static un-doable for (former) debugging +#define REFDATA static // For readonly, pak-specific data which could be overlayed +#define CDEC // use compiler defaults for linkage, 3-16-10 +#define FC // eliminating all 32-bit fastcalls fixes rampant bad bcc32 4.0 compiles 3-3-94. + // tried -Od and -rd compile options at same time, found not necessary. + // gave up on trying to change individual FC's only b4 getting cul.cpp to complete imput compilation 3-94. /*---------------------- return codes / error codes ------------------------*/ + // forward-ref types -struct XFILE; // Extended IO packet set up in dm by xfopen, used as arg to othr - // xiopak/xiochar calls -struct FILEINFO; // file info struct, dospak to xiopak -struct XFNINFO; // for xfScanFileName info return +struct XFILE; // Extended IO packet set up in dm by xfopen, used as arg to othr xiopak/xiochar calls +struct FILEINFO; // file info struct, dospak to xiopak +struct XFNINFO; // for xfScanFileName info return struct VROUTINFO; struct VROUTINFO5; struct STBK; struct CULT; // universal #includes -#undef LOGWIN // define to display screen messages to window (re WINorDLL) - // (not maintained, 9-12) +#undef LOGWIN // define to display screen messages to window (re WINorDLL) + // (not maintained, 9-12) -#include -#include -#include -#include -#include +#include #include +#include #include #include -#include +#include +#include #include +#include +#include -#if defined(USE_STDLIB) -#include -#include -#include +#if defined( USE_STDLIB) #include -#include +#include #include +#include +#include +#include #define WVect std::vector -typedef std::map WMapIntStr; +typedef std::map< int, std::string> WMapIntStr; typedef std::string WStr; #endif -#if defined(NODTYPES) +#if defined( NODTYPES) // fixed subset of dtypes.h // avoid circular use of dtypes.h in e.g. rcdef typedef short SI; @@ -122,96 +117,66 @@ typedef long LI; typedef unsigned long ULI; #endif typedef unsigned char UCH; -typedef struct { - SI year; - SI month; - SI mday; - SI wday; -} IDATE; -typedef struct { - SI hour; - SI min; - SI sec; -} ITIME; +typedef struct { SI year; SI month; SI mday; SI wday; } IDATE; +typedef struct { SI hour; SI min; SI sec; } ITIME; typedef short DOY; typedef unsigned SHOY; -typedef struct { - SI year; - SI month; - SI mday; - SI wday; - SI hour; - SI min; - SI sec; -} IDATETIME; +typedef struct { SI year; SI month; SI mday; SI wday; SI hour; SI min; SI sec; } IDATETIME; typedef time_t LDATETIME; #else -#include // portable data types. File auto generated by rcdef - // NOTE: < > required re rcdef build +#include // portable data types. File auto generated by rcdef + // NOTE: < > required re rcdef build #endif -typedef SI RC; // Return Code explenations on the return code section +typedef SI RC; // Return Code explenations on the return code section // RC Return Codes: integers returned by fcns to indicate call outcome // 0 indicates all OK so RC can be cumulatively or'd -const RC RCOK = 0; // fcn succeeded. code assumes 0 value. -const RC RCFATAL = - -1; // fcn encountered fatal error; current process should be abandoned. - // note (RCFATAL | x) == RCFATAL, code may assume -const RC RCBAD = -2; // fcn failed -- general -const RC RCBAD2 = -4; // alternate failure return, for use where needed; note - // RCBAD2|RCBAD == RCBAD. -// values 1 - 16384 reserved, overlap with MH_xxxx definitions (see -// msghans.h and ...) - -// values > 16384 reserved for local definition and use, e.g. RCUNSET in -// cueval.h -const RC RCUNSET = 16385; // returned by e.g. cuEvalxx if unset data accessed - // (eg by probe of a RAT member) - // -- caller may want to treat this error differently. -const RC RCCANNOT = 16386; // return code for "cannot do it, try another way" - // (see tryImInProbe()) -const RC RCNOP = 16390; // fcn did nothing (due to e.g. specific input values) -const RC RCEOF = - 16391; // indicates end of input when returned by ppRead, ppM, ppC, etc -#define CSE_E(f) \ - { \ - rc = (f); \ - if (rc) \ - return rc; \ - } // if fcn f returns error, return it to caller. Note RCOK is 0. -#define CSE_EF(f) \ - { \ - if (f) \ - return RCBAD; \ - } // "fast" variant: return RCBAD for any non-RCOK +const RC RCOK=0; // fcn succeeded. code assumes 0 value. +const RC RCFATAL = -1; // fcn encountered fatal error; current process should be abandoned. + // note (RCFATAL | x) == RCFATAL, code may assume +const RC RCBAD = -2; // fcn failed -- general +const RC RCBAD2 = -4; // alternate failure return, for use where needed; note RCBAD2|RCBAD == RCBAD. +// values 1 - 16384 reserved, overlap with MH_xxxx definitions (see msghans.h and ...) + +// values > 16384 reserved for local definition and use, e.g. RCUNSET in cueval.h +const RC RCUNSET = 16385; // returned by e.g. cuEvalxx if unset data accessed + // (eg by probe of a RAT member) + // -- caller may want to treat this error differently. +const RC RCCANNOT = 16386; // return code for "cannot do it, try another way" (see tryImInProbe()) +const RC RCNOP = 16390; // fcn did nothing (due to e.g. specific input values) +const RC RCEOF = 16391; // indicates end of input when returned by ppRead, ppM, ppC, etc +#define CSE_E(f) { rc = (f); if (rc) return rc; } // if fcn f returns error, return it to caller. Note RCOK is 0. +#define CSE_EF(f) { if (f) return RCBAD; } // "fast" variant: return RCBAD for any non-RCOK // SEC additional system error codes -- in addition to c library errno values. // must not conflict with errno values. See rmkerr:secMsg and xiopak.cpp. -typedef SI - SEC; // system error code data type (matches type of msc c library "errno") -const SEC SECOK = -1; // system error code for no error -const SEC SECEOF = -2; // system error code for EOF (none def by MSC). -const SEC SECOTHER = -3; // system error code indicating that an error - // was signalled but errno was not set -const SEC SECBADFN = - -4; // bad file name, detected in our code (not library). - // msc at least has no such msg. added by rob 1-88. */ -const SEC SECBADRV = -5; // bad drv letter (xiopak:chdir, likely other uses) -const SEC SECBADPATH = 2; // No file or directory - -typedef SI MH; // message handle, used in calls to err() and msg(), identifier - // for msgs in msgtab:msgTbl[] +typedef SI SEC; // system error code data type (matches type of msc c library "errno") +const SEC SECOK = -1; // system error code for no error +const SEC SECEOF = -2; // system error code for EOF (none def by MSC). +const SEC SECOTHER = -3; // system error code indicating that an error + // was signalled but errno was not set +const SEC SECBADFN = -4; // bad file name, detected in our code (not library). + // msc at least has no such msg. added by rob 1-88. */ +const SEC SECBADRV = -5; // bad drv letter (xiopak:chdir, likely other uses) +const SEC SECBADPATH = 2; // No file or directory + +typedef SI MH; // message handle, used in calls to err() and msg(), identifier for msgs in msgtab:msgTbl[] + + + // min and max (converted to template fcns, 10-12) #undef min #undef max -template inline T min(T a, TX b) { - return a < b ? a : b; +template< typename T, typename TX> inline T min(T a, TX b) +{ + return a < b ? a : b; } -template inline T max(T a, TX b) { - return a > b ? a : b; +template< typename T, typename TX> inline T max(T a, TX b) +{ + return a > b ? a : b; } inline double min(double a, double b, double c) { return min(min(a, b), c); } inline double max(double a, double b, double c) { return max(max(a, b), c); } @@ -224,343 +189,262 @@ inline double max(double a, double b, double c, double d, double e, double f) { /*------------- Error Actions and Options ("erOp" arguments) --------------*/ /*--- error reporting and handling options, for rmkerr and its many callers. */ -// error action: controls error:err response to error -const int IGN = 0x0001; // ignore: no message, silently continue execution -const int INF = 0x0004; // info -const int ERR = 0x0000; // error: msg to screen, error file, and err report if - // open, continue execution. - // 0, often omitted. + // error action: controls error:err response to error +const int IGN = 0x0001; // ignore: no message, silently continue execution +const int INF = 0x0004; // info +const int ERR = 0x0000; // error: msg to screen, error file, and err report if open, continue execution. + // 0, often omitted. const int REG = ERR; -const int WRN = 0x0002; // issue msg as ERR, continue -const int ABT = 0x0003; // issue msg, abort program. +const int WRN = 0x0002; // issue msg as ERR, continue +const int ABT = 0x0003; // issue msg, abort program. // const int KEYP = 0x0002; // keypress bit: on in WRN/ABT, off in IGN/INF/ERR -// eliminated 2-23-2023 -const int ERAMASK = 0x0007; // mask to clear all option/app bits for testing for - // IGN/WRN/ABT. general use. - -const int PROGERR = 0x0008; // program error bit: on in PWRN/PABT (removing - // makes WRN/ABT), off in others. -const int PERR = ERR | PROGERR; // program error (different msg wording) ERR -const int PWRN = WRN | PROGERR; // program error WRN -const int PABT = ABT | PROGERR; // program error ABT -const int NOPREF = 0x0010; // do not add "Error: "/"Program Error: " prefix to - // message text. 2-94. -const int SYSMSG = 0x0020; // Do GetLastError(), add system error msg -const int SHOFNLN = 0x0040; // show input file name and line # (orMsg) -const int RTMSG = - 0x0080; // runtime format (include day/hour info in msg) (orMsg) -const int IGNX = - 0x0100; // "extra ignore" -- no msg, do not return RCBAD (orMsg) + // eliminated 2-23-2023 +const int ERAMASK = 0x0007; // mask to clear all option/app bits for testing for IGN/WRN/ABT. general use. + +const int PROGERR = 0x0008; // program error bit: on in PWRN/PABT (removing makes WRN/ABT), off in others. +const int PERR = ERR | PROGERR; // program error (different msg wording) ERR +const int PWRN = WRN | PROGERR; // program error WRN +const int PABT = ABT | PROGERR; // program error ABT +const int NOPREF = 0x0010; // do not add "Error: "/"Program Error: " prefix to message text. 2-94. +const int SYSMSG = 0x0020; // Do GetLastError(), add system error msg +const int SHOFNLN = 0x0040; // show input file name and line # (orMsg) +const int RTMSG = 0x0080; // runtime format (include day/hour info in msg) (orMsg) +const int IGNX = 0x0100; // "extra ignore" -- no msg, do not return RCBAD (orMsg) const int ERRRT = ERR | RTMSG; const int WRNRT = WRN | RTMSG; -enum class MSGTY { - msgtyUNKNOWN = 0, - msgtyERROR, - msgtyWARNING, - msgtyINFO, - msgtyDEBUG -}; +enum class MSGTY { msgtyUNKNOWN = 0, msgtyERROR, msgtyWARNING, msgtyINFO, msgtyDEBUG }; // options for rmkErr:screen() and :logit() remark display -const int NONL = 0x1000; // do NOT force remark to the beginning of its own line - // (default: start each message on new line) -const int DASHES = 0x2000; // separate this remark from preceding & following - // message with ------------ -const int NOSCRN = 0x4000; // logit: do not also display on screen -const int QUIETIF = - 0x8000; // screen: do not display if zn screenQuiet (rmkErr.cpp) +const int NONL = 0x1000; // do NOT force remark to the beginning of its own line (default: start each message on new line) +const int DASHES = 0x2000; // separate this remark from preceding & following message with ------------ +const int NOSCRN = 0x4000; // logit: do not also display on screen +const int QUIETIF = 0x8000; // screen: do not display if zn screenQuiet (rmkErr.cpp) // options for errI() isWarn -const int NOERRFILE = - 0x1000; // do NOT write message to error file (override default behavior) -const int NOREPORT = - 0x2000; // do NOT write message to report file (override default behavior) -// const int NOSCRN = 0x4000; // do NOT write message to screen (override -// default behavior) +const int NOERRFILE = 0x1000; // do NOT write message to error file (override default behavior) +const int NOREPORT = 0x2000; // do NOT write message to report file (override default behavior) +// const int NOSCRN = 0x4000; // do NOT write message to screen (override default behavior) + // application option bits in same argument word as error action. // Options for specific functions are defined in other .h files in terms of // the following ---------- users include ------ -const int EROP1 = 0x010000; // vrpak.h dmpak.h xiopak.h [ratpak.h] -const int EROP2 = 0x020000; // vrpak.h dmpak.h [xiopak.h] sytb.cpp -const int EROP3 = 0x040000; // vrpak.h wfpak.h [xiopak.h] -const int EROP4 = 0x080000; // vrpak.h wfpak.h -const int EROP5 = 0x100000; // vrpak.h wfpak.h -const int EROP6 = 0x200000; // vrpak.h yacam.h -const int EROP7 = 0x400000; // pgpak.h [ratpak] -const int EROMASK = 0xff0000; // mask for application option bits +const int EROP1 = 0x010000; // vrpak.h dmpak.h xiopak.h [ratpak.h] +const int EROP2 = 0x020000; // vrpak.h dmpak.h [xiopak.h] sytb.cpp +const int EROP3 = 0x040000; // vrpak.h wfpak.h [xiopak.h] +const int EROP4 = 0x080000; // vrpak.h wfpak.h +const int EROP5 = 0x100000; // vrpak.h wfpak.h +const int EROP6 = 0x200000; // vrpak.h yacam.h +const int EROP7 = 0x400000; // pgpak.h [ratpak] +const int EROMASK = 0xff0000; // mask for application option bits /*----------------------------- constants ----------------------------------*/ #define TRUE 1 #define FALSE 0 /*--------------------- Definitions for CSE headers ------------------------*/ -typedef void *DMP; // Dynamic memory block pointer: ptr to any type, record - // struct, etc, of caller's -const int defaultCpl = - 78; // default chars/line, used when Top.repCpl not available - // see getCpl() - +typedef void* DMP; // Dynamic memory block pointer: ptr to any type, record struct, etc, of caller's +const int defaultCpl = 78; // default chars/line, used when Top.repCpl not available + // see getCpl() + /*----------------------------- CSE headers --------------------------------*/ -#include "dmpak.h" // Uses EROP1, EROP2, RC, DMP, IGN and ABT -#include "psychro.h" +#include "dmpak.h" // Uses EROP1, EROP2, RC, DMP, IGN and ABT #include "rmkerr.h" #include "strpak.h" -#include "vecpak.h" // Uses min and max definitions +#include "psychro.h" +#include "vecpak.h" // Uses min and max definitions /*---------------------- global macros and defines -------------------------*/ // useful constants -constexpr double kPi = 3.14159265358979323846; -constexpr double k2Pi = 2. * kPi; -constexpr double k4Pi = 4. * kPi; -constexpr double kPiOver2 = kPi / 2.; -constexpr double g0Std = - 32.2; // standard acceleration of gravity (g0 or "little g"), ft/sec2 -constexpr double sigmaSB = 0.1714e-8; // stefan-boltzman constant, Btuh/ft2-R4 -constexpr double sigmaSB4 = sigmaSB * 4.; // 4 * ditto -constexpr double sigmaSBSI = 5.669e-8; // stefan-boltzman constant, W/m2-K4 -constexpr double tAbs0F // 0 F in Rankine -#if defined(CZM_COMPARE) - = 460.; +constexpr double kPi=3.14159265358979323846; +constexpr double k2Pi=2.*kPi; +constexpr double k4Pi=4.*kPi; +constexpr double kPiOver2=kPi/2.; +constexpr double g0Std = 32.2; // standard acceleration of gravity (g0 or "little g"), ft/sec2 +constexpr double sigmaSB = 0.1714e-8; // stefan-boltzman constant, Btuh/ft2-R4 +constexpr double sigmaSB4 = sigmaSB*4.; // 4 * ditto +constexpr double sigmaSBSI = 5.669e-8; // stefan-boltzman constant, W/m2-K4 +constexpr double tAbs0F // 0 F in Rankine +#if defined( CZM_COMPARE) + = 460.; #else - = 459.67; + = 459.67; #endif constexpr double tol_tF = 1.e-12; // temperature tolerance (degF) + /*------------------------------- math -------------------------------------*/ // error handling extern UINT doControlFP(); #if CSE_OS == CSE_OS_WINDOWS && CSE_ARCH == 32 -#define FPCHECK __asm { fwait } // trap pending FP exception (if any) - // re searching for FP exceptions +#define FPCHECK __asm { fwait } // trap pending FP exception (if any) + // re searching for FP exceptions #else #define FPCHECK ; #endif // floating point compare const double ABOUT0 = 0.001; -template inline int fAboutEqual(T a, T b) { - return fabs(a - b) < ABOUT0; -} -template inline T frDiff(T a, T b, T tolAbs = T(.00001)) { - T d = abs(a - b); - return d <= max(tolAbs, T(1e-30)) ? T(0.) : T(2.) * d / (abs(a) + abs(b)); +template< typename T> inline int fAboutEqual(T a, T b) { return fabs(a-b) < ABOUT0; } +template< typename T> inline T frDiff( T a, T b, T tolAbs=T(.00001)) +{ T d=abs(a-b); + return d<=max( tolAbs, T( 1e-30)) ? T( 0.) : T(2.)*d/(abs( a)+abs( b)); } -#define ABSCHANGE(is, was) (fabs((is) - (was))) // absolute change of float -#define RELCHANGE(is, was) \ - (fabs(((is) - (was)) / \ - (fabs(is) + \ - .1f))) // relative change of float as fraction, w /0 protection +#define ABSCHANGE(is,was) (fabs((is)-(was))) // absolute change of float +#define RELCHANGE(is,was) (fabs( ((is)-(was)) / (fabs(is)+.1f) )) // relative change of float as fraction, w /0 protection // setToXxx: formerly non-template floor( a, b) and ceil( a, b), 10-12 // setToMax: a = max( a, b) -template inline T setToMax(T &a, TX b) { - if (b > a) - a = T(b); - return a; -} +template< typename T, typename TX> inline T setToMax( T &a, TX b) +{ if (b > a) a = T( b); return a; } // setToMin: a = min( a, b) -template inline T setToMin(T &a, TX b) { - if (b < a) - a = T(b); - return a; -} +template< typename T, typename TX> inline T setToMin( T &a, TX b) +{ if (b < a) a = T( b); return a; } // setToMinOrMax: if doMin setToMin( a, b) else setToMax( a, b) -template -inline T setToMinOrMax(T &a, TX b, int doMin) { - if ((b > a) ^ doMin) - a = T(b); - return a; -} +template< typename T, typename TX> inline T setToMinOrMax( T &a, TX b, int doMin) +{ if ((b > a)^doMin) a = T( b); return a; } // return -1, 0, or 1 per value of v -template T xsign(T v) { return v == 0 ? 0 : v > 0 ? 1 : -1; } +template< typename T> T xsign( T v) { return v==0 ? 0 : v>0 ? 1 : -1; } // return -1 if <, 0 if == , or 1 if > -template int LTEQGT(T v1, T v2) { - return v1 > v2 ? 1 : v1 < v2 ? -1 : 0; -} +template< typename T> int LTEQGT( T v1, T v2) { return v1>v2 ? 1 : v1 inline T bracket(T vMin, T v, T vMax) { - return v < vMin ? vMin : v > vMax ? vMax : v; -} +template< typename T> inline T bracket( T vMin, T v, T vMax) +{ return v < vMin ? vMin : v > vMax ? vMax : v; } // ifBracket: limit value, return 1 iff changed -template inline int ifBracket(T vMin, T &v, T vMax) { - if (v < vMin) { - v = vMin; - return 1; - } - if (v > vMax) { - v = vMax; - return 1; - } - return 0; +template< typename T> inline int ifBracket( T vMin, T& v, T vMax) +{ if (v < vMin) { v = vMin; return 1; } + if (v > vMax) { v = vMax; return 1; } + return 0; } // debugging aid: warn if limits invoked -template inline T bracketWarn(T vMin, T v, T vMax) { - T vx = bracket(vMin, v, vMax); - if (vx != v) - warn("bracketWarn: hit limit\n"); - return vx; -} -template inline void tswap(T v1, T v2) { - T vx = v1; - v1 = v2; - v2 = vx; +template< typename T> inline T bracketWarn( T vMin, T v, T vMax) +{ T vx = bracket( vMin, v, vMax); + if (vx != v) + warn( "bracketWarn: hit limit\n"); + return vx; } +template< typename T> inline void tswap( T v1, T v2) +{ T vx = v1; v1 = v2; v2 = vx; } // snap to specified value -template int snap(T &v, T vSnap, double tol = 1e-10) { - if (fabs(v - vSnap) < tol) { - v = vSnap; - return 1; - } - return 0; -} +template< typename T> int snap( T& v, T vSnap, double tol=1e-10) +{ if (fabs(v-vSnap) inline T snapTrig(T &v, double tol = 1e-10) { - return snap(v, T(0), tol) || snap(v, T(1), tol) || snap(v, T(-1), tol); -} -template int snapRAD(T &v, double tol = .1) { - return snap(v, T(0), tol) || v < T(0) - ? snap(v, T(-kPiOver2), tol) || snap(v, T(-kPi), tol) - : snap(v, T(kPiOver2), tol) || snap(v, T(kPi), tol); +template< typename T> inline T snapTrig( T& v, double tol=1e-10) +{ return snap( v, T( 0), tol) || snap( v, T( 1), tol) || snap( v, T( -1), tol); } +template< typename T> int snapRAD( T& v, double tol=.1) +{ return snap( v, T( 0), tol) + || v < T( 0) ? snap( v, T( -kPiOver2), tol) || snap( v, T( -kPi), tol) + : snap( v, T( kPiOver2), tol) || snap( v, T( kPi), tol); } -template int snapDEG(T &v, double tol = .1) { - return snap(v, T(0), tol) || v < T(0) - ? snap(v, T(-90), tol) || snap(v, T(-180), tol) - : snap(v, T(90), tol) || snap(v, T(180), tol); +template< typename T> int snapDEG( T& v, double tol=.1) +{ return snap( v, T( 0), tol) + || v < T( 0) ? snap( v, T( -90), tol) || snap( v, T( -180), tol) + : snap( v, T( 90), tol) || snap( v, T( 180), tol); } // round v to nearest rv (e.g. roundNearest( 14., 5.) -> 15. -template inline void roundNearest(T &v, T rv) { - v = round(v / rv) * rv; -} +template< typename T> inline void roundNearest( T &v, T rv) +{ v = round( v/rv)*rv; } // round double to integer (no overflow protection!) -inline int iRound(double a) { return int(a > 0. ? a + .5 : a - .5); } +inline int iRound( double a) { return int( a > 0. ? a+.5 : a-.5 ); } // return v if not nan, else alternative value -template inline T ifNotNaN(T v, T vForNaN = 0) { - return isnan(v) ? vForNaN : v; -} +template< typename T> inline T ifNotNaN( T v, T vForNaN=0) +{ return std::isnan( v) ? vForNaN : v; } //----------------------------------------------------------------------------- +#if !defined(NODTYPES) // access to interval data // returns ref to array mbr for C_IVLCH_H/D/M/Y -template T &IvlData(T *ivlData, int ivl) { -#if defined(_DEBUG) - return ivlData[bracketWarn(0, ivl - C_IVLCH_Y, IVLDATADIM - 1)]; +template< typename T> T& IvlData( T* ivlData, int ivl) +{ +#if defined( _DEBUG) + return ivlData[ bracketWarn( 0, ivl-C_IVLCH_Y, IVLDATADIM-1) ]; #else - return ivlData[ivl - C_IVLCH_Y]; + return ivlData[ ivl-C_IVLCH_Y]; #endif -} // IvlData -#if 0 // idea +} // IvlData +#if 0 // idea 0 template< typename T> void SetIvlData( T* ivlData, T v, int ivl1, ivl2) 0 { VSet( ivlData+ivl1-C_IVLCH_Y, ivl2-ivl1+1, v); 0 } #endif +#endif -/*---------------------------- dept of NANs: unset, nandles, nchoices - * ---------------------------*/ +/*---------------------------- dept of NANs: unset, nandles, nchoices ---------------------------*/ -// a NANDLE is a 32-bit quantity that is not a valid IEEE float number nor an -// expected string pointer value, used to indicate unset data and to specify the -// "expression number" of runtime expression entered for the variable -// or whose value is to be stored in the variable later. More discussion in -// exman.h, exman.cpp. -// A NANDLE contains 0xFF80 in the hi word and 0 for unset or ffff for -// autosizing or expression number 1-16383 in the low word. CAUTION: highly -// system dependent. CAUTION: keep distinct from NCHOICEs (below) +// a NANDLE is a 32-bit quantity that is not a valid IEEE float number nor an expected string pointer value, +// used to indicate unset data and to specify the "expression number" of runtime expression entered for the variable +// or whose value is to be stored in the variable later. More discussion in exman.h, exman.cpp. +// A NANDLE contains 0xFF80 in the hi word and 0 for unset or ffff for autosizing or expression number 1-16383 in the low word. +// CAUTION: highly system dependent. CAUTION: keep distinct from NCHOICEs (below) // type to hold a NANDLE or a datum -typedef uint32_t NANDAT; // 32 bit unsigned integer -#define NANDLE(h) \ - (static_cast( \ - 0xff800000 + \ - h)) // "expr n" ref for float/int (or SI in 4 bytes). h = 1..16383. -#define UNSET (NANDLE(0)) // "unset" value for float/int. cast as desired. -#define ASING \ - (NANDLE( \ - 0xffff)) // may be stored in values to be determined by autosizing 6-95 -template inline NANDAT AsNANDAT(T &v) { - return *reinterpret_cast(&v); -} -#define ISUNSET(v) (AsNANDAT(v) == UNSET) // true iff v is UNSET -#define ISASING(v) (AsNANDAT(v) == ASING) // true iff v is "to be autosized" -#define ISNANDLE(v) \ - ((AsNANDAT(v) & 0xffff0000L) == \ - 0xff800000L) // true iff v is ref to non-constant expr (or unset) -#define ISNUM(v) \ - ((AsNANDAT(v) & 0x7f800000L) != \ - 0x7f800000L) // true iff float v is number (not UNSET, NANDLE, NCHOICE or - // other NAN) -#define ISNANDLEP(pV) \ - ((*(reinterpret_cast(pV)) & 0xffff0000L) == \ - 0xff800000L) // test for ptr to ref to non-constant expr (or unset) -#define EXN(v) (AsNANDAT(v) & 0xffff) // extract expression # from nandle - -// macro to access a float that may contain a NAN: don't let compiler treat as -// floats til numeric content verified. usage: CSE_V x = CSE_V y; if (CSE_V x -// == CSE_V y) .. where x and y are floats such as CHOICN's. +typedef uint32_t NANDAT; // 32 bit unsigned integer +#define NANDLE(h) (static_cast(0xff800000 + h)) // "expr n" ref for float/int (or SI in 4 bytes). h = 1..16383. +#define UNSET (NANDLE(0)) // "unset" value for float/int. cast as desired. +#define ASING (NANDLE(0xffff)) // may be stored in values to be determined by autosizing 6-95 +template inline NANDAT AsNANDAT(T& v) { return *reinterpret_cast(&v); } +#define ISUNSET( v) (AsNANDAT( v)==UNSET) // true iff v is UNSET +#define ISASING( v) (AsNANDAT( v)==ASING) // true iff v is "to be autosized" +#define ISNANDLE( v) ((AsNANDAT( v) & 0xffff0000L)==0xff800000L) // true iff v is ref to non-constant expr (or unset) +#define ISNUM(v) ((AsNANDAT(v) & 0x7f800000L) != 0x7f800000L) // true iff float v is number (not UNSET, NANDLE, NCHOICE or other NAN) +#define ISNANDLEP(pV) ((*(reinterpret_cast(pV)) & 0xffff0000L)==0xff800000L) // test for ptr to ref to non-constant expr (or unset) +#define EXN(v) (AsNANDAT(v) & 0xffff) // extract expression # from nandle + + +// macro to access a float that may contain a NAN: don't let compiler treat as floats til numeric content verified. +// usage: CSE_V x = CSE_V y; if (CSE_V x == CSE_V y) .. where x and y are floats such as CHOICN's. #define CSE_V *(NANDAT *)& // NCHOICEs are values which can represent one of several choices and which can // be stored in a float and distinguished from all numeric values. 2-92. -// NCHOICEs are used in CHOICN data types, definable thru rcdef.exe per input -// file cndtypes.def. NCHOICES are stored as IEEE NAN's (not-a-number's) in the -// form 0x7f80 plus the choice number 1-7f in the hi word. NCHOICES must be -// kept distinct from UNSET and NANDLEs (hi word ff8x) (above/exman.h). +// NCHOICEs are used in CHOICN data types, definable thru rcdef.exe per input file cndtypes.def. +// NCHOICES are stored as IEEE NAN's (not-a-number's) in the form 0x7f80 plus the choice number 1-7f in the hi word. +// NCHOICES must be kept distinct from UNSET and NANDLEs (hi word ff8x) (above/exman.h). // CAUTION: to test a float's bit pattern, cast to a pointer then to ULI. -// Casting directly to ULI causes its numeric value to be converted, altering -// the bit pattern +// Casting directly to ULI causes its numeric value to be converted, altering the bit pattern -// macro to fetch/store into variable n's hi word. Use w 16-bit flag/choice # -// dtypes.h C_DTYPE_XXXX constants gen'd by rcdef. +// macro to fetch/store into variable n's hi word. Use w 16-bit flag/choice # dtypes.h C_DTYPE_XXXX constants gen'd by rcdef. // usage: float y; CHN(y) = C_ABCNC_X; if (CHN(y)==C_ABCNC_X) ... -#define CHN(n) \ - (*((uint16_t *)&(n) + 1)) // access hi word, lvalue use ok. 80x86 DEPENDENT. - // PCMS<--grep target. +#define CHN(n) (*((uint16_t*)&(n)+1)) // access hi word, lvalue use ok. 80x86 DEPENDENT. PCMS<--grep target. -#define NCNAN \ - 0x7f80 // bits that make nchoice a nan; is combined with choice index 1-7f to - // form stored value +#define NCNAN 0x7f80 // bits that make nchoice a nan; is combined with choice index 1-7f to form stored value // macro to test if n has an NCHOICE value: -#define ISNCHOICE(n) ((AsNANDAT(n) & 0xff800000L) == 0x7f800000L) -// macro to generate 32-bit value from 16-bit choice constants, for use where -// full value needed, as in initialized data +#define ISNCHOICE(n) ((AsNANDAT(n) & 0xff800000L)==0x7f800000L) +// macro to generate 32-bit value from 16-bit choice constants, for use where full value needed, as in initialized data // usage: float y = NCHOICE(C_ABCNC_X); -#define NCHOICE(nck) \ - (NANDAT(static_cast(nck) \ - << 16)) // put in hi word. nck must include 0x7f80. +#define NCHOICE(nck) (NANDAT(static_cast(nck) << 16)) // put in hi word. nck must include 0x7f80. + // ------------------------- Debug aid ASSERT macro ------------------------- // // Issues message and terminates program if argument is false. -// Like compiler library "assert" macro (assert.h), but exits our way so DDE is -// properly terminated, etc (otherwise windows program dissappears from screen -// but does not actually exit promptly, necessitating Windows restart to reRun -// program.) 8-95. +// Like compiler library "assert" macro (assert.h), but exits our way so DDE is properly terminated, etc +// (otherwise windows program dissappears from screen but does not actually exit promptly, +// necessitating Windows restart to reRun program.) 8-95. -#ifdef NDEBUG // (un)def above -#define ASSERT(p) ((void)0) // if ommitting assertion checks +#ifdef NDEBUG // (un)def above + #define ASSERT(p) ((void)0) // if ommitting assertion checks #else -#define ASSERT(p) \ - ((p) ? (void)0 \ - : ourAssertFail(#p, __FILE__, __LINE__)) // function in rmkerr.cpp + #define ASSERT(p) ((p) ? (void)0 : ourAssertFail( #p, __FILE__, __LINE__)) // function in rmkerr.cpp #endif // compile-time assert (ugly but finds errors) -#define STATIC_ASSERT(condition, name) \ - typedef char assert_failed_##name[(condition) ? 1 : -1]; +#define STATIC_ASSERT( condition, name ) \ + typedef char assert_failed_ ## name[ (condition) ? 1 : -1 ]; // check floating point value // calls function that does _isnan, _finite, etc -#if defined(_DEBUG) -extern int CheckFP(double v, const char *tag); -#define CHECKFP(v) CheckFP(v, #v); +#if defined( _DEBUG) +extern int CheckFP( double v, const char* tag); +#define CHECKFP( v) CheckFP( v, #v); #else // compile to nothing in release -#define CHECKFP(v) +#define CHECKFP( v) #endif -#if 0 && !defined(_DEBUG) +#if 0 && !defined( _DEBUG) // Intrinsics // DEBUG build slightly slower (2%?) with no intrinsics and possibly easier to debug // intrinsics @@ -574,152 +458,133 @@ extern int CheckFP(double v, const char *tag); // (did not test, but expect pow-like advantage) // memset 15 19 (does not win) // strlen 12 17 (does not win) -#pragma intrinsic(fabs, abs, labs, pow, fmod, acos, asin, cosh, sinh, tanh) +#pragma intrinsic( fabs, abs, labs, pow, fmod, acos, asin, cosh, sinh, tanh) // release build does not use intrinsics w/o #pragma // changes debug build to alternative call -#pragma intrinsic(sin, cos, tan, atan, atan2, exp, log, log10, sqrt) +#pragma intrinsic( sin, cos, tan, atan, atan2, exp, log, log10, sqrt) /* intrinsics which don't win in size (7-3-89 MSC 5.0): memcpy (24 -> 43), memcmp (24 -> 55), strlen (18 -> 24), strcpy (20 -> 55), strcat (20 -> 64), min, max (error: "not available as intrinsic") */ -#endif // _DEBUG +#endif // _DEBUG + // COMMONLY USED TYPES and defines -typedef USI RCT; // record type type. Needed for RATBASE defn below, for - // ratpak.h, and rccn.h. -struct SFIR; // small fields-in-record info table (for basAnc.fir, SRD.fir). - // struct in srd.h. -#define DMPP(p) ((DMP *)DMP(&(p))) -inline void IncP(void **pp, int b) { *pp = (void *)((char *)(*pp) + b); } +typedef USI RCT; // record type type. Needed for RATBASE defn below, for ratpak.h, and rccn.h. +struct SFIR; // small fields-in-record info table (for basAnc.fir, SRD.fir). struct in srd.h. +#define DMPP( p) ((DMP *)DMP( &(p))) +inline void IncP(void** pp, int b) { *pp = (void*)((char*)(*pp) + b); } class record; -extern class TOPRAT Top; // top-level record universally accessible -typedef USI PSOP; // type for pseudo-code opcodes -- used in sevaral .cpp files - // and in cuparse.h -namespace Pumbra { -class Penumbra; -} -namespace Kiva { -class Instance; -class Aggregator; -class Foundation; -} // namespace Kiva -namespace Btwxt { -class RegularGridInterpolator; -} +extern class TOPRAT Top; // top-level record universally accessible +typedef USI PSOP; // type for pseudo-code opcodes -- used in sevaral .cpp files and in cuparse.h +namespace Pumbra { class Penumbra; } +namespace Kiva { class Instance; class Aggregator; class Foundation; } +namespace Btwxt { class RegularGridInterpolator; } #ifdef WINorDLL -// control of scattered code for returning file names used via a cse() argument. -#define OUTPNAMS // expect to test this in cse.cpp, rmkerr.cpp, vrpak.cpp, etc. - // 10-6-93 + // control of scattered code for returning file names used via a cse() argument. + #define OUTPNAMS // expect to test this in cse.cpp, rmkerr.cpp, vrpak.cpp, etc. 10-6-93 #endif // cse.h. keep after OUTPNAMS define! #ifdef OUTPNAMS -void FC -saveAnOutPNam(const char *pNam); // accepts output file name to return to caller + void FC saveAnOutPNam( const char* pNam); // accepts output file name to return to caller #endif // for init/cleanup procedure args 10-93 -enum CLEANCASE // caution code assumes STARTUP < ENTRY < others. -{ /*STARTUP=1,*/ // possible future: initializing, at DLL entry - ENTRY = 2, // initializing at user call to subr package. May follow a previous - // "fatal" error exit. - DONE, // cleanup from normal completion path, after success or error - CRASH // cleanup after longjmp out after "fatal" error (that terminated - // program prior to 10-93) -#ifdef DLL -/*, CLOSEDOWN */ // possible future: terminating library, from WEP procedure -#endif +enum CLEANCASE // caution code assumes STARTUP < ENTRY < others. +{ /*STARTUP=1,*/ // possible future: initializing, at DLL entry + ENTRY=2, // initializing at user call to subr package. May follow a previous "fatal" error exit. + DONE, // cleanup from normal completion path, after success or error + CRASH // cleanup after longjmp out after "fatal" error (that terminated program prior to 10-93) + #ifdef DLL + /*, CLOSEDOWN */ // possible future: terminating library, from WEP procedure + #endif }; -// stored in place of record subscript for special words entered, where -// permitted. Code assumes negative. +// stored in place of record subscript for special words entered, where permitted. Code assumes negative. // cul.cpp, cucnultx.cpp, cgresult.cpp, etc 1-92 -#define TI_SUM \ - -3 // "sum" entered: use sum of all (zones, etc), not a specific one. -#define TI_ALL \ - -4 // "all" entered: do report, warmest zone control, etc for all records - // (zones, meters, etc). -#define TI_ALLBUT -5 // "all_but" entered (at start of list of TI's). +#define TI_SUM -3 // "sum" entered: use sum of all (zones, etc), not a specific one. +#define TI_ALL -4 // "all" entered: do report, warmest zone control, etc for all records (zones, meters, etc). +#define TI_ALLBUT -5 // "all_but" entered (at start of list of TI's). /*------------------------- COMMONLY USED FUNCTIONS ------------------------*/ // here to reduce need to include files. // cncult.cpp (or stub in e.g. rcdef.cpp) -int getCpl(class TOPRAT **pTp = NULL); // get chars/line - // defaultCpl if Top not yet init & input - // value unavailable +int getCpl( class TOPRAT** pTp=NULL); // get chars/line + // defaultCpl if Top not yet init & input value unavailable // re DLL interrupt (in cse.cpp, stub in rcdef.cpp) int CheckAbort(); // messages.cpp -const char *msg(char *mBuf, MSGORHANDLE mOrH, ...); +const char* msg( char* mBuf, MSGORHANDLE mOrH, ...); // commonly used pow variants -inline float pow2(float v) { return v * v; } -inline double pow2(double v) { return v * v; } -inline float pow3(float v) { return v * v * v; } -inline double pow3(double v) { return v * v * v; } -inline float pow4(float v) { return v * v * v * v; } -inline double pow4(double v) { return v * v * v * v; } -inline float pow033(float v) { -#if defined(FASTPOW) - ? +inline float pow2( float v) { return v*v; } +inline double pow2( double v) { return v*v; } +inline float pow3( float v) { return v*v*v; } +inline double pow3( double v) { return v*v*v; } +inline float pow4( float v) { return v*v*v*v; } +inline double pow4( double v) { return v*v*v*v; } +inline float pow033( float v) +{ +#if defined( FASTPOW) + ? #else - return powf(v, .333f); + return powf( v, .333f); #endif } -inline float DegFtoR(float f) { return float(f + tAbs0F); } -inline double DegFtoR(double f) { return f + tAbs0F; } -inline float DegRtoF(float r) { return float(r - tAbs0F); } -inline double DegRtoF(double r) { return r - tAbs0F; } -inline float DegFtoC(float f) { return float((f - 32.) / 1.8); } -inline double DegFtoC(double f) { return (f - 32.) / 1.8; } -inline float DegCtoF(float c) { return float(c * 1.8 + 32.); } -inline double DegCtoF(double c) { return c * 1.8 + 32.; } -inline float DegCtoK(float c) { return float(c + 273.15); } -inline double DegCtoK(double c) { return c + 273.15; } -inline constexpr float DegFtoK(float f) { return float((f + tAbs0F) / 1.8); } -inline double DegFtoK(double f) { return (f + tAbs0F) / 1.8; } -inline float DegKtoF(float k) { return float(k * 1.8 - tAbs0F); } -inline double DegKtoF(double k) { return k * 1.8 - tAbs0F; } - -inline double QRad(double t1, double t2) // radiant power betw 2 black surfaces -{ - return sigmaSB * (pow4(DegFtoR(t1)) - pow4(DegFtoR(t2))); -} +inline float DegFtoR( float f) { return float( f+tAbs0F); } +inline double DegFtoR( double f) { return f+tAbs0F; } +inline float DegRtoF( float r) { return float( r-tAbs0F); } +inline double DegRtoF( double r) { return r-tAbs0F; } +inline float DegFtoC( float f) { return float( (f-32.)/1.8); } +inline double DegFtoC( double f) { return (f-32.)/1.8; } +inline float DegCtoF( float c) { return float( c*1.8+32.); } +inline double DegCtoF( double c) { return c*1.8+32.; } +inline float DegCtoK(float c) { return float(c + 273.15); } +inline double DegCtoK(double c) { return c + 273.15; } +inline constexpr float DegFtoK(float f) { return float((f + tAbs0F) / 1.8); } +inline double DegFtoK( double f) { return (f+tAbs0F)/1.8; } +inline float DegKtoF( float k) { return float( k*1.8-tAbs0F); } +inline double DegKtoF( double k) { return k*1.8-tAbs0F; } + + +inline double QRad( double t1, double t2) // radiant power betw 2 black surfaces +{ return sigmaSB*(pow4( DegFtoR( t1))-pow4(DegFtoR( t2))); } // Radians <--> degrees -template inline T DEG(T r) { return T(r * 180. / kPi); } -template inline T RAD(T d) { return T(d * kPi / 180.); } +template inline T DEG( T r) { return T( r*180./kPi); } +template inline T RAD( T d) { return T( d*kPi/180.); } // Btuh/ft2-F <--> W/m2-K -const double cfU = 5.678263; // W/m2-K per Btuh/ft2-F -inline float UIPtoSI(float u) { return float(u * cfU); } -inline double UIPtoSI(double u) { return u * cfU; } -inline float USItoIP(float u) { return float(u / cfU); } -inline double USItoIP(double u) { return u / cfU; } +const double cfU = 5.678263; // W/m2-K per Btuh/ft2-F +inline float UIPtoSI( float u) { return float( u*cfU); } +inline double UIPtoSI( double u) { return u*cfU; } +inline float USItoIP( float u) { return float( u/cfU); } +inline double USItoIP( double u) { return u/cfU; } // Irradiance Btuh/ft2 <--> W/m2 const double cfIr = 3.154591; -inline float IrIPtoSI(float ir) { return float(ir * cfIr); } -inline double IrIPtoSI(double ir) { return ir * cfIr; } -inline float IrSItoIP(float ir) { return float(ir / cfIr); } -inline double IrSItoIP(double ir) { return ir / cfIr; } +inline float IrIPtoSI( float ir) { return float( ir*cfIr); } +inline double IrIPtoSI( double ir) { return ir*cfIr; } +inline float IrSItoIP( float ir) { return float( ir/cfIr); } +inline double IrSItoIP( double ir) { return ir/cfIr; } // length ft <--> m -const double cfL = 1. / 3.28084; // m/ft -inline float LIPtoSI(float l) { return float(l * cfL); } -inline double LIPtoSI(double l) { return l * cfL; } -inline float LSItoIP(float l) { return float(l / cfL); } -inline double LSItoIP(double l) { return l / cfL; } +const double cfL = 1./3.28084; // m/ft +inline float LIPtoSI( float l) { return float( l*cfL); } +inline double LIPtoSI( double l) { return l*cfL; } +inline float LSItoIP( float l) { return float( l/cfL); } +inline double LSItoIP( double l) { return l/cfL; } // area ft^2 <--> m^2 -const double cfA = cfL * cfL; // m2/ft2 +const double cfA = cfL*cfL; // m2/ft2 inline float AIPtoSI(float a) { return float(a * cfA); } inline double AIPtoSI(double a) { return a * cfA; } inline float ASItoIP(float a) { return float(a / cfA); } @@ -753,52 +618,47 @@ inline float MFRSItoIP(float mfr) { return mfr * cfMFR; } inline double MFRIPtoSI(double mfr) { return float(mfr / cfMFR); } inline double MFRSItoIP(double mfr) { return mfr / cfMFR; } + // velocity mph <-> m/s -const double cfV = 1. / 2.23694; // m/s / mph -template inline T VIPtoSI(T mph) { return T(mph * cfV); } -template inline T VSItoIP(T ms) { return T(ms / cfV); } +const double cfV = 1./2.23694; // m/s / mph +template< typename T> inline T VIPtoSI(T mph) { return T( mph*cfV); } +template< typename T> inline T VSItoIP(T ms) { return T( ms/cfV); } // air mass flow <--> air volume flow // amf in lbm/hr // cfm in ft3/min std air -inline double AMFtoAVF(double amf) { return amf / (60. * .075); } -inline float AMFtoAVF(float amf) { return amf / (60.f * .075f); } -inline double AVFtoAMF(double avf) { return avf * 60. * .075; } -inline float AVFtoAMF(float avf) { return avf * 60.f * .075f; } +inline double AMFtoAVF( double amf) { return amf / (60.*.075); } +inline float AMFtoAVF( float amf) { return amf / (60.f*.075f); } +inline double AVFtoAMF( double avf) { return avf * 60.*.075; } +inline float AVFtoAMF( float avf) { return avf * 60.f*.075f; } // amf in lbm/s -inline float AMFtoAVF2(float amf) { return amf * 60.f / .075f; } +inline float AMFtoAVF2( float amf) { return amf * 60.f / .075f; } + // Powers of 10 -const int PTENSIZE = 21; // # of pwrs of 10 in Pten array -const double Pten[PTENSIZE] = {1., 10., 1e2, 1e3, 1e4, 1e5, 1e6, - 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, - 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20}; -const int NPTENSIZE = 21; // # of negative pwrs of 10 in NPten array -const double NPten[NPTENSIZE] = { - 1., .1, .01, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, 1e-9, 1e-10, - 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16, 1e-17, 1e-18, 1e-19, 1e-20}; - -const float FHuge = - float(1.E38); // Handy large (not strictly largest) value used for x/0, - // search initializations, etc. Single defn for portability - // and code clarity. +const int PTENSIZE = 21; // # of pwrs of 10 in Pten array +const double Pten[PTENSIZE] = {1.,10.,1e2,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13,1e14,1e15,1e16,1e17,1e18,1e19,1e20}; +const int NPTENSIZE = 21; // # of negative pwrs of 10 in NPten array +const double NPten[NPTENSIZE] = {1.,.1,.01,1e-3,1e-4,1e-5,1e-6,1e-7,1e-8,1e-9,1e-10,1e-11,1e-12,1e-13,1e-14,1e-15,1e-16,1e-17,1e-18,1e-19,1e-20 }; + +const float FHuge = float(1.E38); // Handy large (not strictly largest) value used for x/0, + // search initializations, etc. Single defn for portability and code clarity. // Physical constants #if 1 -const float BtuperWh = 3.41214f; // Btu per Wh -const float BtuperkWh = 3412.14f; // Btu per kWh -const float waterRhoCp = - 8.29018282f; // water volumetric weight, lb/gal - // = volumetric heat capacity, Btu/gal-F - // 30 C (85 F) value used, appropriate for DHW calcs +const float BtuperWh = 3.41214f; // Btu per Wh +const float BtuperkWh = 3412.14f; // Btu per kWh +const float waterRhoCp = 8.29018282f; // water volumetric weight, lb/gal + // = volumetric heat capacity, Btu/gal-F + // 30 C (85 F) value used, appropriate for DHW calcs #else -const float BtuperWh = 3.413f; // Btu per Wh -const float BtuperkWh = 3413.f; // Btu per kWh +const float BtuperWh = 3.413f; // Btu per Wh +const float BtuperkWh = 3413.f; // Btu per kWh const float waterRhoCp = 8.345f; #endif -const float galPerFt3 = 7.48052f; // gal per cubic foot +const float galPerFt3 = 7.48052f; // gal per cubic foot -#if 0 // obsolete +#if 0 // obsolete x const float SrcMultElect = 3.f; // Source multiplier for electrical energy (California CEC value) x const float BtuSrcperWh = 10.239f; /* Btu (source energy) per Wh. Includes California electric x to source conversion factor of 3 (3 * 3.413 = 10.239); @@ -806,20 +666,20 @@ x used to convert electrical consumption to source energy */ #endif // re solar gain distribution (must be known for rccn.h) -enum { socOPEN, socCLSD, socCOUNT }; // internal shade modes (open, closed) -enum { // irrad components - sgcBMXBM, // beam per unit exterior beam - sgcDFXDF, // diffuse per exterior diffuse - sgcDFXBM, // diffuse per exterior beam - sgcCOUNT +enum { socOPEN, socCLSD, socCOUNT }; // internal shade modes (open, closed) +enum { // irrad components + sgcBMXBM, // beam per unit exterior beam + sgcDFXDF, // diffuse per exterior diffuse + sgcDFXBM, // diffuse per exterior beam + sgcCOUNT }; // surface classes e.g. for argument to topSf1(), topSf2() // NOTE: code depends on value order, change with care -enum SFCLASS { sfcNUL, sfcSURF, sfcDOOR, sfcWINDOW, sfcDUCT, sfcPIPE }; +enum SFCLASS { sfcNUL, sfcSURF, sfcDOOR, sfcWINDOW, sfcDUCT, sfcPIPE}; // zone air flow indices -#if 0 // unused, 2-20-2013 +#if 0 // unused, 2-20-2013 x // note: zafCOUNT must equal #define ZAFCOUNT in cndefns.h (re use in cnrecs.def) x enum ZAFTY { zafANINF, zafANVENT, zafINFIL, zafDUCTLKI, zafSYSAIRI, x zafDUCTLKO, zafSYSAIRO, zafDUCTLKILS, zafCOUNT }; @@ -830,32 +690,33 @@ x const int zanSYSAIR = 0x00000008; x const int zanSYSAIRUB = 0x00000010; #endif -#if 1 || defined(_DEBUG) -#define DEBUGDUMP // define to include DbPrintf() etc code + +#if 1 || defined( _DEBUG) +#define DEBUGDUMP // define to include DbPrintf() etc code #endif -bool DbDo(DWORD oMsk, int options = 0); +bool DbDo( DWORD oMsk, int options=0); // debugging option bits // re control of conditional DbPrintf()s // const DWORD dbdALWAYS = 1; // defined in rmkerr.h -const DWORD dbdCULT = 4; // CULT input tables -const DWORD dbdCONSTANTS = 8; // various constants +const DWORD dbdCULT = 4; // CULT input tables +const DWORD dbdCONSTANTS = 8; // various constants const DWORD dbdAIRNET = 16; const DWORD dbdMASS = 32; -const DWORD dbdZM = 64; // main zone model debug print control -const DWORD dbdDUCT = 128; // duct model -const DWORD dbdHPWH = 256; // heat pump water heater (writes to external CSV) -const DWORD dbdRADX = 512; // radiant exchange and SGDIST -const DWORD dbdRCM = 1024; // radiant/convective model details -const DWORD dbdIZ = 2048; // selected interzone info -const DWORD dbdSGDIST = 4096; // solar gain distrib -const DWORD dbdASHWAT = 8192; // ASHWAT call/return -// const DWORD dbdCOMFORT = 16384; // Comfort model values (minimal as of -// 1-11) -const DWORD dbdSBC = 16384; // surface conditions +const DWORD dbdZM = 64; // main zone model debug print control +const DWORD dbdDUCT = 128; // duct model +const DWORD dbdHPWH = 256; // heat pump water heater (writes to external CSV) +const DWORD dbdRADX = 512; // radiant exchange and SGDIST +const DWORD dbdRCM = 1024; // radiant/convective model details +const DWORD dbdIZ = 2048; // selected interzone info +const DWORD dbdSGDIST = 4096; // solar gain distrib +const DWORD dbdASHWAT = 8192; // ASHWAT call/return +// const DWORD dbdCOMFORT = 16384; // Comfort model values (minimal as of 1-11) +const DWORD dbdSBC = 16384; // surface conditions // NOTE: max value = 32768 due to USI limit for expression values // workaround: create additional user-settable values // shift / combine these values in tp_SetDbMask() // 1-2012 + // end of cnglob.h diff --git a/src/cnloads.cpp b/src/cnloads.cpp index 2990a0336..5573a8415 100644 --- a/src/cnloads.cpp +++ b/src/cnloads.cpp @@ -2,26 +2,25 @@ // Use of this source code is governed by a BSD-style license // that can be found in the LICENSE file. -// cnloads.cpp -- Hourly simulation loads modelling calculations for a single -// hour for CSE +// cnloads.cpp -- Hourly simulation loads modelling calculations for a single hour for CSE /*------------------------------- INCLUDES --------------------------------*/ -#include "cnglob.h" // global defines +#include "cnglob.h" // global defines -#include "ancrec.h" // record: base class for rccn.h classes -#include "irats.h" -#include "rccn.h" // IZXRAT SGRAT ZNR #include "srd.h" +#include "ancrec.h" // record: base class for rccn.h classes +#include "rccn.h" // IZXRAT SGRAT ZNR +#include "irats.h" #include "timer.h" -#include "cgwthr.h" // Wthr.nZ .zD -#include "hvac.h" // CoolingSHR and Heat Pump 95/47 sizing -#include "psychro.h" // psyHCondWtr +#include "hvac.h" // CoolingSHR and Heat Pump 95/47 sizing +#include "psychro.h" // psyHCondWtr +#include "cgwthr.h" // Wthr.nZ .zD -#include "cnguts.h" -#include "cse.h" #include "cueval.h" #include "exman.h" +#include "cse.h" +#include "cnguts.h" #include "mspak.h" #include "nummeth.h" @@ -32,25 +31,26 @@ #include "comfort/comfort.h" #endif -#include "ashwface.h" // #includes xmodule.h +#include "ashwface.h" // #includes xmodule.h /*-------------------------------- OPTIONS --------------------------------*/ // 7-92 MARG1 and MARG2 2.0 made NO DIFFERENCE even in # itertions (q2, q3, q4) // suspect means humidity is limiting condition. -#define MARG1 1. // tolerance multiplier for some tests in loadsHourBeg. -#define MARG2 1. // tolerance multiplier for some tests in loadsSubhr. -#undef ZNHVACFCONV // define to activate consideration of - // HVAC heat conv/radiant ratio (incomplete) - // Disabled 1-2021 pending elaboration. +#define MARG1 1. // tolerance multiplier for some tests in loadsHourBeg. +#define MARG2 1. // tolerance multiplier for some tests in loadsSubhr. +#undef ZNHVACFCONV // define to activate consideration of + // HVAC heat conv/radiant ratio (incomplete) + // Disabled 1-2021 pending elaboration. /*-------------------------------- DEFINES --------------------------------*/ /*----------------------- LOCAL FUNCTION DECLARATIONS ---------------------*/ static RC loadsIzxSh1(); static RC loadsIzxSh2(); -static RC loadsSurfaces(BOO subhrly); +static RC loadsSurfaces( BOO subhrly); static RC loadsXFans(); + /*------------------------ The MAIN EQUATION story ------------------------*/ // rob 12-89 prelim /* @@ -58,13 +58,13 @@ Start with the first law of thermodynamics: the change in enthalpy (temperature (Tz) times heat capacity hc) of an object (such as the air in a zone) is equal to the sum of the heat flows (powers) Qj into it: - dTz - --- * hc = sigma(Qj) - dt + dTz + --- * hc = sigma(Qj) + dt Change the derivative to a delta for numerical approx purposes, and write: - (Tz - Tzold) * hc / t = sigma(Qj) + (Tz - Tzold) * hc / t = sigma(Qj) where Tzold is the prior cycle temperature and t is the time interval. @@ -83,25 +83,25 @@ Solve for Tz: Tz*hc/t + Tz*sigma(UAi) = sigma(UAi*Ti) + Tzold*hc/t + Qheat + sigma(Qj) Tz * (hc/t + sigma(UAi)) = ditto - sigma(UAi*Ti) + Tzold*hc/t + Qheat + sigma(Qj) + sigma(UAi*Ti) + Tzold*hc/t + Qheat + sigma(Qj) Tz = ---------------------------------------------- sigma(UAi) + hc/t Define "a" as the numerator less Qheat, and "b" as the denominator. The MAIN EQUATION can then be written in the following two forms: - a + Qheat - Tz = --------- Qheat = Tz *z * b - a - b + a + Qheat + Tz = --------- Qheat = Tz *z * b - a + b where a = sigma(UAi*Ti) + sigma(Qj) + Tzold*hc/t - The sum of the heat flows thru conductances plus solar - and other heat flows plus the old temperature * hc/t. + The sum of the heat flows thru conductances plus solar + and other heat flows plus the old temperature * hc/t. and b = sigma(UAi) + hc/t - The sum of the conductances to other objects plus hc/t. + The sum of the conductances to other objects plus hc/t. Application to ZONES: The above formulas are used to simulate zone (air) @@ -109,10 +109,10 @@ temperatures. When Tz is fixed, Qheat is computed; when Qheat is 0 (Tz floating between heat and cool thermostat settings, or in an unconditioned space), Tz is computed. -Zone temperatures are computed in cnloads.cpp/cnhvac.cpp, using various -subexpressions (members of the ZNR struct) precomputed in cnguts.cpp. They are -computed several times an hour; t is Top.tp_subhrDur. For "hc" the CAIR is used --- the "air heat capacity" which has all the not-very-massive heat capacity of +Zone temperatures are computed in cnloads.cpp/cnhvac.cpp, using various subexpressions +(members of the ZNR struct) precomputed in cnguts.cpp. They are computed +several times an hour; t is Top.tp_subhrDur. For "hc" the CAIR is used -- +the "air heat capacity" which has all the not-very-massive heat capacity of the space (walls, furniture) lumped into it for modelling purposes. MASSes: It turns out that the model must account for the fact that the @@ -133,111 +133,105 @@ But Masses now all changed 1-95. RC ZNR::zn_RddInit() // initialization common to main simulation run and each autosize design day { - // Set up initial and constant values - // some redundant (not needed each design day) but cheap - zn_md = 1; // zone hvac mode (control mode) - tz = aTz = tzls = tzlh = 80.f; // zone air temps, incl ah working copy - zn_tr = zn_trls = zn_trlh = 80.f; // zone radiant temps - wz = aWz = wzls = Top.tp_refW; // zone humidity ratios 5-25-92 - zn_rho0ls = zn_Rho0(); // zone air density at tz, wz, pz0 - zn_bcon // init constant part of main equation denom: - = zn_ua // sum uval*area of zone ambient lite surfs - + zn_uaSpecT; // sum uval*area of zn specT lite surfs (hsu) - i.znCAirSh = i.znCAir / Top.tp_subhrDur; // commonly used in subhr code - - // Mass->ha's done in ms_RddInit - // aMassHr = aMassSh = 0.; object is pre-0'd but may be re-used in - // autoSizing - // but believe these don't - // need init for start-interval masses - haMass = - 0.; // +='d in ms_rddInit. pre-0'd object may be re-used in autoSizing. - znXLGain = znXLGainLs = - 0; // no condensation heat leftover from prior iteration, rob 6-11-97 - zn_ebErrCount = 0; // count of short-interval energy balance errors - zn_pz0WarnCount[0] = zn_pz0WarnCount[1] = 0; - - // HVAC convective delivery fraction - // needs elaboration for radiant systems - zn_fConvH = zn_fConvC = zn_fConv = 1.f; // used iff ZNHVACFCONV defined - - return RCOK; -} // ZNR::zn_RddInit() + // Set up initial and constant values + // some redundant (not needed each design day) but cheap + zn_md = 1; // zone hvac mode (control mode) + tz = aTz = tzls = tzlh = 80.f; // zone air temps, incl ah working copy + zn_tr = zn_trls = zn_trlh = 80.f; // zone radiant temps + wz = aWz = wzls = Top.tp_refW; // zone humidity ratios 5-25-92 + zn_rho0ls = zn_Rho0(); // zone air density at tz, wz, pz0 + zn_bcon // init constant part of main equation denom: + = zn_ua // sum uval*area of zone ambient lite surfs + + zn_uaSpecT; // sum uval*area of zn specT lite surfs (hsu) + i.znCAirSh = i.znCAir / Top.tp_subhrDur; // commonly used in subhr code + + // Mass->ha's done in ms_RddInit + // aMassHr = aMassSh = 0.; object is pre-0'd but may be re-used in autoSizing + // but believe these don't need init for start-interval masses + haMass = 0.; // +='d in ms_rddInit. pre-0'd object may be re-used in autoSizing. + znXLGain = znXLGainLs = 0; // no condensation heat leftover from prior iteration, rob 6-11-97 + zn_ebErrCount = 0; // count of short-interval energy balance errors + zn_pz0WarnCount[0] = zn_pz0WarnCount[1] = 0; + + // HVAC convective delivery fraction + // needs elaboration for radiant systems + zn_fConvH = zn_fConvC = zn_fConv = 1.f; // used iff ZNHVACFCONV defined + + return RCOK; +} // ZNR::zn_RddInit() //----------------------------------------------------------------------------- -RC ZNR::zn_RddDone( // called at end of simulation and each autosize design day - bool isAusz) // true: autosize - // false: simulation +RC ZNR::zn_RddDone( // called at end of simulation and each autosize design day + bool isAusz) // true: autosize + // false: simulation // duplicate calls harmless // NOTE: clears zn_pz0WarnCount[] -// +// // returns RCOK iff all OK { - RC rc = RCOK; - - // report count of AirNet pressure warnings - // see AIRNET_SOLVER::an_CheckResults() and ZNR::zn_CheckAirNetPressure() - if (zn_pz0WarnCount[0] > 0 || zn_pz0WarnCount[1] > 0) { - warn("Zone '%s': %s unreasonable pressure warning counts --" - "\n mode 0 = %d / mode 1 = %d", - Name(), - isAusz ? strtprintf("%s autosizing", Top.tp_AuszDoing()) - : "Simulation", - zn_pz0WarnCount[0], zn_pz0WarnCount[1]); - zn_pz0WarnCount[0] = zn_pz0WarnCount[1] = 0; // prevent duplicate msg - } - - return rc; -} // ZNR::zn_RddDone + RC rc = RCOK; + + // report count of AirNet pressure warnings + // see AIRNET_SOLVER::an_CheckResults() and ZNR::zn_CheckAirNetPressure() + if (zn_pz0WarnCount[0] > 0 || zn_pz0WarnCount[1] > 0) + { + warn("Zone '%s': %s unreasonable pressure warning counts --" + "\n mode 0 = %d / mode 1 = %d", + Name(), + isAusz ? strtprintf("%s autosizing", Top.tp_AuszDoing()) : "Simulation", + zn_pz0WarnCount[0], zn_pz0WarnCount[1]); + zn_pz0WarnCount[0] = zn_pz0WarnCount[1] = 0; // prevent duplicate msg + } + + return rc; +} // ZNR::zn_RddDone //==================================================================== -RC FC loadsHourBeg() // start of hour loads stuff: solar gains, hourly masses, - // zones init, . +RC FC loadsHourBeg() // start of hour loads stuff: solar gains, hourly masses, zones init, . // non-RCOK return means terminate run. Message already issued. { - /* CALCULATE HOURLY SOLAR GAINS (most are subhourly, done elsewhere) - Set "targets" in [zones and] masses to their solar gains for hour, - calculated by combining hour's weather data and control variable values - (window shade positions) with the precalculated (cgsolar.cpp) factors in the - entries in the (currently selected) Solar Gain Rat. The factors already - reflect the size, orientation, and absorptivity of each XSURF and the sun's - position this month. cnrecs.def/rccn.h structures. */ - SGRAT *sge; - RLUP(SgR, sge) { - if (!sge->sg_isSubhrly) - sge->sg_ToTarg(Top.radBeamHrAv, Top.radDiffHrAv); - } - - // ducts - DUCTSEG *ds; - RLUP(DsR, ds) - ds->ds_BegHour(); - - IZXRAT *ize; - RLUP(IzxR, ize) - ize->iz_BegHour(); // start-hour zone transfer / airnet (ignore return) - -#if defined(ZONE_XFAN) - // SIMULATE zone exhaust fans (xfans) - // xfan flow controlled hourly by schedule - // do before masses (may alter convective xfer (future)) - // do before airnet (so flows are known) - loadsXFans(); -#endif - - // SIMULATE HOURLY MASSES Updates their surface temperatures. - // (All light ("quick") are subhourly.) - // Uses hourly mass solar gains (set in loop above) - // sets zone aMassHr's (used in loop below). - loadsSurfaces(FALSE); // below. FALSE to do hourly masses. - - // zones - ZNR *zp; - RLUP(ZrB, zp) - zp->zn_BegHour2(); - - RSYS *rs; - RLUP(RsR, rs) - rs->rs_BegHour(); + /* CALCULATE HOURLY SOLAR GAINS (most are subhourly, done elsewhere) + Set "targets" in [zones and] masses to their solar gains for hour, calculated by combining + hour's weather data and control variable values (window shade positions) with the + precalculated (cgsolar.cpp) factors in the entries in the (currently selected) Solar Gain Rat. + The factors already reflect the size, orientation, and absorptivity of each XSURF and the + sun's position this month. cnrecs.def/rccn.h structures. */ + SGRAT* sge; + RLUP( SgR, sge) + { if (!sge->sg_isSubhrly) + sge->sg_ToTarg( Top.radBeamHrAv, Top.radDiffHrAv); + } + + // ducts + DUCTSEG* ds; + RLUP( DsR, ds) + ds->ds_BegHour(); + + IZXRAT* ize; + RLUP( IzxR, ize) + ize->iz_BegHour(); // start-hour zone transfer / airnet (ignore return) + +#if defined( ZONE_XFAN) + // SIMULATE zone exhaust fans (xfans) + // xfan flow controlled hourly by schedule + // do before masses (may alter convective xfer (future)) + // do before airnet (so flows are known) + loadsXFans(); +#endif + + // SIMULATE HOURLY MASSES Updates their surface temperatures. + // (All light ("quick") are subhourly.) + // Uses hourly mass solar gains (set in loop above) + // sets zone aMassHr's (used in loop below). + loadsSurfaces( FALSE); // below. FALSE to do hourly masses. + + // zones + ZNR* zp; + RLUP( ZrB, zp) + zp->zn_BegHour2(); + + RSYS* rs; + RLUP( RsR, rs) + rs->rs_BegHour(); #if 0 // !defined( SHINTERP) else done subhrly below o// MASSES LOOP: @@ -252,404 +246,357 @@ o ZrB.p[ mse->outside.zi ].qMsSg += mse->outside.sg; // add surf's outside sol o } #endif - return RCOK; // error return above -} // loadsHourBeg + return RCOK; // error return above +} // loadsHourBeg //----------------------------------------------------------------------------- -void SGRAT::sg_ToTarg( // apply solar gain to target - float bmRad, // beam radiation on normal surface, Btuh/ft2 - float dfRad) // diffuse radiation on horizontal surface, Btuh/ft2 +void SGRAT::sg_ToTarg( // apply solar gain to target + float bmRad, // beam radiation on normal surface, Btuh/ft2 + float dfRad) // diffuse radiation on horizontal surface, Btuh/ft2 // can be called hourly or subhourly, args must appropriately correspond to time { - if (bmRad <= 0. && dfRad <= 0.) // if no insolation this hour - { - sg_pTarg->st_bm = - 0.; // just zero all targets. Unnecessary to test sge->addIt. - sg_pTarg->st_df = 0.; - } else { - double bmGain = bmRad * sg_bmXBmF[Top.iHrST]; - double dfGain = dfRad * sg_dfXDfF[Top.iHrST] + bmRad * sg_dfXBmF[Top.iHrST]; - if (sg_pControl) // if this SGRAT has a control - { - bmGain *= - *sg_pControl; // multiply gain by it. eg zone shades-closed fraction. - dfGain *= *sg_pControl; - } - if (sg_addIt == 0) // if 1st for target - { - sg_pTarg->st_bm = bmGain; // store gain, initializing target value - sg_pTarg->st_df = dfGain; - } else // if additional for target - { - sg_pTarg->st_bm += bmGain; // add gain to previous - sg_pTarg->st_df += dfGain; - } - // note: addIt != 0 probably now 3-90 corresponds to control != NULL - // but we don't depend on that assumption. - } - sg_pTarg->st_tot = sg_pTarg->st_bm + sg_pTarg->st_df; // total gain -} // SGRAT::sg_ToTarg + if (bmRad <= 0. && dfRad <= 0.) // if no insolation this hour + { sg_pTarg->st_bm = 0.; // just zero all targets. Unnecessary to test sge->addIt. + sg_pTarg->st_df = 0.; + } + else + { double bmGain = bmRad * sg_bmXBmF[ Top.iHrST]; + double dfGain = dfRad * sg_dfXDfF[ Top.iHrST] + bmRad * sg_dfXBmF[ Top.iHrST]; + if (sg_pControl) // if this SGRAT has a control + { bmGain *= *sg_pControl; // multiply gain by it. eg zone shades-closed fraction. + dfGain *= *sg_pControl; + } + if (sg_addIt==0) // if 1st for target + { sg_pTarg->st_bm = bmGain; // store gain, initializing target value + sg_pTarg->st_df = dfGain; + } + else // if additional for target + { sg_pTarg->st_bm += bmGain; // add gain to previous + sg_pTarg->st_df += dfGain; + } + // note: addIt != 0 probably now 3-90 corresponds to control != NULL + // but we don't depend on that assumption. + } + sg_pTarg->st_tot = sg_pTarg->st_bm + sg_pTarg->st_df; // total gain +} // SGRAT::sg_ToTarg //----------------------------------------------------------------------------- -void SGRAT::sg_DbDump() const { - DbPrintf("\nSGDIST '%s': isSubhrly=%d addIt=%d\n%s targ=%p control=%p", - Name(), sg_isSubhrly, sg_addIt, Top.tp_RepTestPfx(), sg_pTarg, - sg_pControl); - for (int iH = 0; iH < 24; iH++) { - if (iH % 4 == 0) - DbPrintf("\n"); - DbPrintf("%8.2d %6.2f %6.2f %6.2f", iH, sg_dfXDfF[iH], sg_dfXBmF[iH], - sg_bmXBmF[iH]); - } -} // sg_DbDump +void SGRAT::sg_DbDump() const +{ + DbPrintf( "\nSGDIST '%s': isSubhrly=%d addIt=%d\n%s targ=%p control=%p", + Name(), sg_isSubhrly, sg_addIt, + Top.tp_RepTestPfx(), sg_pTarg, sg_pControl); + for (int iH=0; iH<24; iH++) + { if (iH%4 == 0) + DbPrintf( "\n"); + DbPrintf("%8.2d %6.2f %6.2f %6.2f", + iH, sg_dfXDfF[ iH], sg_dfXBmF[ iH], sg_bmXBmF[ iH]); + } +} // sg_DbDump //----------------------------------------------------------------------------- -RC ZNR::zn_BegHour1() // "early" hourly initializations +RC ZNR::zn_BegHour1() // "early" hourly initializations // 0s gains and other values // done *after* expressions, before dependent inits { - qrIgTot = qrIgTotO = qrIgTotIz = qrIgAir = 0.f; - znSGain = znLGain = znLitDmd = znLitEu = 0.; - zn_anVentEffect = 0; // # of IZXRATs that *could* impact this - // zone when operated in vent mode. - // see IZXRAT::iz_HasVentEffect() - - if (Top.isBegRun) { // prior hour setpoints - zn_tzspHlh = i.znTH; - zn_tzspDlh = i.znTD; - zn_tzspClh = i.znTC; - } - return RCOK; -} // ZNR::zn_BegHour1 + qrIgTot = qrIgTotO = qrIgTotIz = qrIgAir = 0.f; + znSGain = znLGain = znLitDmd = znLitEu = 0.; + zn_anVentEffect = 0; // # of IZXRATs that *could* impact this + // zone when operated in vent mode. + // see IZXRAT::iz_HasVentEffect() + + if (Top.isBegRun) + { // prior hour setpoints + zn_tzspHlh = i.znTH; + zn_tzspDlh = i.znTD; + zn_tzspClh = i.znTC; + } + return RCOK; +} // ZNR::zn_BegHour1 //----------------------------------------------------------------------------- -RC ZNR::zn_BegHour2() // beginning-of-hour calcs for zone -{ - zn_SetAirRadXArea(); // update air temp/relHum dependent radiant exchange - // values - // for convective/radiant zone model; - // small impact, not worth substep cost - - // hour's sensible internal gain: scheduled value, if any. - // Used for znaqLdHr, zp->qsIg, and to apportion to subhr results. - qsIgHr = znSGain; // sensible gain totalled from GAIN records - // also: qrIgAir: NOT included here0 - - // Non-hvac hour-constant portions of MAIN EQUATION "Tz = (a + q)/b" (story at - // start file) - - // sum ua*t for specified-temp-exposure surfaces - XSRAT *xs /*= NULL*/; - zn_uaXSpecT = 0.; - for (TI xi = xsSpecT1; xi != 0; - xi = xs->nxXsSpecT) // these xsurfs have own chain. loop over them. - { - xs = XsB.p + xi; // access XSRAT record xi - zn_uaXSpecT += xs->x.xs_uval * xs->x.xs_area * - xs->x.sfExT; // u * a * outside temp (hourly vbl) - /*>>>> there are 2 hourly and 1 subhourly use of uval*area --> otta - precompute it, maybe also use in cnguts re ua -SpecT. FLOAT gud enuf for - the latter? rob 3-91. */ - } - - // zn_aqLdHr: hour-constant non-hvac a's and q's for numerator. a = - // sigma(UAi*Ti) + Tzold*hc/t. - zn_aqLdHr = - aMassHr // hourly masses UAT (from loadsSurfaces call just above) - + - qsIgHr // q's: sched sens internal gain from GAINS (znSGAIN) & wthr file. - + - qrIgAir // q's: radiant internal gain to zone CAir (light surfaces) 11-95. - + zn_uaXSpecT; // uaT total from just above - // all other components (infil etc) done subhourly - - // zn_bLdHr: hour-constant non-hvac b's for denominator. b = sigma(UA) + - // hc/t. - zn_bLdHr = zn_bcon; // just the run-const UAs - - /* zn_xqHr: q = b * t - a using only hourly components, for change tests - (just below) only: Intended to be appropriately sensitive to changes when - b*a almost equal to a, eg when aMassHr settling & all else constant, cuz - terminal loads are of form b * t - a (with added subhrly components). - Fixed problems with bug39a.inp run (1 mass surf, const temps). - 12-5-94. */ - zn_xqHr = - zn_bLdHr * tz - zn_aqLdHr; // member only cuz saved to -Pr in loadsSubhr. - - // check/warn alternate model values - // However: don't correct value -- consumers use max(), min() as approp. - // (Changed value can persist due to expression eval optimization.) - if (i.znQMxH < -0.01f) - orWarn("znQMxH (%0.f) taken as 0 (s/b >= 0)", i.znQMxH); - if (i.znQMxC > 0.01f) - orWarn("znQMxC (%0.f) taken as 0 (s/b <= 0)", i.znQMxC); - - /* hourly-only load change checks: - zn_xqHr: Hourly parts of b * t - a, just above. - znLGain: latent gain rate (set per GAINs by cnguts.cpp:doHourgains, used - in cnZtu.cpp:ZNR::znW). Note: for subsequent subhrs, wz change sets ztuCf - in loadsSubhr. -Pr's used here are updated in loadsSubhr, every time ztuCf - is on. */ - // zn_aqLdHr and zn_bLdHr are covered by zn_xqHr check & no longer checked - // separately 12-94. - // Removing aq- and zn_bLdHr checks made NO difference in test suites 12-94 - if ( - // .02 found better than .05,.1,.2 in bug39a.inp run (1 mass surf, const - // temps) (12-94): - RELCHANGE(zn_xqHr, zn_xqHrPr) > - .02 * Top.relTol * - MARG1 // if hourly part of difference b*t-aq (for loads) changed - || ABSCHANGE(znLGain, - znLGainPr) // if zone moisture GAIN rate (Btuh) changed: - > PsyHCondWtr * - Top.absHumTol) // test abs lb/hr change after conversion - { - ztuCf++; // say zone load changed: must do ztuCompute. - // ztuCf causes -Pr's to be updated. - } - return RCOK; -} // ZNR::zn_BegHour2 +RC ZNR::zn_BegHour2() // beginning-of-hour calcs for zone +{ + zn_SetAirRadXArea(); // update air temp/relHum dependent radiant exchange values + // for convective/radiant zone model; + // small impact, not worth substep cost + + // hour's sensible internal gain: scheduled value, if any. + // Used for znaqLdHr, zp->qsIg, and to apportion to subhr results. + qsIgHr = znSGain; // sensible gain totalled from GAIN records + // also: qrIgAir: NOT included here0 + + // Non-hvac hour-constant portions of MAIN EQUATION "Tz = (a + q)/b" (story at start file) + + // sum ua*t for specified-temp-exposure surfaces + XSRAT* xs /*= NULL*/; + zn_uaXSpecT = 0.; + for (TI xi = xsSpecT1; xi != 0; xi = xs->nxXsSpecT) // these xsurfs have own chain. loop over them. + { xs = XsB.p + xi; // access XSRAT record xi + zn_uaXSpecT += xs->x.xs_uval * xs->x.xs_area * xs->x.sfExT; // u * a * outside temp (hourly vbl) + /*>>>> there are 2 hourly and 1 subhourly use of uval*area --> otta precompute it, + maybe also use in cnguts re ua -SpecT. FLOAT gud enuf for the latter? rob 3-91. */ + } + + // zn_aqLdHr: hour-constant non-hvac a's and q's for numerator. a = sigma(UAi*Ti) + Tzold*hc/t. + zn_aqLdHr = + aMassHr // hourly masses UAT (from loadsSurfaces call just above) + + qsIgHr // q's: sched sens internal gain from GAINS (znSGAIN) & wthr file. + + qrIgAir // q's: radiant internal gain to zone CAir (light surfaces) 11-95. + + zn_uaXSpecT; // uaT total from just above + // all other components (infil etc) done subhourly + + // zn_bLdHr: hour-constant non-hvac b's for denominator. b = sigma(UA) + hc/t. + zn_bLdHr = zn_bcon; // just the run-const UAs + + /* zn_xqHr: q = b * t - a using only hourly components, for change tests (just below) only: + Intended to be appropriately sensitive to changes when b*a almost equal to a, + eg when aMassHr settling & all else constant, + cuz terminal loads are of form b * t - a (with added subhrly components). + Fixed problems with bug39a.inp run (1 mass surf, const temps). 12-5-94. */ + zn_xqHr = zn_bLdHr * tz - zn_aqLdHr; // member only cuz saved to -Pr in loadsSubhr. + + // check/warn alternate model values + // However: don't correct value -- consumers use max(), min() as approp. + // (Changed value can persist due to expression eval optimization.) + if (i.znQMxH < -0.01f) + orWarn( "znQMxH (%0.f) taken as 0 (s/b >= 0)", i.znQMxH); + if (i.znQMxC > 0.01f) + orWarn( "znQMxC (%0.f) taken as 0 (s/b <= 0)", i.znQMxC); + + /* hourly-only load change checks: + zn_xqHr: Hourly parts of b * t - a, just above. + znLGain: latent gain rate (set per GAINs by cnguts.cpp:doHourgains, used in cnZtu.cpp:ZNR::znW). + Note: for subsequent subhrs, wz change sets ztuCf in loadsSubhr. + -Pr's used here are updated in loadsSubhr, every time ztuCf is on. */ + // zn_aqLdHr and zn_bLdHr are covered by zn_xqHr check & no longer checked separately 12-94. + // Removing aq- and zn_bLdHr checks made NO difference in test suites 12-94 + if ( + // .02 found better than .05,.1,.2 in bug39a.inp run (1 mass surf, const temps) (12-94): + RELCHANGE( zn_xqHr, zn_xqHrPr) > .02*Top.relTol*MARG1 // if hourly part of difference b*t-aq (for loads) changed + || ABSCHANGE( znLGain, znLGainPr) // if zone moisture GAIN rate (Btuh) changed: + > PsyHCondWtr*Top.absHumTol ) // test abs lb/hr change after conversion + { + ztuCf++; // say zone load changed: must do ztuCompute. + // ztuCf causes -Pr's to be updated. + } + return RCOK; +} // ZNR::zn_BegHour2 //----------------------------------------------------------------------------- -RC ZNR::zn_BegSubhr1() // zone start of subhour, part 1 +RC ZNR::zn_BegSubhr1() // zone start of subhour, part 1 // call *before* airnet and possible others needing prior-step (lagged) values { - memset(&ZnresB.p[ss].curr.S, 0, - sizeof(ZNRES_IVL_SUB)); // access subhr results struct in ZNRES via - // subscr in ZrB & 0 it. - ZnresB.p[ss].curr.S.nSubhr = - 1L; // count subhours by setting to 1 at subhr level & accumulating + memset( &ZnresB.p[ss].curr.S, 0, sizeof( ZNRES_IVL_SUB) ); // access subhr results struct in ZNRES via subscr in ZrB & 0 it. + ZnresB.p[ss].curr.S.nSubhr = 1L; // count subhours by setting to 1 at subhr level & accumulating - // zone-specific wind velocity pressure (re AirNet) - // zn_windFLkg defaults from zn_eaveZ and zn_infShld - // else is input (subhour variability) - zn_windPresV = Top.tp_WindPresV(i.zn_windFLkg * Top.windSpeedSh); + // zone-specific wind velocity pressure (re AirNet) + // zn_windFLkg defaults from zn_eaveZ and zn_infShld + // else is input (subhour variability) + zn_windPresV = Top.tp_WindPresV( i.zn_windFLkg * Top.windSpeedSh); - if (!nMd) // if zone has no modes (eg startup with no terminals) - spCf++; // tell ztuCompute to build zone mode sequence + if (!nMd) // if zone has no modes (eg startup with no terminals) + spCf++; // tell ztuCompute to build zone mode sequence - // initialize DHW heat transfer totals - // TODO: could be done in DHW code iff needed - // Win? -- generally less DHWHEATERs than ZONEs - // OTOH, this is foolproof and simple, 2-16 - zn_qDHWLoss = zn_qDHWLossAir = zn_qDHWLossRad = zn_qHPWH = zn_hpwhAirX = 0.; + // initialize DHW heat transfer totals + // TODO: could be done in DHW code iff needed + // Win? -- generally less DHWHEATERs than ZONEs + // OTOH, this is foolproof and simple, 2-16 + zn_qDHWLoss = zn_qDHWLossAir = zn_qDHWLossRad = zn_qHPWH = zn_hpwhAirX = 0.; - return RCOK; -} // ZNR::zn_BegSubhr1 + return RCOK; +} // ZNR::zn_BegSubhr1 //----------------------------------------------------------------------------- -RC ZNR::zn_BegSubhr2() // zone start of subhour, part 2 +RC ZNR::zn_BegSubhr2() // zone start of subhour, part 2 // call *after* lagged values used { - // forced convection coeff, Btuh/ft2-F; re C_CONVMODELCH_UNIFIED - // based on lagged value of zone total air change rate - // combined with surface-specific sb_hcNat, see SBC::sb_SetCoeffs() - // must be done *after* DHW calcs re HPWH air motion - zn_hcFrc = Top.tp_hConvF * i.zn_hcFrcF * - pow(max(zn_hcAirXls + zn_hpwhAirX, 0.f), 0.8f); - zn_airNetI[0].af_Init(); - zn_airNetI[1].af_Init(); - - zn_qIzXAnSh = 0.; // subhour non-airnet xfers - zn_qIzSh = 0.; // total - - qMsSg = 0.f; // zone's mass solar and radiant internal gains. Accum'd during - // mass calcs - qrIgMs = 0.f; - - znXLGain = 0.; // sensible gain due to excess latent "condensation" - - // heat balance terms for convective/radiant model - // later accum additional from mass, airnet, etc - zn_nAirSh = - zn_qDuctCondAir // duct conduction: lagged value from end of prior step - + zn_qDHWLossAir // DHW losses into zone: current step - + zn_qHPWH // DHW heat pump water heater extraction: current step - + zn_sysDepAirIls - .af_AmfCpT(); // prior step system-dependent - // air flow into zone (duct leaks, OAV relief,) - zn_dAirSh = zn_sysDepAirIls.af_AmfCp(); - - zn_nRadSh = zn_qDuctCondRad // duct conduction: lagged value - + zn_qDHWLossRad; // DHW losses into zone - zn_dRadSh = 0.; - zn_cxSh = 0.; - - // duct conduction - // save total for ZNRES balance - // init air and rad re cur step accum if system runs - zn_qDuctCond = zn_qDuctCondAir + zn_qDuctCondRad; - zn_qDuctCondAir = zn_qDuctCondRad = 0.; - - zn_ductLkI.af_Init(); // air flows in / out - zn_ductLkO.af_Init(); - zn_sysAirI.af_Init(); - zn_sysAirO.af_Init(); - zn_OAVRlfO.af_Init(); - zn_rsAmfSup = zn_rsAmfRet = 0.; - - // NO! CNE zone model can skip step calc and use prior result - // initialization done later for CZM zone model - // zn_qsHvac = 0.; - // zn_qlHvac = 0.; - - zn_fVentPrf = zn_fVent = 0.f; // vent fraction - // = actual / possible vent flow - - return RCOK; -} // ZNR::zn_BegSubhr2 + // forced convection coeff, Btuh/ft2-F; re C_CONVMODELCH_UNIFIED + // based on lagged value of zone total air change rate + // combined with surface-specific sb_hcNat, see SBC::sb_SetCoeffs() + // must be done *after* DHW calcs re HPWH air motion + zn_hcFrc = Top.tp_hConvF * i.zn_hcFrcF * pow( max( zn_hcAirXls + zn_hpwhAirX, 0.f), 0.8f); + zn_airNetI[ 0].af_Init(); + zn_airNetI[ 1].af_Init(); + + zn_qIzXAnSh = 0.; // subhour non-airnet xfers + zn_qIzSh = 0.; // total + + qMsSg = 0.f; // zone's mass solar and radiant internal gains. Accum'd during mass calcs + qrIgMs = 0.f; + + znXLGain = 0.; // sensible gain due to excess latent "condensation" + + // heat balance terms for convective/radiant model + // later accum additional from mass, airnet, etc + zn_nAirSh = zn_qDuctCondAir // duct conduction: lagged value from end of prior step + + zn_qDHWLossAir // DHW losses into zone: current step + + zn_qHPWH // DHW heat pump water heater extraction: current step + + zn_sysDepAirIls.af_AmfCpT(); // prior step system-dependent + // air flow into zone (duct leaks, OAV relief,) + zn_dAirSh = zn_sysDepAirIls.af_AmfCp(); + + zn_nRadSh = zn_qDuctCondRad // duct conduction: lagged value + + zn_qDHWLossRad; // DHW losses into zone + zn_dRadSh = 0.; + zn_cxSh = 0.; + + // duct conduction + // save total for ZNRES balance + // init air and rad re cur step accum if system runs + zn_qDuctCond = zn_qDuctCondAir + zn_qDuctCondRad; + zn_qDuctCondAir = zn_qDuctCondRad = 0.; + + zn_ductLkI.af_Init(); // air flows in / out + zn_ductLkO.af_Init(); + zn_sysAirI.af_Init(); + zn_sysAirO.af_Init(); + zn_OAVRlfO.af_Init(); + zn_rsAmfSup = zn_rsAmfRet = 0.; + + // NO! CNE zone model can skip step calc and use prior result + // initialization done later for CZM zone model + // zn_qsHvac = 0.; + // zn_qlHvac = 0.; + + zn_fVentPrf = zn_fVent = 0.f; // vent fraction + // = actual / possible vent flow + + return RCOK; +} // ZNR::zn_BegSubhr2 //============================================================================ -RC FC loadsSubhr() // loads stuff for all zones for subhour. Call BEFORE hvac - // code. +RC FC loadsSubhr() // loads stuff for all zones for subhour. Call BEFORE hvac code. // Assumes zone subhour results pre-0'd: done in zn_BegSubhr { - RC rc = RCOK; - ZNR *zp; - - /* CALCULATE SUBHOURLY SOLAR GAINS - Set subhourly "targets" in zones and masses to their solar gains for - subhour, calculated by combining subhour's weather data and control variable - values (window shade positions) with the precalculated (cgsolar.cpp) factors - in the entries in the (currently selected) Solar Gain Rat. The factors - already reflect the size, orientation, and absorptivity of each XSURF and - the sun's position this month. cnrecs.def/rccn.h structures. */ - SGRAT *sge; + RC rc = RCOK; + ZNR *zp; + + /* CALCULATE SUBHOURLY SOLAR GAINS + Set subhourly "targets" in zones and masses to their solar gains for subhour, calculated by combining + subhour's weather data and control variable values (window shade positions) with the + precalculated (cgsolar.cpp) factors in the entries in the (currently selected) Solar Gain Rat. + The factors already reflect the size, orientation, and absorptivity of each XSURF and the + sun's position this month. cnrecs.def/rccn.h structures. */ + SGRAT* sge; #if 1 - RLUP(SgR, sge) // loop SgR records, setting sge to point at each - { - if (sge->sg_isSubhrly) // hourly sg's done elsewhere -- in loadsHourBeg - sge->sg_ToTarg(Top.radBeamShAv, Top.radDiffShAv); - } + RLUP( SgR, sge) // loop SgR records, setting sge to point at each + { if (sge->sg_isSubhrly) // hourly sg's done elsewhere -- in loadsHourBeg + sge->sg_ToTarg( Top.radBeamShAv, Top.radDiffShAv); + } #else - x SI sunup = Top.radDiffShAv > 0.F || - Top.radBeamShAv > 0.F; // 0 if no sun this subhour - x RLUP(SgR, sge) // loop SgR records, setting sge to point at each - x { - if (!sge->sg_isSubhrly) // hourly sg's done elsewhere -- in loadsHourBeg - x continue; - x if (!sunup) // if no sun this hour, just 0 all subhourly gains - x *sge->sg_targ = - 0.f; // zero the target. testing sge->addIt unnecessary. - x else x { - x #ifdef SOLAVNEND // undef in cndefns.h (via cnglob.h) 1-18-94: only if - // computing & using end-ivl as well as ivl avg solar - // values - xo float gain = // gain consists of diffuse & - // hour's beam values - xo sge->isEndIvl // TRUE to use end-subhr not subhr-avg, 1-95 - xo - ? Top.radDiffSh * sge->diff[Top.iHrST] // combine weather data - xo + - Top.radBeamSh * - sge->beam[Top.iHrST] // with this sge's gain factors - xo - : Top.radDiffShAv * sge->diff[Top.iHrST] xo + - Top.radBeamShAv * sge->beam[Top.iHrST]; - x #else x #if 1 x float gain = - Top.radDiffShAv * - sge->sg_diff[Top.iHrST] // gain consists of subhour average - // weather data values - x + - Top.radBeamShAv * sge->sg_beam[Top.iHrST]; // ... combined with hour's - // gain factors in sge - x #else // temp backtrack 2-95, undone - xx float gain = - Top.radDiffShAv * - sge->diff[Top.iHr] // gain consists of subhour average weather - // data values - xx + - Top.radBeamShAv * sge->beam[Top.iHr]; // ... combined with hour's - // gain factors in sge - x #endif x #endif x // (gain fixed for diff[24], standard time 2-95) - x if (sge->sg_control) // if sge has pointer to control factor - x gain *= - *sge->sg_control; // apply control. eg fraction zone shades closed. - x if (sge->sg_addIt == 0) // if first sge for target - x *sge->sg_targ = gain; // initialize by storing not adding gain - x else x *sge->sg_targ += - gain; // accumulate additional gains into target by adding - x x /* *sge->targ is ZrB.p[i].qSgAir or .qSgTotSh, or subhrly - MsR.p[i].outside.sg or .inside.sg, x as targeted - in cgsolar.cpp:sgrGet(). */ - x // note: addIt != 0 probably now 3-90 corresponds to control != - // NULL, but we don't depend on that assumption. - x - } - x - } -#endif - - // SIMULATE SUBHOURLY SURFACES - // Must follow (any future) subhouly solar gain determination, precede zone - // loop. Uses Top.tp_subhrDur. - loadsSurfaces(TRUE); // below. TRUE = do subhourly (not hourly masses) - - TMRSTART(TMR_ZONE); - - // COMPUTE INTERZONE TRANSFERS and base (infil only) airnet - CSE_E(loadsIzxSh1()) - - bool bDbPrint = DbDo(dbdZM); -#if defined(_DEBUG) - [[maybe_unused]] const ZNR *zp1 = ZrB.GetAtSafe(1); +x SI sunup = Top.radDiffShAv > 0.F || Top.radBeamShAv > 0.F; // 0 if no sun this subhour +x RLUP( SgR, sge) // loop SgR records, setting sge to point at each +x { if (!sge->sg_isSubhrly) // hourly sg's done elsewhere -- in loadsHourBeg +x continue; +x if (!sunup) // if no sun this hour, just 0 all subhourly gains +x *sge->sg_targ = 0.f; // zero the target. testing sge->addIt unnecessary. +x else +x { +x#ifdef SOLAVNEND // undef in cndefns.h (via cnglob.h) 1-18-94: only if computing & using end-ivl as well as ivl avg solar values +xo float gain = // gain consists of diffuse & hour's beam values +xo sge->isEndIvl // TRUE to use end-subhr not subhr-avg, 1-95 +xo ? Top.radDiffSh * sge->diff[Top.iHrST] // combine weather data +xo + Top.radBeamSh * sge->beam[Top.iHrST] // with this sge's gain factors +xo : Top.radDiffShAv * sge->diff[Top.iHrST] +xo + Top.radBeamShAv * sge->beam[Top.iHrST]; +x#else +x#if 1 +x float gain = Top.radDiffShAv * sge->sg_diff[Top.iHrST] // gain consists of subhour average weather data values +x + Top.radBeamShAv * sge->sg_beam[Top.iHrST]; // ... combined with hour's gain factors in sge +x#else // temp backtrack 2-95, undone +xx float gain = Top.radDiffShAv * sge->diff[Top.iHr] // gain consists of subhour average weather data values +xx + Top.radBeamShAv * sge->beam[Top.iHr]; // ... combined with hour's gain factors in sge +x#endif +x#endif +x // (gain fixed for diff[24], standard time 2-95) +x if (sge->sg_control) // if sge has pointer to control factor +x gain *= *sge->sg_control; // apply control. eg fraction zone shades closed. +x if (sge->sg_addIt==0) // if first sge for target +x *sge->sg_targ = gain; // initialize by storing not adding gain +x else +x *sge->sg_targ += gain; // accumulate additional gains into target by adding +x +x /* *sge->targ is ZrB.p[i].qSgAir or .qSgTotSh, or subhrly MsR.p[i].outside.sg or .inside.sg, +x as targeted in cgsolar.cpp:sgrGet(). */ +x // note: addIt != 0 probably now 3-90 corresponds to control != NULL, but we don't depend on that assumption. +x } +x } +#endif + +// SIMULATE SUBHOURLY SURFACES +// Must follow (any future) subhouly solar gain determination, precede zone loop. Uses Top.tp_subhrDur. + loadsSurfaces( TRUE); // below. TRUE = do subhourly (not hourly masses) + + TMRSTART( TMR_ZONE); + +// COMPUTE INTERZONE TRANSFERS and base (infil only) airnet + CSE_E( loadsIzxSh1() ) + + bool bDbPrint = DbDo( dbdZM); +#if defined( _DEBUG) + [[maybe_unused]] const ZNR* zp1 = ZrB.GetAtSafe( 1); #endif - // GLOBAL LOAD CHANGE CHECK: outdoor humidity - // change changes all zone loads, but does not show in a,b. - BOO azCf = Top.tp_wOShChange; + // GLOBAL LOAD CHANGE CHECK: outdoor humidity + // change changes all zone loads, but does not show in a,b. + BOO azCf = Top.tp_wOShChange; - RLUP(ZrB, zp) { - rc |= zp->zn_InitSubhr(); // final zone init for loads calc - // sets values for all models -#if defined(CRTERMAH) - rc |= zp->zn_SetZtuCfIf(azCf); + RLUP(ZrB, zp) + { + rc |= zp->zn_InitSubhr(); // final zone init for loads calc + // sets values for all models +#if defined( CRTERMAH) + rc |= zp->zn_SetZtuCfIf(azCf); #endif - } - if (!Top.tp_bAllCR) { // all CNE zones (all CR or all CNE enforced) -#if !defined(CRTERMAH) - RLUP(ZrB, zp) - rc |= zp->zn_LoadsSubhr(azCf); // non-CR stuff - // change-check subhour zone loads, etc. + } + + if (!Top.tp_bAllCR) + { // all CNE zones (all CR or all CNE enforced) +#if !defined( CRTERMAH) + RLUP( ZrB, zp) + rc |= zp->zn_LoadsSubhr( azCf); // non-CR stuff + // change-check subhour zone loads, etc. #else - // may extrapolate tz if load unchanged - // subhour terminal/airhandler hvac - rc |= hvacIterSubhr(); - RLUP(ZrB, zp) { - zp->zn_MapTerminalResults(); - zp->zn_AirXMoistureBal(true); - } + // may extrapolate tz if load unchanged + // subhour terminal/airhandler hvac + rc |= hvacIterSubhr(); + RLUP(ZrB, zp) + { + zp->zn_MapTerminalResults(); + zp->zn_AirXMoistureBal(true); + } #endif - return rc; - } + return rc; + } - // current floating temp of non-terminal zones - RLUPC(ZrB, zp, !zp->zn_HasTerminal()) - zp->zn_TAirFloatCR(); + // current floating temp of non-terminal zones + RLUPC( ZrB, zp, !zp->zn_HasTerminal()) + zp->zn_TAirFloatCR(); - // convective-radiant model multizone vent control + // convective-radiant model multizone vent control - // TODO ideas - // * Overall vent available flag Top.? (allows scheduling vent) - // * Better estimate of available vent supply temp. - // Use prior step? (run vent at substep 0 even if not needed?) + // TODO ideas + // * Overall vent available flag Top.? (allows scheduling vent) + // * Better estimate of available vent supply temp. + // Use prior step? (run vent at substep 0 even if not needed?) -#if 0 && defined(_DEBUG) +#if 0 && defined( _DEBUG) bool bReportVent = Top.jDay == 178 /* && !Top.isWarmup */; #endif - int ventAvail = Top.tp_GetVentAvail(); // overall vent availability + int ventAvail = Top.tp_GetVentAvail(); // overall vent availability - Top.tp_fVent = 0.f; // consensus whole building vent fraction (if not RSYSOAV) - // = fraction of full vent flow to use - // determined by zone that wants the least vent - // or -1 for zonal vent control + Top.tp_fVent = 0.f; // consensus whole building vent fraction (if not RSYSOAV) + // = fraction of full vent flow to use + // determined by zone that wants the least vent + // or -1 for zonal vent control - if (ventAvail == C_VENTAVAILVC_WHOLEBLDG || - ventAvail == C_VENTAVAILVC_ZONAL) { - int nVentUseful = - 0; // ventilation usefulness - // >0: vent potentially useful for 1 or more zones - // =0: vent not useful - // <0: vent forbidden by conditions in 1 or more zones - // e.g. heating or cooling required - RLUPC(ZrB, zp, !zp->zn_HasTerminal()) { - int nVU1 = zp->zn_AssessVentUtility(); + if (ventAvail == C_VENTAVAILVC_WHOLEBLDG || ventAvail == C_VENTAVAILVC_ZONAL) + { + int nVentUseful = 0; // ventilation usefulness + // >0: vent potentially useful for 1 or more zones + // =0: vent not useful + // <0: vent forbidden by conditions in 1 or more zones + // e.g. heating or cooling required + RLUPC(ZrB, zp, !zp->zn_HasTerminal()) + { + int nVU1 = zp->zn_AssessVentUtility(); #if 0 && defined(_DEBUG) if (bReportVent) { // printf("\n%s Hr=%d, Zone '%s': VU=%d", Top.dateStr.CStr(), Top.iHr, zp->Name(), nVU1); @@ -657,76 +604,85 @@ RC FC loadsSubhr() // loads stuff for all zones for subhour. Call BEFORE hvac zp->zn_AssessVentUtility(); // call again for debug } // break NO: zn_AssessVentUtility() needed for all zones -#endif - if (nVU1 > 0 || ventAvail != C_VENTAVAILVC_ZONAL) - nVentUseful += nVU1; - } - - int bAllNoVent = 1; - if (nVentUseful <= 0) { - if (Top.tp_pAirNet) - Top.tp_pAirNet->an_ClearResults(1); - } else { // vent might be useful for at least 1 zone - loadsIzxSh2(); // AirNet "vents open" - Top.tp_fVent = 1.f; // try full vent and limit - RLUP(ZrB, zp) { - if (!zp->zn_IsUZ()) { - int vRet = zp->zn_FVentCR(); - if (ventAvail == C_VENTAVAILVC_WHOLEBLDG) { // whole building control - // if vent bad for any zone, all off - // else vent fract = minimum preferred - if (vRet < 0) { - Top.tp_fVent = 0.f; // vent is harmful for this zone: off for all - break; - } else if (vRet > 0) { - if (zp->zn_fVentPrf < Top.tp_fVent) - Top.tp_fVent = zp->zn_fVentPrf; - bAllNoVent = 0; - } - // else vRet == 0 - // this zone does not care, do nothing - } else { // zonal control: each zone gets preferred - zp->zn_fVent = zp->zn_fVentPrf; - } - } - } - } - if (bAllNoVent) - Top.tp_fVent = 0.f; // whole-building vent fraction +#endif + if (nVU1 > 0 || ventAvail != C_VENTAVAILVC_ZONAL) + nVentUseful += nVU1; + } -#if 0 && defined(_DEBUG) + int bAllNoVent = 1; + if (nVentUseful <= 0) + { if (Top.tp_pAirNet) + Top.tp_pAirNet->an_ClearResults(1); + } + else + { // vent might be useful for at least 1 zone + loadsIzxSh2(); // AirNet "vents open" + Top.tp_fVent = 1.f; // try full vent and limit + RLUP(ZrB, zp) + { + if (!zp->zn_IsUZ()) + { + int vRet = zp->zn_FVentCR(); + if (ventAvail == C_VENTAVAILVC_WHOLEBLDG) + { // whole building control + // if vent bad for any zone, all off + // else vent fract = minimum preferred + if (vRet < 0) + { Top.tp_fVent = 0.f; // vent is harmful for this zone: off for all + break; + } + else if (vRet > 0) + { if (zp->zn_fVentPrf < Top.tp_fVent) + Top.tp_fVent = zp->zn_fVentPrf; + bAllNoVent = 0; + } + // else vRet == 0 + // this zone does not care, do nothing + } + else + { // zonal control: each zone gets preferred + zp->zn_fVent = zp->zn_fVentPrf; + } + } + } + } + if (bAllNoVent) + Top.tp_fVent = 0.f; // whole-building vent fraction + +#if 0 && defined( _DEBUG) if (bReportVent && !Top.isWarmup) printf("\n%s Hr=%d: NVU=%d ANV=%d TFvnt=%0.3f", Top.dateStr.CStr(), Top.iHr, nVentUseful, bAllNoVent, Top.tp_fVent); #endif - } -#if defined(DEBUGDUMP) - if (bDbPrint) - DbPrintf("\n"); + } + +#if defined( DEBUGDUMP) + if (bDbPrint) + DbPrintf( "\n"); #endif - RSYS *rs; - if (ventAvail == C_VENTAVAILVC_RSYSOAV) { // attempt OAV for each system - // if successful, rs_mode = (zones)->zn_hcMode = rsmOAV (prevents other RSYS - // modeling) fVent = 0 if here - RLUP(RsR, rs) - rs->rs_OAVAttempt(); // ignore return (zn_hcMode / rs_mode retain outcome) - } + RSYS* rs; + if (ventAvail == C_VENTAVAILVC_RSYSOAV) + { // attempt OAV for each system + // if successful, rs_mode = (zones)->zn_hcMode = rsmOAV (prevents other RSYS modeling) + // fVent = 0 if here + RLUP( RsR, rs) + rs->rs_OAVAttempt(); // ignore return (zn_hcMode / rs_mode retain outcome) + } - RLUP(ZrB, zp) - rc |= zp->zn_CondixCR( - ventAvail); // duct leakage can add cross-zone air - // do all zones before zn_AirXMoistureBal() etc. + RLUP( ZrB, zp) + rc |= zp->zn_CondixCR( ventAvail); // duct leakage can add cross-zone air + // do all zones before zn_AirXMoistureBal() etc. - RLUP(RsR, rs) - rc |= rs->rs_AllocateZoneAir(); // NOP if OAV + RLUP( RsR, rs) + rc |= rs->rs_AllocateZoneAir(); // NOP if OAV -#if defined(CRTERMAH) - // may extrapolate tz if load unchanged - // subhour terminal/airhandler hvac - rc |= hvacIterSubhr(); +#if defined( CRTERMAH) +// may extrapolate tz if load unchanged +// subhour terminal/airhandler hvac + rc |= hvacIterSubhr(); -#if 0 && defined(_DEBUG) +#if 0 && defined( _DEBUG) if (Top.jDay == 1) { if (zp1 && abs( zp1->zn_qsHvac) < 0.01) @@ -735,72 +691,75 @@ RC FC loadsSubhr() // loads stuff for all zones for subhour. Call BEFORE hvac } #endif -#endif // CRTERMAH +#endif // CRTERMAH - RLUP(ZrB, zp) - rc |= zp->zn_CondixCR2(); // finalize zone for all modes (including OAV) + RLUP( ZrB, zp) + rc |= zp->zn_CondixCR2(); // finalize zone for all modes (including OAV) - RLUP(RsR, rs) - rc |= rs->rs_FinalizeSh(); + RLUP( RsR, rs) + rc |= rs->rs_FinalizeSh(); - RLUP(ZrB, zp) { - if (zp->zn_IsConvRad()) - zp->zn_AirXMoistureBal(true); - rc |= zp->zn_ComfortCR(); -#if !defined(CRTERMAH) - rc |= zp->zn_LoadsSubhr(azCf); // insurance, may not be needed for CR + RLUP( ZrB, zp) + { + if (zp->zn_IsConvRad()) + zp->zn_AirXMoistureBal(true); + rc |= zp->zn_ComfortCR(); +#if !defined( CRTERMAH) + rc |= zp->zn_LoadsSubhr( azCf); // insurance, may not be needed for CR #endif -#if defined(DEBUGDUMP) - if (bDbPrint) - zp->zn_DbDump(); +#if defined( DEBUGDUMP) + if (bDbPrint) + zp->zn_DbDump(); #endif - } + } -#if defined(DEBUGDUMP) - if (DbDo(dbdAIRNET | dbdIZ) && ZrB.GetCount() > 0) { - DbPrintf("\nZone airnet results\n" - "Zone pz0W[0] pz0W[1] pz0 qIzSh " - "AmfCp AmfCpT\n"); - RLUP(ZrB, zp) - DbPrintf("%-20.20s %8.5f %8.5f %8.5f %10.2f %8.2f %10.1f\n", zp->Name(), - zp->zn_pz0W[0], zp->zn_pz0W[1], zp->zn_pz0, zp->zn_qIzSh, - zp->zn_AnAmfCp(0), zp->zn_AnAmfCpT(0)); - } +#if defined( DEBUGDUMP) + if (DbDo(dbdAIRNET | dbdIZ) && ZrB.GetCount() > 0) + { DbPrintf("\nZone airnet results\n" + "Zone pz0W[0] pz0W[1] pz0 qIzSh AmfCp AmfCpT\n"); + RLUP(ZrB, zp) + DbPrintf("%-20.20s %8.5f %8.5f %8.5f %10.2f %8.2f %10.1f\n", + zp->Name(), zp->zn_pz0W[0], zp->zn_pz0W[1], zp->zn_pz0, zp->zn_qIzSh, + zp->zn_AnAmfCp(0), zp->zn_AnAmfCpT(0)); + } #endif - TMRSTOP(TMR_ZONE); + TMRSTOP( TMR_ZONE); - return rc; -} // loadsSubhr + return rc; +} // loadsSubhr //---------------------------------------------------------------------------- -RC ZNR::zn_InitSubhr() { - // derive working setpoints. - // done unconditionally altho not always used. - if (Top.tp_autoSizing) { // avoid setpoint step changes when autosizing - // assume zn_tzspXbs changes hourly (altho they have subhourly variability) - // ramp from prior value over the hour - float f = float(Top.iSubhr + 1) / Top.tp_nSubSteps; - zn_tzspH = (1.f - f) * zn_tzspHlh + f * i.znTH; - zn_tzspD = (1.f - f) * zn_tzspDlh + f * i.znTD; - zn_tzspC = (1.f - f) * zn_tzspClh + f * i.znTC; - } else { // not autosizing: use setpoints as input - zn_tzspH = i.znTH; - zn_tzspD = i.znTD; - zn_tzspC = i.znTC; - } - - if (zn_UsesZoneSetpoints() && zn_tzspH >= zn_tzspC) - orer("Impossible setpoints -- znTH (%0.2f) >= znTC (%.2f)", zn_tzspH, - zn_tzspC); - - // subhr's Infil UA (Btuh/F) -#if 1 // 4-17-2013 - // infiltration UA based on AMF of *zone* air - // UA (Btuh/F) = dry amf (lbm/hr) * spec ht (Btu/lbmDry-F) - // (could derive spec ht from current humrat, but effect is minor) - // (formerly based on outdoor air state, 4-17-2013) - zn_uaInfil = zn_NonAnIVAmf() * Top.tp_airSH; -#if 0 && defined(_DEBUG) +RC ZNR::zn_InitSubhr() +{ + // derive working setpoints. + // done unconditionally altho not always used. + if (Top.tp_autoSizing) + { // avoid setpoint step changes when autosizing + // assume zn_tzspXbs changes hourly (altho they have subhourly variability) + // ramp from prior value over the hour + float f = float(Top.iSubhr + 1) / Top.tp_nSubSteps; + zn_tzspH = (1.f - f) * zn_tzspHlh + f * i.znTH; + zn_tzspD = (1.f - f) * zn_tzspDlh + f * i.znTD; + zn_tzspC = (1.f - f) * zn_tzspClh + f * i.znTC; + } + else + { // not autosizing: use setpoints as input + zn_tzspH = i.znTH; + zn_tzspD = i.znTD; + zn_tzspC = i.znTC; + } + + if (zn_UsesZoneSetpoints() && zn_tzspH >= zn_tzspC) + orer("Impossible setpoints -- znTH (%0.2f) >= znTC (%.2f)", zn_tzspH, zn_tzspC); + + // subhr's Infil UA (Btuh/F) +#if 1 // 4-17-2013 + // infiltration UA based on AMF of *zone* air + // UA (Btuh/F) = dry amf (lbm/hr) * spec ht (Btu/lbmDry-F) + // (could derive spec ht from current humrat, but effect is minor) + // (formerly based on outdoor air state, 4-17-2013) + zn_uaInfil = zn_NonAnIVAmf() * Top.tp_airSH; +#if 0 && defined( _DEBUG) float uaInfilX = Top.tp_airxOSh // Btuh/cfm-F air flow heat xfer at current outdoor temp * ( i.znVol // air changes/hr part: zone volume (ft3), times... * i.infAC / 60. // user input air changes per hour, hourly variable, /60 for per minute. @@ -813,246 +772,225 @@ RC ZNR::zn_InitSubhr() { printf( "uaInfil mismatch!\n"); #endif #else - zn_uaInfil = - Top.tp_airxOSh // Btuh/cfm-F air flow heat xfer at current outdoor temp - * - (i.znVol // air changes/hr part: zone volume (ft3), times... - * i.infAC / 60. // user input air changes per hour, hourly variable, - // /60 for per minute. - + - i.infELA // ELA part: user input effective leakage area (ft2), hourly - // variable - * sqrt( // flow is proportional to square root of - zn_stackc * - fabs(tzlh - Top.tDbOSh) + // stack coeff (see - // ZNR.zn_InfilSetup) * delta t - zn_windc * Top.windSpeedSquaredSh) // wind coeff (ditto) * - // wind speed ^ 2 - ); -#endif - - // for hvac code, compute a+q, b contributions this subhour for everything but - // hvac, for use in "Tz = (a + q)/b" a+q = sigma(UAi*Ti) + other q's + - // Told*hc/t - zn_qSgAir = zn_sgAirTarg.st_tot; // solar gain to air - zn_aqLdSh = - zn_aqLdHr // hour-constant part: intl gain, solar gain, cond to ambient & - // specT's, etc. - + aMassSh // subhourly masses UAT (from loadsSurfaces) - + Top.tDbOSh * (zn_ua + zn_uaInfil) // temp * (constUA + infilUA) - + zn_qSgAir // q's: solar gain (power, Btuh) to zone (air) - + zn_qIzSh // interzone transfers to zone (q's), just set by loadsIzxSubhr - // (included zn_qIzXAnSh + zn_anAmfCp[ 0]*deltaT) - + tzls * i.znCAirSh // Told*hc/t - + znXLGainLs; // excess latent gain last subhr === heat of condensation. - // 5-97. - - // b = sigma(UAi) + hc/t - zn_bLdSh = zn_bLdHr // non-hvac UA's constant for hour - + zn_uaInfil // + infil UA this subhr - + i.znCAirSh; // + hc/t: .znCAirSh is hc. - // "cair" is heat capac of everything in zone not modelled as "mass": - // user const & per-ft2 parts for air, furniture, walls, etc). - -#if !defined(CRTERMAH) - /* zn_xqSh: q = b * t - a using components avail here, for change tests only: - Intended to be appropriately sensitive to changes when b*a almost - equal to a, since terminal loads are of form b * t - a. Insurance 12-94 -- - need was not proven at subhour level when all masses hourly (b4 aMassSh - added). But it did something: eliminated undercooling in hour 13 in S1LOH - test run, 12-5-94, so keep. */ - zn_xqSh = zn_bLdSh * tzls - zn_aqLdSh; -#endif - - // convective-radiant zone terms - double nAirSh = - i.znCAirSh * tzls // air heat cap - + qsIgHr // convective internal gains - + zn_AnAmfCpT(0) + zn_qIzXAnSh // non-airnet interzone - + znXLGainLs // excess latent gain last subhr === heat of condensation - + zn_uaInfil * Top.tDbOSh; // non-airnet infiltration - zn_nAirSh += nAirSh; - - double dAirSh = // associated denominator terms - i.znCAirSh + zn_AnAmfCp(0) + zn_uaInfil; - zn_dAirSh += dAirSh; - - double nRadSh = qrIgTot; // radiant internal gains - - zn_nRadSh += nRadSh; - - double dRadSh = 0.; // nothing additional - // zn_dRadSh += dRadSh; - - zn_airCx = zn_airCxF * pow3(DegFtoR(tzls)); - zn_cxSh += zn_airCx; - if (zn_cxSh < .001) - zn_cxSh = .001; - - // useful combinations of terms - zn_dRpCx = zn_dRadSh + zn_cxSh; - zn_nRxCx = zn_nRadSh * zn_cxSh; - zn_dRxCx = zn_dRadSh * zn_cxSh; - - zn_balC1 = zn_nAirSh * zn_dRpCx + zn_nRxCx; - zn_balC2 = zn_dAirSh * zn_dRpCx + zn_dRxCx; - -#if defined(CRTERMAH) - if (zn_IsConvRad()) { // convective/radiant: set up CNE-style zone factors - zn_aqLdSh = zn_balC1 / zn_dRpCx; - zn_bLdSh = zn_balC2 / zn_dRpCx; - } - - // zn_xqSh: q = b * t - a using components avail here, for change tests only: - // Intended to be appropriately sensitive to changes when b*a almost equal to - // a, since terminal loads are of form b * t - a. Insurance 12-94 -- need - // was not proven at subhour level when all masses hourly (b4 aMassSh added). - // But it did something: eliminated undercooling in hour 13 in S1LOH test - // run, 12-5-94, so keep. */ - zn_xqSh = zn_bLdSh * tzls - zn_aqLdSh; -#endif - - zn_hcMode = RSYS::rsmOFF; - -#if defined(DEBUGDUMP) - if (DbDo(dbdRCM)) - DbPrintf("%s air qsIgHr=%0.1f qrIgTot=%0.1f uaInfil=%0.1f\n" - " nAir=%0.2f dAir=%0.2f nRad=%0.2f dRad=%0.2f CxF=%0.6g " - "CX=%0.2f\n", - Name(), qsIgHr, qrIgTot, zn_uaInfil, nAirSh, dAirSh, nRadSh, - dRadSh, zn_airCxF, zn_airCx); -#endif - return RCOK; -} // ZNR::zn_InitSubhr + zn_uaInfil = Top.tp_airxOSh // Btuh/cfm-F air flow heat xfer at current outdoor temp + * ( i.znVol // air changes/hr part: zone volume (ft3), times... + * i.infAC / 60. // user input air changes per hour, hourly variable, /60 for per minute. + + i.infELA // ELA part: user input effective leakage area (ft2), hourly variable + * sqrt( // flow is proportional to square root of + zn_stackc * fabs( tzlh - Top.tDbOSh) + // stack coeff (see ZNR.zn_InfilSetup) * delta t + zn_windc * Top.windSpeedSquaredSh ) // wind coeff (ditto) * wind speed ^ 2 + ); +#endif + + // for hvac code, compute a+q, b contributions this subhour for everything but hvac, for use in "Tz = (a + q)/b" + // a+q = sigma(UAi*Ti) + other q's + Told*hc/t + zn_qSgAir = zn_sgAirTarg.st_tot; // solar gain to air + zn_aqLdSh = zn_aqLdHr // hour-constant part: intl gain, solar gain, cond to ambient & specT's, etc. + + aMassSh // subhourly masses UAT (from loadsSurfaces) + + Top.tDbOSh * (zn_ua + zn_uaInfil) // temp * (constUA + infilUA) + + zn_qSgAir // q's: solar gain (power, Btuh) to zone (air) + + zn_qIzSh // interzone transfers to zone (q's), just set by loadsIzxSubhr + // (included zn_qIzXAnSh + zn_anAmfCp[ 0]*deltaT) + + tzls * i.znCAirSh // Told*hc/t + + znXLGainLs; // excess latent gain last subhr === heat of condensation. 5-97. + + // b = sigma(UAi) + hc/t + zn_bLdSh = zn_bLdHr // non-hvac UA's constant for hour + + zn_uaInfil // + infil UA this subhr + + i.znCAirSh; // + hc/t: .znCAirSh is hc. + // "cair" is heat capac of everything in zone not modelled as "mass": + // user const & per-ft2 parts for air, furniture, walls, etc). + +#if !defined( CRTERMAH) + /* zn_xqSh: q = b * t - a using components avail here, for change tests only: + Intended to be appropriately sensitive to changes when b*a almost equal to a, + since terminal loads are of form b * t - a. + Insurance 12-94 -- need was not proven at subhour level when all masses hourly (b4 aMassSh added). + But it did something: eliminated undercooling in hour 13 in S1LOH test run, 12-5-94, so keep. */ + zn_xqSh = zn_bLdSh * tzls - zn_aqLdSh; +#endif + + // convective-radiant zone terms + double nAirSh = + i.znCAirSh*tzls // air heat cap + + qsIgHr // convective internal gains + + zn_AnAmfCpT( 0) + + zn_qIzXAnSh // non-airnet interzone + + znXLGainLs // excess latent gain last subhr === heat of condensation + + zn_uaInfil*Top.tDbOSh; // non-airnet infiltration + zn_nAirSh += nAirSh; + + double dAirSh = // associated denominator terms + i.znCAirSh + + zn_AnAmfCp( 0) + + zn_uaInfil; + zn_dAirSh += dAirSh; + + double nRadSh = + qrIgTot; // radiant internal gains + + zn_nRadSh += nRadSh; + + double dRadSh = 0.; // nothing additional + // zn_dRadSh += dRadSh; + + zn_airCx = zn_airCxF*pow3( DegFtoR( tzls)); + zn_cxSh += zn_airCx; + if (zn_cxSh < .001) + zn_cxSh = .001; + + // useful combinations of terms + zn_dRpCx = zn_dRadSh + zn_cxSh; + zn_nRxCx = zn_nRadSh * zn_cxSh; + zn_dRxCx = zn_dRadSh * zn_cxSh; + + zn_balC1 = zn_nAirSh*zn_dRpCx + zn_nRxCx; + zn_balC2 = zn_dAirSh*zn_dRpCx + zn_dRxCx; + +#if defined( CRTERMAH) + if (zn_IsConvRad()) + { // convective/radiant: set up CNE-style zone factors + zn_aqLdSh = zn_balC1 / zn_dRpCx; + zn_bLdSh = zn_balC2 / zn_dRpCx; + } + + // zn_xqSh: q = b * t - a using components avail here, for change tests only: + // Intended to be appropriately sensitive to changes when b*a almost equal to a, + // since terminal loads are of form b * t - a. + // Insurance 12-94 -- need was not proven at subhour level when all masses hourly (b4 aMassSh added). + // But it did something: eliminated undercooling in hour 13 in S1LOH test run, 12-5-94, so keep. */ + zn_xqSh = zn_bLdSh * tzls - zn_aqLdSh; +#endif + + zn_hcMode = RSYS::rsmOFF; + +#if defined( DEBUGDUMP) + if (DbDo( dbdRCM)) + DbPrintf( "%s air qsIgHr=%0.1f qrIgTot=%0.1f uaInfil=%0.1f\n" + " nAir=%0.2f dAir=%0.2f nRad=%0.2f dRad=%0.2f CxF=%0.6g CX=%0.2f\n", + Name(), qsIgHr, qrIgTot, zn_uaInfil, + nAirSh, dAirSh, nRadSh, dRadSh, zn_airCxF, zn_airCx); +#endif + return RCOK; +} // ZNR::zn_InitSubhr //----------------------------------------------------------------------------- RC ZNR::zn_SetZtuCfIf( - BOO azCf) // TRUE iff "all-zones change" e.g. outdoor w changed -{ - - // set change flag for terminal module if zone loads changed. Cleared in - // ZNR::ztuCompute. - - // monitor subhourly-changing loads inputs to zone heat and moisture balances - // except subhrDur: - // heat balance: aqLdSh: tzls, zn_qIzSh; zn_aqLdHr is hourly; znCair is - // constant. zn_bLdSh: changes only per subhrDur. - // zn_xqSh - // moisture balance: wzls. znLGain is hourly. - // other: md - - if (ztuCf // if a flag already set, skip tests, update priors to avoid unnec - // flagging later. - || azCf // if all-zones change detected (outdoor w, above) - || spCf // spCf implies ztuCf (and more), 6-92. - || RELCHANGE(zn_xqSh, zn_xqShPr) > - .02 * Top.relTol * - MARG2 // b*t - a as known here: difference sensitive. - // .02 copied from hour case. Probably new dominant term; - // may make some of the other checks unnec 12-5-94. - || - ABSCHANGE(tzls, tzlsPr) > - Top.relTol * 2. * - MARG2 // Dominant term. MATCH above. use relTol as small absTol. - // historical 7-13-92 (b4 zn_bLdHr ck above): .002 - // .005 (w/o mdPr check) --> .0001 ebal errs (T12) 4-92. - // (without tz += tzlsDelta below, < .001 required here) - || ABSCHANGE(wzls, wzlsPr) > - .2 * Top.absHumTol // MATCH tolerance above. dflt .000006, or as - // changed. .00002 (vs what??) adds iterations; - // .00001 adds even more; otherwise unexplored. - || zn_md != - zn_mdPr // if zn hvac mode changed last subhr, don't skip tu's now: - // eg be sure tz right on change into a setpoint mode. - || RELCHANGE(zn_qIzSh, qIzShPr) > .00005) { - ztuCf++; // say zone's load changed. - qIzShPr = zn_qIzSh; // reset comparators (Prior values), whenever (and only - // when) flag set. - tzlsPr = tzls; - wzlsPr = wzls; - zn_mdPr = zn_md; - zn_xqHrPr = zn_xqHr; // ditto; hourly part of b*t - a 12-5-94. - zn_xqShPr = zn_xqSh; // ditto; as much of b*t - a as known here 12-5-94 - znLGainPr = znLGain; // ditto - } else // load not changed - { - // extrapolate zone temp in case ztuCompute not invoked elsewhere: - // this is zone/terminal item that changes most when not zone changing much; - // other items, such as zn_qsHvac, remain approx same for same tz rate of - // change. 4-92 - aTz = // ah working copy of tz: init same as tz - tz += tzlsDelta; // extrapolate zone temp - - // Also extraploate zone w cuz znLGain and qlMech continue. - // Tried removing in 6-92, restoring seemed to reduce # subhours & # - // iterations in test runs. - aWz = // ah working copy - wz += wzlsDelta; // also extrap zone humidity ratio - } - - return RCOK; - -} // ZNR::zn_SetZtuCfIf + BOO azCf) // TRUE iff "all-zones change" e.g. outdoor w changed +{ + + // set change flag for terminal module if zone loads changed. Cleared in ZNR::ztuCompute. + + // monitor subhourly-changing loads inputs to zone heat and moisture balances except subhrDur: + // heat balance: aqLdSh: tzls, zn_qIzSh; zn_aqLdHr is hourly; znCair is constant. zn_bLdSh: changes only per subhrDur. + // zn_xqSh + // moisture balance: wzls. znLGain is hourly. + // other: md + + if (ztuCf // if a flag already set, skip tests, update priors to avoid unnec flagging later. + || azCf // if all-zones change detected (outdoor w, above) + || spCf // spCf implies ztuCf (and more), 6-92. + || RELCHANGE(zn_xqSh, zn_xqShPr) > .02 * Top.relTol * MARG2 // b*t - a as known here: difference sensitive. + // .02 copied from hour case. Probably new dominant term; + // may make some of the other checks unnec 12-5-94. + || ABSCHANGE(tzls, tzlsPr) > Top.relTol * 2. * MARG2 // Dominant term. MATCH above. use relTol as small absTol. + // historical 7-13-92 (b4 zn_bLdHr ck above): .002 + // .005 (w/o mdPr check) --> .0001 ebal errs (T12) 4-92. + // (without tz += tzlsDelta below, < .001 required here) + || ABSCHANGE(wzls, wzlsPr) > .2 * Top.absHumTol // MATCH tolerance above. dflt .000006, or as changed. + // .00002 (vs what??) adds iterations; + // .00001 adds even more; otherwise unexplored. + || zn_md != zn_mdPr // if zn hvac mode changed last subhr, don't skip tu's now: + // eg be sure tz right on change into a setpoint mode. + || RELCHANGE(zn_qIzSh, qIzShPr) > .00005) + { + ztuCf++; // say zone's load changed. + qIzShPr = zn_qIzSh; // reset comparators (Prior values), whenever (and only when) flag set. + tzlsPr = tzls; + wzlsPr = wzls; + zn_mdPr = zn_md; + zn_xqHrPr = zn_xqHr; // ditto; hourly part of b*t - a 12-5-94. + zn_xqShPr = zn_xqSh; // ditto; as much of b*t - a as known here 12-5-94 + znLGainPr = znLGain; // ditto + } + else // load not changed + { + // extrapolate zone temp in case ztuCompute not invoked elsewhere: + // this is zone/terminal item that changes most when not zone changing much; + // other items, such as zn_qsHvac, remain approx same for same tz rate of change. 4-92 + aTz = // ah working copy of tz: init same as tz + tz += tzlsDelta; // extrapolate zone temp + + // Also extraploate zone w cuz znLGain and qlMech continue. + // Tried removing in 6-92, restoring seemed to reduce # subhours & # iterations in test runs. + aWz = // ah working copy + wz += wzlsDelta; // also extrap zone humidity ratio + } + + return RCOK; + +} // ZNR::zn_SetZtuCfIf //----------------------------------------------------------------------------- void ZNR::zn_CoupleDHWLossSubhr( - double qLoss, // loss rate from DHWHEATER, DHWTANK, etc., Btuh - float fR /*=0.5f*/) // fraction radiant + double qLoss, // loss rate from DHWHEATER, DHWTANK, etc., Btuh + float fR /*=0.5f*/) // fraction radiant // incorporates zone heat gain (calc'd by DHW models) into heat balance terms { - // TODO: 50/50 conc/rad split is approx at best - zn_qDHWLossRad += qLoss * fR; - zn_qDHWLossAir += qLoss * (1.f - fR); - - zn_qDHWLoss = zn_qDHWLossRad + zn_qDHWLossAir; + // TODO: 50/50 conc/rad split is approx at best + zn_qDHWLossRad += qLoss * fR; + zn_qDHWLossAir += qLoss * (1.f - fR); + + zn_qDHWLoss = zn_qDHWLossRad + zn_qDHWLossAir; -} // ZNR::zn_CoupleDHWLossSubhr +} // ZNR::zn_CoupleDHWLossSubhr //----------------------------------------------------------------------------- -void ZNR::zn_DbDump() const { - // int nhour = (Top.jDay-1)*24 + Top.iHr; - // int stepno = Top.iSubhr+1; - DbPrintf("%s %s: anMCp/T[ 0]=%.2f/%.1f anMCp/T[ 1]=%.2f/%.1f ventUt=%d\n", - Name(), zn_IsUZ() ? "UZ" : "CZ", zn_AnAmfCp(0), zn_AnAmfCpT(0), - zn_AnAmfCp(1), zn_AnAmfCpT(1), zn_ventUt); - DbPrintf( - " Nair=%.2f Dair=%.2f Nrad=%.2f Drad=%.2f CX=%.2f airX=%.3f\n", - zn_nAirSh, zn_dAirSh, zn_nRadSh, zn_dRadSh, zn_cxSh, i.zn_hcAirX); - if (!zn_IsUZ()) { - DbPrintf(" TH=%.2f TD=%.2f TC=%.2f", zn_tzspH, zn_tzspD, zn_tzspC); - if (!zn_HasRSYS()) - DbPrintf(" qhCap=%.f qcCap=%.f\n", i.znQMxH, i.znQMxC); - else - DbPrintf(" tSP=%.2f md=%d amfReq=%.f amfSup=%.f tSup=%.2f\n", - zn_tzsp, zn_hcMode, zn_rsAmfSysReq[0], zn_sysAirI.af_amf, - zn_sysAirI.as_tdb); - } - DbPrintf( - " ta=%.2f tr=%.2f qIzSh=%.f fvent=%.3f pz0=%.4f qsHvac=%.f\n", - tz, zn_tr, zn_qIzSh, zn_fVent, zn_pz0, zn_qsHvac); -} // zn_DbDump +void ZNR::zn_DbDump() const +{ + // int nhour = (Top.jDay-1)*24 + Top.iHr; + // int stepno = Top.iSubhr+1; + DbPrintf("%s %s: anMCp/T[ 0]=%.2f/%.1f anMCp/T[ 1]=%.2f/%.1f ventUt=%d\n", + Name(), zn_IsUZ() ? "UZ" : "CZ", + zn_AnAmfCp( 0), zn_AnAmfCpT( 0), zn_AnAmfCp( 1), zn_AnAmfCpT( 1), + zn_ventUt); + DbPrintf(" Nair=%.2f Dair=%.2f Nrad=%.2f Drad=%.2f CX=%.2f airX=%.3f\n", + zn_nAirSh, zn_dAirSh, zn_nRadSh, zn_dRadSh, zn_cxSh, i.zn_hcAirX); + if (!zn_IsUZ()) + { DbPrintf(" TH=%.2f TD=%.2f TC=%.2f", + zn_tzspH, zn_tzspD, zn_tzspC); + if (!zn_HasRSYS()) + DbPrintf(" qhCap=%.f qcCap=%.f\n", i.znQMxH, i.znQMxC); + else + DbPrintf(" tSP=%.2f md=%d amfReq=%.f amfSup=%.f tSup=%.2f\n", + zn_tzsp, zn_hcMode, zn_rsAmfSysReq[ 0], zn_sysAirI.af_amf, zn_sysAirI.as_tdb); + } + DbPrintf( " ta=%.2f tr=%.2f qIzSh=%.f fvent=%.3f pz0=%.4f qsHvac=%.f\n", + tz, zn_tr, zn_qIzSh, zn_fVent, zn_pz0, zn_qsHvac); +} // zn_DbDump //----------------------------------------------------------------------------- -RSYS *ZNR::zn_GetRSYS() { - RSYS *rs = RsR.GetAtSafe(i.zn_rsi); - return rs; -} // ZNR::zn_GetRSYS +RSYS* ZNR::zn_GetRSYS() +{ + RSYS* rs = RsR.GetAtSafe( i.zn_rsi); + return rs; +} // ZNR::zn_GetRSYS //----------------------------------------------------------------------------- -const RSYS *ZNR::zn_GetRSYS() const { - const RSYS *rs = RsR.GetAtSafe(i.zn_rsi); - return rs; -} // ZNR::zn_GetRSYS +const RSYS* ZNR::zn_GetRSYS() const +{ + const RSYS* rs = RsR.GetAtSafe( i.zn_rsi); + return rs; +} // ZNR::zn_GetRSYS //----------------------------------------------------------------------------- -int ZNR::zn_IsHCAvail( // determine system availability - [[maybe_unused]] int rsMode) const // capability required: rsmHEAT, rsmCOOL +int ZNR::zn_IsHCAvail( // determine system availability + [[maybe_unused]] int rsMode) const // capability required: rsmHEAT, rsmCOOL // returns 1 if specified capability is available -{ - return !zn_IsUZ(); // TODO: schedule availability? -} // ZNR::zn_HCAvail +{ return !zn_IsUZ(); // TODO: schedule availability? +} // ZNR::zn_HCAvail //----------------------------------------------------------------------------- -float ZNR::zn_VentTSup() // vent supply temp under current condtions +float ZNR::zn_VentTSup() // vent supply temp under current condtions // returns vent supply temp (at zone), F { - return Top.tDbOSh; // TODO not quite right (fan heat?) -} // ZNR::zn_VentTSup + return Top.tDbOSh; // TODO not quite right (fan heat?) +} // ZNR::zn_VentTSup //---------------------------------------------------------------------------- -int ZNR::zn_AssessVentUtility() // assess vent utility +int ZNR::zn_AssessVentUtility() // assess vent utility // tz assumed set to current floating zone temp @@ -1064,260 +1002,278 @@ int ZNR::zn_AssessVentUtility() // assess vent utility // always returns 0 for unconditioned zones (even if vent off) // 1: might be helpful { - const int vForbid = -9999; - int ventUt = 0; // init to "don't care" - if (!zn_IsUZ() && zn_anVentEffect > 0) { - if (zn_tzspD < 0.f) { - orer("znTD is needed re vent control but has not been set."); - ventUt = vForbid; - } else if (tz < zn_tzspD) - ventUt = vForbid; // vent off or zone temp below TD (any vent would hurt) - else if (tz > zn_tzspD) { // zone temp is above TD - // vent might be useful - float tvSup = zn_VentTSup(); - if (tvSup > tz) - ventUt = vForbid; // venting would add heat, forbid - else if (tvSup > zn_tzspC && zn_IsHCAvail(RSYS::rsmCOOL)) - ventUt = vForbid; // venting cannot prevent cooling - else - ventUt = 1; // venting might help get to TD - } - // else don't care - } - zn_ventUt = max(ventUt, -1); - return ventUt; -} // ZNR::zn_AssessVentUtility +const int vForbid = -9999; + int ventUt = 0; // init to "don't care" + if (!zn_IsUZ() && zn_anVentEffect > 0) + { + if (zn_tzspD < 0.f) + { + orer("znTD is needed re vent control but has not been set."); + ventUt = vForbid; + } + else if (tz < zn_tzspD) + ventUt = vForbid; // vent off or zone temp below TD (any vent would hurt) + else if (tz > zn_tzspD) + { // zone temp is above TD + // vent might be useful + float tvSup = zn_VentTSup(); + if (tvSup > tz) + ventUt = vForbid; // venting would add heat, forbid + else if (tvSup > zn_tzspC && zn_IsHCAvail( RSYS::rsmCOOL)) + ventUt = vForbid; // venting cannot prevent cooling + else + ventUt = 1; // venting might help get to TD + } + // else don't care + } + zn_ventUt = max(ventUt, -1); + return ventUt; +} // ZNR::zn_AssessVentUtility //----------------------------------------------------------------------------- -int ZNR::zn_FVentCR() // find zone's preferred vent fraction +int ZNR::zn_FVentCR() // find zone's preferred vent fraction // assume tz is at floating temp // sets zn_fVentPrf = best vent fraction for this zone in isolation (0 - 1) // returns -1: vent is harmful // 0: no effect / don't care // 1: useful { -#if 0 && defined(_DEBUG) +#if 0 && defined( _DEBUG) if (Top.jDay == 178) printf("\nzn_FVentCR '%s'", Name()); #endif - int ret = 0; - zn_fVentPrf = 0.f; - - double anAmfCpT = zn_AnAmfCpT(1) - zn_AnAmfCpT(0); - if (fabs(anAmfCpT) < - .001) { // zn_fVentPrf = 0.f; // other zones free to use max - zn_tzVent = 0.f; // vent air temp not known - ret = 0; // vent has no effect on this zone - } else { - double anAmfCp = zn_AnAmfCp(1) - zn_AnAmfCp(0); - zn_tzVent = zn_TAirCR( - anAmfCpT, anAmfCp); // zone air temp at full vent (do not set tz) - if (zn_tzVent > zn_tzspC && zn_IsHCAvail(RSYS::rsmCOOL)) - ret = -1; // zn_fVentPrf = 0 - else if (zn_tzVent > zn_tzspD) { - zn_fVentPrf = 1.f; - ret = 1; - } else { - double amfDT = anAmfCpT - anAmfCp * zn_tzspD; - if (amfDT != 0.) { - double qVent = zn_QAirCR(zn_tzspD); // q required to keep tz at zn_tzspD - zn_fVentPrf = qVent / amfDT; - if (zn_fVentPrf < 0.f) { -#if 0 && defined(_DEBUG) + int ret = 0; + zn_fVentPrf = 0.f; + + double anAmfCpT = zn_AnAmfCpT( 1) - zn_AnAmfCpT( 0); + if (fabs( anAmfCpT) < .001) + { // zn_fVentPrf = 0.f; // other zones free to use max + zn_tzVent = 0.f; // vent air temp not known + ret = 0; // vent has no effect on this zone + } + else + { double anAmfCp = zn_AnAmfCp(1) - zn_AnAmfCp(0); + zn_tzVent = zn_TAirCR( anAmfCpT, anAmfCp); // zone air temp at full vent (do not set tz) + if (zn_tzVent > zn_tzspC && zn_IsHCAvail( RSYS::rsmCOOL)) + ret = -1; // zn_fVentPrf = 0 + else if (zn_tzVent > zn_tzspD) + { zn_fVentPrf = 1.f; + ret = 1; + } + else + { double amfDT = anAmfCpT - anAmfCp * zn_tzspD; + if (amfDT != 0.) + { double qVent = zn_QAirCR( zn_tzspD); // q required to keep tz at zn_tzspD + zn_fVentPrf = qVent / amfDT; + if (zn_fVentPrf < 0.f) + { +#if 0 && defined( _DEBUG) orWarn("fVent < 0\n"); #endif - zn_fVentPrf = 0.f; - } else if (zn_fVentPrf > 1.f) - zn_fVentPrf = 1.f; - } - ret = 1; - } - } - return ret; -} // ZNR::zn_FVentCR + zn_fVentPrf = 0.f; + } + else if (zn_fVentPrf > 1.f) + zn_fVentPrf = 1.f; + } + ret = 1; + } + } + return ret; +} // ZNR::zn_FVentCR //----------------------------------------------------------------------------- -RC ZNR::zn_CondixCR( // zone conditions part 1, convective/radiant model - int ventAvail) // vent availability - // C_VENTAVAILVC_WHOLEBLDG, C_VENTAVAILVC_ZONAL +RC ZNR::zn_CondixCR( // zone conditions part 1, convective/radiant model + int ventAvail) // vent availability + // C_VENTAVAILVC_WHOLEBLDG, C_VENTAVAILVC_ZONAL // determines tz, tr, zn_qsHvac, zn_qIzSh // returns RCOK or ... { - RC rc = RCOK; + RC rc = RCOK; -#if 0 && defined(_DEBUG) +#if 0 && defined( _DEBUG) x if (!Top.isWarmup) x { if (Top.jDay==31 && strMatch( Name(), "SDuctZone")) x printf( "Hit\n"); x } #endif -#if defined(CRTERMAH) - zn_rsAmfSysReq[0] = zn_rsAmfSysReq[1] = 0.; // RSYS air requests +#if defined( CRTERMAH) + zn_rsAmfSysReq[ 0] = zn_rsAmfSysReq[ 1] = 0.; // RSYS air requests - zn_anAmfCpVent = 0.; // full vent heat rate, Btuh/F - zn_anAmfCpTVent = 0.; // full vent heat addition, Btuh + zn_anAmfCpVent = 0.; // full vent heat rate, Btuh/F + zn_anAmfCpTVent = 0.; // full vent heat addition, Btuh - if (zn_HasTerminal()) { - zn_fVent = 0.f; // no vent - zn_pz0 = zn_pz0W[0]; + if (zn_HasTerminal()) + { + zn_fVent = 0.f; // no vent + zn_pz0 = zn_pz0W[0]; - return rc; - } + return rc; + } - // zn_fVent = fVent -- don't change until end (zone value used herein) - zn_qsHvac = 0.; - zn_qlHvac = 0.; -#if defined(ZNHVACFCONV) - zn_fConv = 1.f; // HVAC convective fraction + // zn_fVent = fVent -- don't change until end (zone value used herein) + zn_qsHvac = 0.; + zn_qlHvac = 0.; +#if defined( ZNHVACFCONV) + zn_fConv = 1.f; // HVAC convective fraction #endif #else - // zn_fVent = fVent -- don't change until end (zone value used herein) - zn_qsHvac = 0.; - zn_qlHvac = 0.; -#if defined(ZNHVACFCONV) - zn_fConv = 1.f; // HVAC convective fraction -#endif - zn_rsAmfSysReq[0] = zn_rsAmfSysReq[1] = 0.; // RSYS air requests - - zn_anAmfCpVent = 0.; // full vent heat rate, Btuh/F - zn_anAmfCpTVent = 0.; // full vent heat addition, Btuh -#endif - - bool bUZ = zn_IsUZ(); - float znfVent = ventAvail == C_VENTAVAILVC_ZONAL ? zn_fVentPrf : Top.tp_fVent; - if (bUZ || znfVent > 0.) { // float temp - // * unconditioned: HVAC not possible - // * conditioned: HVAC is off if vent is on - zn_tzsp = 0.f; - if (znfVent > 0.f) { - zn_anAmfCpVent = znfVent * (zn_AnAmfCp(1) - zn_AnAmfCp(0)); - zn_anAmfCpTVent = znfVent * (zn_AnAmfCpT(1) - zn_AnAmfCpT(0)); - - zn_pz0 = - znfVent * zn_pz0W[1] + (1.f - znfVent) * zn_pz0W[0]; // zone pressure - - tz = zn_TAirCR(zn_anAmfCpTVent, zn_anAmfCpVent); - - if (!bUZ && znfVent < 1.f && - znfVent == zn_fVentPrf) { // equal fVents: current zone is "control" - // tz s/b exactly TD, fix if very close - // else error - if (fabs(zn_tzspD - tz) < .001) - tz = zn_tzspD; -#if defined(_DEBUG) - else - printf("Zone '%s': control zone vent mismatch\n", Name()); + // zn_fVent = fVent -- don't change until end (zone value used herein) + zn_qsHvac = 0.; + zn_qlHvac = 0.; +#if defined( ZNHVACFCONV) + zn_fConv = 1.f; // HVAC convective fraction +#endif + zn_rsAmfSysReq[0] = zn_rsAmfSysReq[1] = 0.; // RSYS air requests + + zn_anAmfCpVent = 0.; // full vent heat rate, Btuh/F + zn_anAmfCpTVent = 0.; // full vent heat addition, Btuh +#endif + + bool bUZ = zn_IsUZ(); + float znfVent = ventAvail == C_VENTAVAILVC_ZONAL ? zn_fVentPrf : Top.tp_fVent; + if (bUZ || znfVent > 0.) + { // float temp + // * unconditioned: HVAC not possible + // * conditioned: HVAC is off if vent is on + zn_tzsp = 0.f; + if (znfVent > 0.f) + { + zn_anAmfCpVent = znfVent*(zn_AnAmfCp( 1) - zn_AnAmfCp( 0)); + zn_anAmfCpTVent = znfVent*(zn_AnAmfCpT( 1) - zn_AnAmfCpT( 0)); + + zn_pz0 = znfVent*zn_pz0W[ 1] + (1.f-znfVent)*zn_pz0W[ 0]; // zone pressure + + tz = zn_TAirCR( zn_anAmfCpTVent, zn_anAmfCpVent); + + if (!bUZ && znfVent < 1.f && znfVent == zn_fVentPrf) + { // equal fVents: current zone is "control" + // tz s/b exactly TD, fix if very close + // else error + if (fabs( zn_tzspD - tz) < .001) + tz = zn_tzspD; +#if defined( _DEBUG) + else + printf( "Zone '%s': control zone vent mismatch\n", Name()); #endif - } - } else { // tz already known - zn_pz0 = zn_pz0W[0]; // no venting -#if defined(_DEBUG) - float tzx = zn_TAirCR(0., 0.); - if (fabs(tz - tzx) > .0001) - printf("Zone '%s': floating temp mismatch\n", Name()); + } + } + else + { // tz already known + zn_pz0 = zn_pz0W[ 0]; // no venting +#if defined( _DEBUG) + float tzx = zn_TAirCR( 0., 0.); + if (fabs( tz - tzx) > .0001) + printf( "Zone '%s': floating temp mismatch\n", Name()); #endif - } + } -#if defined(_DEBUG) - if (!bUZ && zn_anVentEffect > 0) { // check conditioned zone outcome - // don't complain about zones with no vents - if (tz < zn_tzspD - .001f) { - if (znfVent > .001 && zn_anAmfCpTVent > .001) - orWarn("vent tz (%.1f F) below TD (%.1f F)", tz, zn_tzspD); - if (tz < zn_tzspH) - orWarn("floating tz (%.1f F) below TH (%.1f F)", tz, zn_tzspH); - } else if (tz > zn_tzspC + .1f) - orWarn("floating tz (%.1f F) above TC (%.1f F)", tz, zn_tzspC); - } -#endif - zn_fVent = znfVent; // final vent fraction - } - - else { // HVAC may be required - - zn_fVent = 0.f; // no vent - zn_pz0 = zn_pz0W[0]; - - RSYS *rs = zn_GetRSYS(); - if (rs) { // this zone is served by an RSYS - if (zn_hcMode == RSYS::rsmOFF) // if not already handled (e.g. rsmOAV) - { - zn_tzsp = 0.f; - if (tz > zn_tzspC) { - zn_tzsp = zn_tzspC; - zn_hcMode = RSYS::rsmCOOL; -#if defined(ZNHVACFCONV) - zn_fConv = zn_fConvC; -#endif - } else if (tz < zn_tzspH) { - zn_tzsp = zn_tzspH; - zn_hcMode = RSYS::rsmHEAT; -#if defined(ZNHVACFCONV) - zn_fConv = zn_fConvH; -#endif - } - if (zn_hcMode != RSYS::rsmOFF) { -#if 0 && defined(_DEBUG) +#if defined( _DEBUG) + if (!bUZ && zn_anVentEffect > 0) + { // check conditioned zone outcome + // don't complain about zones with no vents + if (tz < zn_tzspD - .001f) + { if (znfVent > .001 && zn_anAmfCpTVent > .001) + orWarn("vent tz (%.1f F) below TD (%.1f F)", tz, zn_tzspD); + if (tz < zn_tzspH) + orWarn("floating tz (%.1f F) below TH (%.1f F)", tz, zn_tzspH); + } + else if (tz > zn_tzspC + .1f) + orWarn("floating tz (%.1f F) above TC (%.1f F)", tz, zn_tzspC); + } +#endif + zn_fVent = znfVent; // final vent fraction + } + + else + { // HVAC may be required + + zn_fVent = 0.f; // no vent + zn_pz0 = zn_pz0W[ 0]; + + RSYS* rs = zn_GetRSYS(); + if (rs) + { // this zone is served by an RSYS + if (zn_hcMode == RSYS::rsmOFF) // if not already handled (e.g. rsmOAV) + { zn_tzsp = 0.f; + if (tz > zn_tzspC) + { zn_tzsp = zn_tzspC; + zn_hcMode = RSYS::rsmCOOL; +#if defined( ZNHVACFCONV) + zn_fConv = zn_fConvC; +#endif + } + else if (tz < zn_tzspH) + { zn_tzsp = zn_tzspH; + zn_hcMode = RSYS::rsmHEAT; +#if defined( ZNHVACFCONV) + zn_fConv = zn_fConvH; +#endif + } + if (zn_hcMode != RSYS::rsmOFF) + { +#if 0 && defined( _DEBUG) x if (Top.tp_pass1B) x printf( "1B\n"); #endif - int rsAvail = rs->rs_SupplyAirState(zn_hcMode); - - if (rsAvail >= 2) { // mode is possible - rc |= zn_AirRequest(rs, - 1); // determine air requirement given rs_asSup - // 1: msg if bad supply temp - // zone temp calc'd in zn_CondixCR2() - } else if (rsAvail == 1) { // autosizing but not requested mode - // hold temp at set point, ignore HVAC air (TODO?) - tz = zn_tzsp; -#if defined(ZNHVACFCONV) - zn_QsHvacCR(tz, zn_fConv); + int rsAvail = rs->rs_SupplyAirState( zn_hcMode); + + if (rsAvail >= 2) + { // mode is possible + rc |= zn_AirRequest( rs, 1); // determine air requirement given rs_asSup + // 1: msg if bad supply temp + // zone temp calc'd in zn_CondixCR2() + } + else if (rsAvail == 1) + { // autosizing but not requested mode + // hold temp at set point, ignore HVAC air (TODO?) + tz = zn_tzsp; +#if defined( ZNHVACFCONV) + zn_QsHvacCR( tz, zn_fConv); #else - zn_QAirCR(tz); -#endif - } - // else system not available - // leave tz = floating and zn_rsAmfSysReq[] = 0 - } - } - } else { // non-RSYS zone ("magic" heating and cooling) - zn_IdealHVAC(); - } - } + zn_QAirCR(tz); +#endif + } + // else system not available + // leave tz = floating and zn_rsAmfSysReq[] = 0 + } + } + } + else + { // non-RSYS zone ("magic" heating and cooling) + zn_IdealHVAC(); + } + } -#if defined(_DEBUG) - if (tz < -100.f || tz > 200.f) - warn("Zone '%s': unreasonable air temp %0.2f\n", Name(), tz); +#if defined( _DEBUG) + if (tz < -100.f || tz > 200.f) + warn( "Zone '%s': unreasonable air temp %0.2f\n", Name(), tz); #endif - return rc; -} // ZNR::zn_CondixCR + return rc; +} // ZNR::zn_CondixCR //----------------------------------------------------------------------------- -RC ZNR::zn_AirRequest( // determine air requirement given rs_asSup - RSYS *rs, // zone's RSYS - [[maybe_unused]] int options /*=0*/) // option bits - // 1: report "flipped" supply temps +RC ZNR::zn_AirRequest( // determine air requirement given rs_asSup + RSYS* rs, // zone's RSYS + [[maybe_unused]] int options /*=0*/) // option bits + // 1: report "flipped" supply temps // Prereq: rs->rs_SupplyAirState() at current speed // returns RCOK iff success { - RC rc = RCOK; - double tSup0 = rs->rs_asSup.as_tdb; -#if 1 && defined(_DEBUG) - if ((options & 1) && !Top.isWarmup && rs->rs_speedF > 0.99f && - ((zn_hcMode == RSYS::rsmCOOL && tSup0 >= zn_tzsp) || - (zn_hcMode == RSYS::rsmHEAT && tSup0 <= zn_tzsp && - rs->rs_effHt > 0.f))) { - orWarn("Flipped tSup RSYS='%s', tPln=%0.3f, tSup=%0.3f, tSP=%0.1f, " - "tZn=%0.3f\n", - rs->Name(), rs->rs_asOut.as_tdb, tSup0, zn_tzsp, tz); - } -#endif - - double amfSup0 = - zn_AmfHvacCR(zn_tzsp, tSup0); // dry air mass flow rate required to hold + RC rc = RCOK; + double tSup0 = rs->rs_asSup.as_tdb; +#if 1 && defined( _DEBUG) + if ((options & 1) && !Top.isWarmup && rs->rs_speedF > 0.99f + && ( (zn_hcMode == RSYS::rsmCOOL && tSup0 >= zn_tzsp) + || (zn_hcMode == RSYS::rsmHEAT && tSup0 <= zn_tzsp && rs->rs_effHt > 0.f))) + { + orWarn("Flipped tSup RSYS='%s', tPln=%0.3f, tSup=%0.3f, tSP=%0.1f, tZn=%0.3f\n", + rs->Name(), rs->rs_asOut.as_tdb, tSup0, zn_tzsp, tz); + } +#endif + + double amfSup0 = zn_AmfHvacCR(zn_tzsp, tSup0); // dry air mass flow rate required to hold #if 0 && defined(_DEBUG) double qLoad = zn_QAirCR(zn_tzsp); // load at system, Btuh @@ -1326,530 +1282,534 @@ RC ZNR::zn_AirRequest( // determine air requirement given rs_asSup printf("\nzn_AirRequest mismatch"); #endif - zn_rsAmfSysReq[0] = - rs->rs_ZoneAirRequest(amfSup0, 0); // notify system of requirement - CHECKFP(zn_rsAmfSysReq[0]); // check for NaN etc (debug only) + zn_rsAmfSysReq[0] = rs->rs_ZoneAirRequest(amfSup0, 0); // notify system of requirement + CHECKFP(zn_rsAmfSysReq[0]); // check for NaN etc (debug only) - if (zn_hcMode == RSYS::rsmHEAT && rs->rs_CanHaveAuxHeat() && - rs->rs_speedF == - 1.f) { // HP heating full speed: repeat calc with full aux - double tSup1 = rs->rs_asSupAux.as_tdb; - double amfSup1 = zn_AmfHvacCR(zn_tzsp, tSup1); - zn_rsAmfSysReq[1] = rs->rs_ZoneAirRequest(amfSup1, 1); -#if defined(_DEBUG) - double qHt0 = amfSup0 * (tSup0 - zn_tzsp); - double qHt1 = amfSup1 * (tSup1 - zn_tzsp); - // zn_AmfHvacCR can return DBL_MAX - if (qHt0 > 0. && qHt0 < 1.e10 && qHt1 > 0. && qHt1 < 1.e10 && - frDiff(qHt0, qHt1) > .001) - printf("\nqHt mismatch"); -#endif - } - return rc; -} // ZNR::zn_AirRequest + if (zn_hcMode == RSYS::rsmHEAT && rs->rs_CanHaveAuxHeat() && rs->rs_speedF==1.f) + { // HP heating full speed: repeat calc with full aux + double tSup1 = rs->rs_asSupAux.as_tdb; + double amfSup1 = zn_AmfHvacCR(zn_tzsp, tSup1); + zn_rsAmfSysReq[1] = rs->rs_ZoneAirRequest(amfSup1, 1); +#if defined( _DEBUG) + double qHt0 = amfSup0 * (tSup0 - zn_tzsp); + double qHt1 = amfSup1 * (tSup1 - zn_tzsp); + // zn_AmfHvacCR can return DBL_MAX + if (qHt0 > 0. && qHt0 < 1.e10 && qHt1 > 0. && qHt1 < 1.e10 + && frDiff(qHt0, qHt1) > .001) + printf("\nqHt mismatch"); +#endif + } + return rc; +} // ZNR::zn_AirRequest //----------------------------------------------------------------------------- -void ZNR::zn_SetRSYSAmf( // set RSYS air flow - float fSize, // fraction of zone request that system can meet - int iAux) // [ 0]=main, [ 1]=main+aux +void ZNR::zn_SetRSYSAmf( // set RSYS air flow + float fSize, // fraction of zone request that system can meet + int iAux) // [ 0]=main, [ 1]=main+aux { - RSYS *rs = zn_GetRSYS(); + RSYS* rs = zn_GetRSYS(); - // supply / return adjusted for duct leakage - int iHC = rs->rs_DsHC(); // select duct config (0=htg, 1=clg) - zn_rsAmfSup = fSize * zn_rsAmfSysReq[iAux] * rs->rs_ducts[iHC].ductLkXF[0]; - zn_rsAmfRet = fSize * zn_rsAmfSysReq[iAux] * rs->rs_ducts[iHC].ductLkXF[1]; + // supply / return adjusted for duct leakage + int iHC = rs->rs_DsHC(); // select duct config (0=htg, 1=clg) + zn_rsAmfSup = fSize * zn_rsAmfSysReq[ iAux] * rs->rs_ducts[ iHC].ductLkXF[ 0]; + zn_rsAmfRet = fSize * zn_rsAmfSysReq[ iAux] * rs->rs_ducts[ iHC].ductLkXF[ 1]; - zn_rsFSize = fSize; + zn_rsFSize = fSize; -} // ZNR::zn_SetRSYSAmf +} // ZNR::zn_SetRSYSAmf //---------------------------------------------------------------------------- -void ZNR::zn_SetRSYSAmfFromTSup() // set RSYS-to-zone air flow given tSup +void ZNR::zn_SetRSYSAmfFromTSup() // set RSYS-to-zone air flow given tSup // supply temp = rs_asSup.as_tdb // call ONLY when RSYS capacity sufficient { - if (zn_hcMode != RSYS::rsmOFF) { - RSYS *rs = zn_GetRSYS(); - int iHC = rs->rs_DsHC(); - zn_rsAmfSup = zn_AmfHvacCR(zn_tzsp, rs->rs_asSup.as_tdb); - zn_rsAmfRet = zn_rsAmfSup * rs->rs_ducts[iHC].ductLkXF[1] / - rs->rs_ducts[iHC].ductLkXF[0]; - zn_rsFSize = 1.f; - } - // else zn_rsAmfxxx = 0 -} // ZNR::zn_SetRSYSAmfFromTSup + if (zn_hcMode != RSYS::rsmOFF) + { RSYS* rs = zn_GetRSYS(); + int iHC = rs->rs_DsHC(); + zn_rsAmfSup = zn_AmfHvacCR( zn_tzsp, rs->rs_asSup.as_tdb); + zn_rsAmfRet = zn_rsAmfSup * rs->rs_ducts[ iHC].ductLkXF[ 1] / rs->rs_ducts[ iHC].ductLkXF[ 0]; + zn_rsFSize = 1.f; + } + // else zn_rsAmfxxx = 0 +} // ZNR::zn_SetRSYSAmfFromTSup //----------------------------------------------------------------------------- -void ZNR::zn_IdealHVAC() // idealized space conditioning +void ZNR::zn_IdealHVAC() // idealized space conditioning // applies "magic" heating or cooling { - if (tz > zn_tzspC && i.znQMxC < 0.f) { - zn_hcMode = RSYS::rsmCOOL; - tz = zn_tzsp = zn_tzspC; -#if defined(ZNHVACFCONV) - zn_fConv = zn_fConvC; - zn_qsHvac = zn_QsHvacCR(tz, zn_fConv); -#else - zn_qsHvac = zn_QAirCR(tz); -#endif - if (fabs(zn_qsHvac) > fabs(i.znQMxC)) { - zn_qsHvac = i.znQMxC; -#if defined(ZNHVACFCONV) - tz = zn_TAirCR(zn_qsHvac * zn_fConv, 0., zn_qsHvac * (1.f - zn_fConv)); -#else - tz = zn_TAirCR(zn_qsHvac, 0.); -#endif - } - // else capacity sufficient - } else if (tz < zn_tzspH && i.znQMxH > 0.f) { // tz < TH: air needs heating - zn_hcMode = RSYS::rsmHEAT; - tz = zn_tzsp = zn_tzspH; -#if defined(ZNHVACFCONV) - zn_fConv = zn_fConvH; - zn_qsHvac = zn_QsHvacCR(tz, zn_fConv); + if (tz > zn_tzspC && i.znQMxC < 0.f) + { + zn_hcMode = RSYS::rsmCOOL; + tz = zn_tzsp = zn_tzspC; + #if defined( ZNHVACFCONV) + zn_fConv = zn_fConvC; + zn_qsHvac = zn_QsHvacCR(tz, zn_fConv); + #else + zn_qsHvac = zn_QAirCR(tz); + #endif + if (fabs(zn_qsHvac) > fabs(i.znQMxC)) + { zn_qsHvac = i.znQMxC; + #if defined( ZNHVACFCONV) + tz = zn_TAirCR(zn_qsHvac * zn_fConv, 0., zn_qsHvac * (1.f - zn_fConv)); + #else + tz = zn_TAirCR(zn_qsHvac, 0.); + #endif + } + // else capacity sufficient + } + else if (tz < zn_tzspH && i.znQMxH > 0.f) + { // tz < TH: air needs heating + zn_hcMode = RSYS::rsmHEAT; + tz = zn_tzsp = zn_tzspH; +#if defined( ZNHVACFCONV) + zn_fConv = zn_fConvH; + zn_qsHvac = zn_QsHvacCR(tz, zn_fConv); #else - zn_qsHvac = zn_QAirCR(tz); + zn_qsHvac = zn_QAirCR(tz); #endif - if (zn_qsHvac > i.znQMxH) { // heat at heating capacity, find resulting tz. - zn_qsHvac = i.znQMxH; -#if defined(ZNHVACFCONV) - tz = zn_TAirCR(zn_qsHvac * zn_fConv, 0., zn_qsHvac * (1.f - zn_fConv)); + if (zn_qsHvac > i.znQMxH) + { // heat at heating capacity, find resulting tz. + zn_qsHvac = i.znQMxH; +#if defined( ZNHVACFCONV) + tz = zn_TAirCR(zn_qsHvac * zn_fConv, 0., zn_qsHvac * (1.f - zn_fConv)); #else - tz = zn_TAirCR(zn_qsHvac, 0.); + tz = zn_TAirCR(zn_qsHvac, 0.); #endif - } - // else capacity sufficient - } -} // ZNR::zn_IdealHVAC + } + // else capacity sufficient + } +} // ZNR::zn_IdealHVAC //----------------------------------------------------------------------------- -void ZNR::zn_MapTerminalResults() // transfer terminal model outcomes to working - // mbrs +void ZNR::zn_MapTerminalResults() // transfer terminal model outcomes to working mbrs { - double cSup = 0.; - double cRet = 0.; - double tSup = 0.; - double wSup = 0.; + double cSup = 0.; + double cRet = 0.; + double tSup = 0.; + double wSup = 0.; - for (TU *tu = nullptr; nxTu(tu);) { - const AH *ah = AhB.GetAtSafe(tu->ai); - if (!ah) { // no terminal air flow w/o AH - continue; - } + for (TU* tu = nullptr; nxTu(tu); ) + { + const AH* ah = AhB.GetAtSafe(tu->ai); + if (!ah) + { // no terminal air flow w/o AH + continue; + } - if (ah->ahcc.q < 0.) - zn_hcMode = RSYS::rsmCOOL; - else if (ah->ahhc.q > 0.) - zn_hcMode = RSYS::rsmHEAT; + if (ah->ahcc.q < 0.) + zn_hcMode = RSYS::rsmCOOL; + else if (ah->ahhc.q > 0.) + zn_hcMode = RSYS::rsmHEAT; -#if defined(_DEBUG) - double aCz = tu->aCz(); - if (frDiff(tu->cz, aCz) > 0.001) - printf("\nFlow mismatch -- tu->cz=%.2f tu->aCz()=%.2f", tu->cz, aCz); +#if defined( _DEBUG) + double aCz = tu->aCz(); + if (frDiff(tu->cz, aCz) > 0.001) + printf("\nFlow mismatch -- tu->cz=%.2f tu->aCz()=%.2f", + tu->cz, aCz); #endif - if (tu->cz > 0.) { // accumulate supply flow and state - cSup += tu->cz; // supply flow (at zone), Btuh/F - cRet += tu->cz * - (1. - ah->oaZoneLeakF * ah->po); // return flow (at zone), Btuh/F - tSup += tu->cz * ah->ah_tSup; - wSup += tu->cz * ah->ah_wSup; - } - } + if (tu->cz > 0.) + { // accumulate supply flow and state + cSup+= tu->cz; // supply flow (at zone), Btuh/F + cRet += tu->cz * (1.-ah->oaZoneLeakF * ah->po); // return flow (at zone), Btuh/F + tSup += tu->cz * ah->ah_tSup; + wSup += tu->cz * ah->ah_wSup; + } + } -#if defined(_DEBUG) - if (frDiff(cSup, double(cSum)) > 0.001) - printf("\nTotal flow mismatch"); +#if defined( _DEBUG) + if (frDiff( cSup, double( cSum)) > 0.001) + printf("\nTotal flow mismatch"); #endif - if (cSup > 0.) { - tSup /= cSup; - wSup /= cSup; + if (cSup > 0.) + { + tSup /= cSup; + wSup /= cSup; - zn_rsAmfSup = cSup / Top.tp_airSH; + zn_rsAmfSup = cSup / Top.tp_airSH; - if (!Top.tp_airNetActive) - cRet = cSup; // prevent unbalanced if no airnet - // (no relief holes available) + if (!Top.tp_airNetActive) + cRet = cSup; // prevent unbalanced if no airnet + // (no relief holes available) - zn_rsAmfRet = cRet / Top.tp_airSH; + zn_rsAmfRet = cRet / Top.tp_airSH; - zn_sysAirI.af_AccumDry(zn_rsAmfSup, tSup, wSup); - zn_sysAirO.af_AccumDry(-zn_rsAmfRet, tz, - wzls); // w finalized in zn_AirXMoistureBal - } + zn_sysAirI.af_AccumDry(zn_rsAmfSup, tSup, wSup); + zn_sysAirO.af_AccumDry(-zn_rsAmfRet, tz, wzls); // w finalized in zn_AirXMoistureBal + } -} // ZNR::zn_MapTerminalResults +} // ZNR::zn_MapTerminalResults //----------------------------------------------------------------------------- -RC ZNR::zn_CondixCR2() // zone conditions, part 2 -{ - RSYS *rs = zn_GetRSYS(); - if (rs) { - if (zn_rsAmfSup > - 0.) { // this zone is served by RSYS and receives air - // RSYS has determined amf available in rs_AllocateZoneAir() - // zn_rsAmfSup, zn_rsAmfRet, and zn_rsFSize are set -#if defined(_DEBUG) - if (!Top.isWarmup && rs->rs_mode != zn_hcMode) - // zone mode should match RSYS mode - // don't check during warmup (including autosize) - printf("Zone '%s': Mode mismatch\n", Name()); -#endif - double mCp = zn_rsAmfSup * Top.tp_airSH; - double tSup = rs->rs_asSup.as_tdb; - tz = zn_TAirCR(mCp * tSup, mCp); - zn_qsHvac = mCp * (tSup - tz); - // zn_qsHvac = zn_QAirCR(tzls); experiment, same results 11-21 - zn_sysAirI.af_AccumDry(zn_rsAmfSup, rs->rs_asSup); - if (zn_hcMode == RSYS::rsmOAV) - zn_OAVRlfO.af_AccumDry(-zn_rsAmfSup, tz, wzls); // w not used - else - zn_sysAirO.af_AccumDry(-zn_rsAmfRet, tz, - wzls); // w finalized in zn_AirXMoistureBal -#if defined(_DEBUG) - if (tz < -100.f || tz > 200.f) - warn("Zone '%s': unreasonable air temp %0.2f\n", Name(), tz); +RC ZNR::zn_CondixCR2() // zone conditions, part 2 +{ + RSYS* rs = zn_GetRSYS(); + if (rs) + { if (zn_rsAmfSup > 0.) + { // this zone is served by RSYS and receives air + // RSYS has determined amf available in rs_AllocateZoneAir() + // zn_rsAmfSup, zn_rsAmfRet, and zn_rsFSize are set +#if defined( _DEBUG) + if (!Top.isWarmup && rs->rs_mode != zn_hcMode) + // zone mode should match RSYS mode + // don't check during warmup (including autosize) + printf( "Zone '%s': Mode mismatch\n", Name()); +#endif + double mCp = zn_rsAmfSup*Top.tp_airSH; + double tSup = rs->rs_asSup.as_tdb; + tz = zn_TAirCR( mCp*tSup, mCp); + zn_qsHvac = mCp*(tSup - tz); + // zn_qsHvac = zn_QAirCR(tzls); experiment, same results 11-21 + zn_sysAirI.af_AccumDry( zn_rsAmfSup, rs->rs_asSup); + if (zn_hcMode == RSYS::rsmOAV) + zn_OAVRlfO.af_AccumDry( -zn_rsAmfSup, tz, wzls); // w not used + else + zn_sysAirO.af_AccumDry( -zn_rsAmfRet, tz, wzls); // w finalized in zn_AirXMoistureBal +#if defined( _DEBUG) + if (tz < -100.f || tz > 200.f) + warn( "Zone '%s': unreasonable air temp %0.2f\n", Name(), tz); #endif - } + } -#if defined(_DEBUG) - // test against setpoint (with a little tolerance) - if (!Top.isWarmup) // skip if warmup (including autosize) - { - if (zn_hcMode == RSYS::rsmHEAT) { - if (tz > zn_tzspH + .001f) - printf("\nOverheat"); - } else if (zn_hcMode == RSYS::rsmCOOL) { - if (tz < zn_tzspC - .001f) - printf("\nOvercool"); - } - } -#endif - } -#if defined(CRTERMAH) - else if (zn_HasTerminal()) { - zn_MapTerminalResults(); - } -#endif - // else all known -#if defined(ZNHVACFCONV) - zn_tr = zn_TRadCR(tz, zn_qsHvac * (1. - zn_fConv)); +#if defined( _DEBUG) + // test against setpoint (with a little tolerance) + if (!Top.isWarmup) // skip if warmup (including autosize) + { if (zn_hcMode == RSYS::rsmHEAT) + { if (tz > zn_tzspH + .001f) + printf( "\nOverheat"); + } + else if (zn_hcMode == RSYS::rsmCOOL) + { if (tz < zn_tzspC - .001f) + printf( "\nOvercool"); + } + } +#endif + } +#if defined( CRTERMAH) + else if (zn_HasTerminal()) + { + zn_MapTerminalResults(); + + + } +#endif + // else all known +#if defined( ZNHVACFCONV) + zn_tr = zn_TRadCR( tz, zn_qsHvac*(1.-zn_fConv)); #else - zn_tr = zn_TRadCR(tz); + zn_tr = zn_TRadCR( tz); #endif - zn_qIzSh = - zn_anAmfCpTVent + zn_AnAmfCpT(0) + zn_sysDepAirIls.af_AmfCpT() - - (zn_anAmfCpVent + zn_AnAmfCp(0) + zn_sysDepAirIls.af_AmfCp()) * tz + - zn_qIzXAnSh; + zn_qIzSh = zn_anAmfCpTVent + zn_AnAmfCpT( 0) + zn_sysDepAirIls.af_AmfCpT() + - (zn_anAmfCpVent + zn_AnAmfCp( 0) + zn_sysDepAirIls.af_AmfCp())*tz + + zn_qIzXAnSh; - return RCOK; + return RCOK; -} // ZNR::zn_CondixCR2 +} // ZNR::zn_CondixCR2 //---------------------------------------------------------------------------- #if defined(_DEBUG) -RC ZNR::zn_AirFlowVsTsup() { - RC rc = RCOK; - if (!Top.isEndHour) - return rc; // skip all except last substep +RC ZNR::zn_AirFlowVsTsup() +{ + RC rc = RCOK; + if (!Top.isEndHour) + return rc; // skip all except last substep + + RSYS* rs = zn_GetRSYS(); + if (!rs) + return RCBAD; - RSYS *rs = zn_GetRSYS(); - if (!rs) - return RCBAD; + static FILE* f = NULL; + if (!f) + { + f = fopen(strtprintf("AirCurves.csv"), "wt"); + if (!f) + return RCBAD; // can't open file + fprintf(f, "%s\n", rs->rs_desc.CStrIfNotBlank( "Curves")); + + fprintf(f, "Mon,Day,Hr,Sh,fAmf,amf,tReg,amfNeeded\n"); + + } - static FILE *f = NULL; - if (!f) { - f = fopen(strtprintf("AirCurves.csv"), "wt"); - if (!f) - return RCBAD; // can't open file - fprintf(f, "%s\n", rs->rs_desc.CStrIfNotBlank("Curves")); + // RSYS rsSave( rs); // save for restore at exit + // (we alter mbrs here) - fprintf(f, "Mon,Day,Hr,Sh,fAmf,amf,tReg,amfNeeded\n"); - } - // RSYS rsSave( rs); // save for restore at exit - // (we alter mbrs here) + AIRSTATE asSup; // air state at register + for (int iAf = 0; iAf < 10; iAf++) + { + float fAmf = 1.f - float(iAf) / 10.f; - AIRSTATE asSup; // air state at register - for (int iAf = 0; iAf < 10; iAf++) { - float fAmf = 1.f - float(iAf) / 10.f; + float amf = rs->rs_TSupVarFlow(fAmf, asSup); - float amf = rs->rs_TSupVarFlow(fAmf, asSup); + double amfNeed = zn_AmfHvacCR(70. /*tz*/, asSup.as_tdb); - double amfNeed = zn_AmfHvacCR(70. /*tz*/, asSup.as_tdb); + fprintf(f, "%d,%d,%d,%d,%.1f,%.0f,%0.1f,%.0f\n", + Top.tp_date.month, Top.tp_date.mday, Top.iHrST + 1,Top.iSubhr, + fAmf, amf, asSup.as_tdb, amfNeed); - fprintf(f, "%d,%d,%d,%d,%.1f,%.0f,%0.1f,%.0f\n", Top.tp_date.month, - Top.tp_date.mday, Top.iHrST + 1, Top.iSubhr, fAmf, amf, - asSup.as_tdb, amfNeed); - } + } - return rc; -} // ZNR::zn_AirFlowVsTsup + return rc; +} // ZNR::zn_AirFlowVsTsup //----------------------------------------------------------------------------- -float RSYS::rs_TSupVarFlow(float fAmf, AIRSTATE &asSup) { +float RSYS::rs_TSupVarFlow( + float fAmf, + AIRSTATE& asSup) +{ - asSup = rs_asOut; + asSup = rs_asOut; - float amf = rs_amf * fAmf; + float amf = rs_amf * fAmf; - return rs_SupplyDSEAndDucts(asSup, amf); + return rs_SupplyDSEAndDucts(asSup, amf); -} // RSYS::rs_TSupVarFlow +} // RSYS::rs_TSupVarFlow #endif //---------------------------------------------------------------------------- -double ZNR::zn_TAirCR( // zone air temp w/ add'l radiant heat - double mCpT, // add'l air heat to zone, Btuh - // either heat added to air ("qAir") - // or mass flow*Tsup (with associated mCp) - double mCp, // add'l air heat rate, Btuh/F - double qRad) const // add'l radiant heat to zone, Btuh +double ZNR::zn_TAirCR( // zone air temp w/ add'l radiant heat + double mCpT, // add'l air heat to zone, Btuh + // either heat added to air ("qAir") + // or mass flow*Tsup (with associated mCp) + double mCp, // add'l air heat rate, Btuh/F + double qRad) const // add'l radiant heat to zone, Btuh // returns zone air temp, F { - double tza = (mCpT * zn_dRpCx + zn_balC1 + zn_cxSh * qRad) / - (mCp * zn_dRpCx + zn_balC2); - return tza; -} // ZNR::zn_TAirCR + double tza = (mCpT*zn_dRpCx + zn_balC1 + zn_cxSh*qRad) + / (mCp*zn_dRpCx + zn_balC2); + return tza; +} // ZNR::zn_TAirCR //---------------------------------------------------------------------------- -double ZNR::zn_TAirCR( // zone air temp - double mCpT, // add'l air heat to zone, Btuh - // either heat added to air ("qAir") - // or mass flow*Tsup (with associated mCp) - double mCp) const // add'l air heat rate, Btuh/F +double ZNR::zn_TAirCR( // zone air temp + double mCpT, // add'l air heat to zone, Btuh + // either heat added to air ("qAir") + // or mass flow*Tsup (with associated mCp) + double mCp) const // add'l air heat rate, Btuh/F // returns zone air temp, F { - double tza = (mCpT * zn_dRpCx + zn_balC1) / (mCp * zn_dRpCx + zn_balC2); - return tza; -} // ZNR::zn_TAirCR + double tza = (mCpT*zn_dRpCx + zn_balC1) + / (mCp*zn_dRpCx + zn_balC2); + return tza; +} // ZNR::zn_TAirCR //---------------------------------------------------------------------------- // Note float w/ no add'l gains (cnrecs.def inline) // void zn_TAirFloatCR() { tz = zn_balC1 / zn_balC2; } //---------------------------------------------------------------------------- -double ZNR::zn_TRadCR( // zone radiant temp w/ add'l radiant heat - double tza, // zone air temp, F - double qRad) const // add'l radiant heat to zone +double ZNR::zn_TRadCR( // zone radiant temp w/ add'l radiant heat + double tza, // zone air temp, F + double qRad) const // add'l radiant heat to zone // returns zone radiant temp, F { - double tzr = (zn_nRadSh + qRad + zn_cxSh * tza) / zn_dRpCx; - return tzr; -} // ZNR::zn_TRadCR + double tzr = (zn_nRadSh + qRad + zn_cxSh*tza)/zn_dRpCx; + return tzr; +} // ZNR::zn_TRadCR //---------------------------------------------------------------------------- -double ZNR::zn_TRadCR( // zone radiant temp - double tza) const // zone air temp, F +double ZNR::zn_TRadCR( // zone radiant temp + double tza) const // zone air temp, F // returns zone radiant temp, F { - double tzr = (zn_nRadSh + zn_cxSh * tza) / zn_dRpCx; - return tzr; -} // ZNR::zn_TRadCR + double tzr = (zn_nRadSh + zn_cxSh * tza) / zn_dRpCx; + return tzr; +} // ZNR::zn_TRadCR //------------------------------------------------------------------------------ -double ZNR::zn_QAirCR( // air heating/cooling requirements w/ add'l radiant heat - double tza, // zone air temp, F - double qRad) const // add'l radiant heat +double ZNR::zn_QAirCR( // air heating/cooling requirements w/ add'l radiant heat + double tza, // zone air temp, F + double qRad) const // add'l radiant heat // returns zone air heating/cooling (Btuh) required to maintain tza { - double q = (zn_balC2 * tza - zn_balC1 - zn_cxSh * qRad) / zn_dRpCx; - return q; -} // ZNR::zn_QAirCR + double q = (zn_balC2*tza - zn_balC1 - zn_cxSh*qRad) / zn_dRpCx; + return q; +} // ZNR::zn_QAirCR //------------------------------------------------------------------------------ -double ZNR::zn_QAirCR( // air heating/cooling requirements - double tza) const // zone air temp, F +double ZNR::zn_QAirCR( // air heating/cooling requirements + double tza) const // zone air temp, F // returns zone air heating/cooling (Btuh) required to maintain tza { - double q = (zn_balC2 * tza - zn_balC1) / zn_dRpCx; - return q; -} // ZNR::zn_QAirCR + double q = (zn_balC2*tza - zn_balC1) / zn_dRpCx; + return q; +} // ZNR::zn_QAirCR //------------------------------------------------------------------------------ -double ZNR::zn_QsHvacCR( // sensible hvac heat/cool requirements - // (with possible conv/radiant mix) - double tza, // zone air temp, F - float fConv) const // convective faction of available source +double ZNR::zn_QsHvacCR( // sensible hvac heat/cool requirements + // (with possible conv/radiant mix) + double tza, // zone air temp, F + float fConv) const // convective faction of available source // assume no vent (HVAC active) // does NOT change ZNR state // returns sensible power rqd to hold tza, Btuh { - double qs = (zn_balC2 * tza - zn_balC1) / (fConv * zn_dRadSh + zn_cxSh); - return qs; -} // ZNR::zn_QsHvacCR + double qs = (zn_balC2*tza - zn_balC1) / (fConv*zn_dRadSh + zn_cxSh); + return qs; +} // ZNR::zn_QsHvacCR //------------------------------------------------------------------------------ -double -ZNR::zn_AmfHvacCR( // sensible hvac air requirements w/ add'l radiant heat - double tza, // zone air temp, F - double tSup, // available supply air temp, F - double qRad) const // add'l radiant heat to zone +double ZNR::zn_AmfHvacCR( // sensible hvac air requirements w/ add'l radiant heat + double tza, // zone air temp, F + double tSup, // available supply air temp, F + double qRad) const // add'l radiant heat to zone // assume no vent (HVAC active) // does NOT change ZNR state // returns dry-air mass flow rate required to hold tza, lbm/hr { - double amf = (fabs(tza - tSup) < tol_tF) - ? DBL_MAX - : (zn_balC1 - zn_balC2 * tza + zn_cxSh * qRad) / - (zn_dRpCx * (tza - tSup) * Top.tp_airSH); - return amf; -} // ZNR::zn_AmfHvacCR + double amf = (fabs( tza - tSup) < tol_tF) + ? DBL_MAX + : (zn_balC1 - zn_balC2*tza + zn_cxSh*qRad) + / (zn_dRpCx * (tza - tSup) * Top.tp_airSH); + return amf; +} // ZNR::zn_AmfHvacCR //------------------------------------------------------------------------------ -double ZNR::zn_AmfHvacCR( // sensible hvac air requirements - double tza, // zone air temp, F - double tSup) const // available supply air temp, F +double ZNR::zn_AmfHvacCR( // sensible hvac air requirements + double tza, // zone air temp, F + double tSup) const // available supply air temp, F // assume no vent (HVAC active) // does NOT change ZNR state // returns dry-air mass flow rate required to hold tza, lbm/hr { - double amf = (fabs(tza - tSup) < tol_tF) - ? DBL_MAX - : (zn_balC1 - zn_balC2 * tza) / - (zn_dRpCx * (tza - tSup) * Top.tp_airSH); - return amf; -} // ZNR::zn_AmfHvacCR + double amf = (fabs(tza - tSup) < tol_tF) + ? DBL_MAX + : (zn_balC1 - zn_balC2 * tza) + / (zn_dRpCx * (tza - tSup) * Top.tp_airSH); + return amf; +} // ZNR::zn_AmfHvacCR //----------------------------------------------------------------------------- -double ZNR::zn_AnAmf() const // current AirNet mass flow rate -// returns dry air mass flow rate into zone (lbm/hr) due to AirNet exchanges, -// lbm/hr -{ - double amf = - (1.f - zn_fVent) * zn_airNetI[0].af_amf + zn_fVent * zn_airNetI[1].af_amf; - return amf; -} // ZNR::zn_AnAmf +double ZNR::zn_AnAmf() const // current AirNet mass flow rate +// returns dry air mass flow rate into zone (lbm/hr) due to AirNet exchanges, lbm/hr +{ + double amf = (1.f - zn_fVent) * zn_airNetI[ 0].af_amf + + zn_fVent * zn_airNetI[ 1].af_amf; + return amf; +} // ZNR::zn_AnAmf //----------------------------------------------------------------------------- -double ZNR::zn_NonAnIVAmf( // non-airnet dry air mass flow rate - double dryAirMass /*=-1.*/) const // current zone dry air mass, lbm - // default = zn_dryAirMass +double ZNR::zn_NonAnIVAmf( // non-airnet dry air mass flow rate + double dryAirMass /*=-1.*/) const // current zone dry air mass, lbm + // default = zn_dryAirMass // Note: non-airnet flows contribute to heat and moisture balance // but *NOT* pressure balance (assumed balanced) // returns dry air mass flow rate (lbm/hr) into zone due to non-AirNet sources { - if (dryAirMass < 0.) - dryAirMass = zn_dryAirMass; - double amf = i.infAC * dryAirMass; - if (i.infELA > 0.f) { - float amfELA = - Top.tp_rhoDryOSh * 60.f * - i.infELA // user input effective leakage area (ft2), hourly variable - * - sqrt( // flow is proportional to square root of - zn_stackc * - fabs(tzls - Top.tDbOSh) + // stack coeff (see - // ZNR.zn_InfilSetup) * delta t - zn_windc * - Top.windSpeedSquaredSh); // wind coeff (ditto) * wind speed ^ 2 - - amf += amfELA; - } - return amf; -} // ZNR::zn_NonAnIVAmf + if (dryAirMass < 0.) + dryAirMass = zn_dryAirMass; + double amf = i.infAC * dryAirMass; + if (i.infELA > 0.f) + { float amfELA = + Top.tp_rhoDryOSh + * 60.f + * i.infELA // user input effective leakage area (ft2), hourly variable + * sqrt( // flow is proportional to square root of + zn_stackc * fabs( tzls - Top.tDbOSh) + // stack coeff (see ZNR.zn_InfilSetup) * delta t + zn_windc * Top.windSpeedSquaredSh ); // wind coeff (ditto) * wind speed ^ 2 + + amf += amfELA; + } + return amf; +} // ZNR::zn_NonAnIVAmf //------------------------------------------------------------------------------ -double ZNR::zn_AirXMoistureBal( // air change rate and zone moisture balance - bool bFinal, // true: set ZNR members to calculated zone state - // false: use temporary vars only - double amfX /*=0.*/, // additional system flow (e.g. air handler), lbm/h - double mwX /*=0.*/, // air handler moisture flow, lbm/hr - double _tz /*=-99.*/, // alternative air temp, F; default = ZNR tz - double * - pXLGain /*=nullptr*/) // if non-null, return condensation heat gain, Btu +double ZNR::zn_AirXMoistureBal( // air change rate and zone moisture balance + bool bFinal, // true: set ZNR members to calculated zone state + // false: use temporary vars only + double amfX /*=0.*/, // additional system flow (e.g. air handler), lbm/h + double mwX /*=0.*/, // air handler moisture flow, lbm/hr + double _tz /*=-99.*/, // alternative air temp, F; default = ZNR tz + double* pXLGain /*=nullptr*/) // if non-null, return condensation heat gain, Btu // see also ZNR::znW() re CNE zone moisture balance // returns humidity ratio { - if (_tz < -98.) - _tz = tz; - - double rho = psyDenMoistAir(_tz, wzls); // moist air density, lbm/ft3 - double dryAirMass = - max(i.znVol * psyDenDryAir2(rho, wzls), .1); // dry air mass, lbm - - // air changes due to infil + vent - // includes flows induced by ducts / HVAC - // but not those flows - // TODO: improve other infil models - double nonAnIVAmf = zn_NonAnIVAmf(dryAirMass); - double ivAirX = (zn_AnAmf() + nonAnIVAmf) / dryAirMass; - if (ivAirX < 0.f) - ivAirX = 0.f; - - double amfSys = zn_ductLkI.af_amf + zn_sysAirI.af_amf + amfX; - double airX = ivAirX + amfSys / dryAirMass; - - // internal gain - double mwIG = znLGain / PsyHCondWtr; // latent internal gains, lbm/hr - - // non-airnet infil, lbm/hr - double mwInf = nonAnIVAmf * Top.wOSh; - - // IZXFER (airnet) gains not including duct leakage and HVAC - double mwAN = (1.f - zn_fVent) * zn_airNetI[0].af_Wmf() + - (zn_fVent)*zn_airNetI[1].af_Wmf(); - - double mw = mwIG + mwInf + mwAN + - mwX // total water vapor mass flow rate, lbm/hr - + zn_ductLkI.af_Wmf() + zn_sysAirI.af_Wmf(); - - // TODO: HPWH moisture removal? 2-16 - - [[maybe_unused]] int wCase = - 0; // debug aid - // 0 = time constant OK, result OK - // 1 = short time constant (steady state sln used), result OK - // 2 = time constant OK, result limited - // 3 = short time constant (steady state sln used), result clamped - - double dryAirMassEff = - dryAirMass * i.zn_HIRatio; // effective dry air mass - // may be adjusted below re short time steps - float f = airX * Top.tp_subhrDur / i.zn_HIRatio; - if (f > 1.f) { - dryAirMassEff *= f; - f = 1.f; - wCase += 1; - } - double _wz = mw * Top.tp_subhrDur / dryAirMassEff + wzls * (1. - f); - - double XLGain = - 0.; // excess gain to space due to "condensation" or "evaporation", Btuh - // if predicted humidity ratio is physically impossible, assume - // magic (sensible) heat transfer - // + = into zone (= "condensation") - double wSat = psyHumRat3(_tz); // maximum w air at this temp can hold - double wzClamp = bracket(double(PsyWmin), _wz, wSat); - if (_wz != wzClamp) { - XLGain = (_wz - wzClamp) * dryAirMassEff * PsyHCondWtr / Top.tp_subhrDur; - _wz = wzClamp; - wCase += 2; // limits applied - } - if (pXLGain) - *pXLGain = XLGain; - - if (bFinal) { // set ZNR members - zn_rho = rho; - zn_dryAirMass = dryAirMass; - zn_dryAirMassEff = dryAirMassEff; - zn_ivAirX = ivAirX; - zn_airX = airX; - - // modified airX re convection coefficients - if (!i.zn_hcAirXIsSet) // if not fixed by user input - { - i.zn_hcAirX = zn_airX; // note: does NOT include zn_hpwhAirX - if (zn_IsAirHVACActive() && !zn_HasAirHVAC()) - i.zn_hcAirX += 4.8f; // additional "virtual" air flow for non-air zones - } - - wz = _wz; - znXLGain = XLGain; // zone total excess latent gain, Btuh - - // latent gains to space - zn_qlHvac = zn_sysAirI.af_QLat(wzls); - if (fabs(zn_qlHvac) < .1) - zn_qlHvac = 0.; // drop tiny values (re report aesthetics) -#if 0 && defined(_DEBUG) + if (_tz < -98.) + _tz = tz; + + double rho = psyDenMoistAir(_tz, wzls); // moist air density, lbm/ft3 + double dryAirMass = max(i.znVol * psyDenDryAir2( rho, wzls), .1); // dry air mass, lbm + + // air changes due to infil + vent + // includes flows induced by ducts / HVAC + // but not those flows + // TODO: improve other infil models + double nonAnIVAmf = zn_NonAnIVAmf( dryAirMass); + double ivAirX = (zn_AnAmf() + nonAnIVAmf) / dryAirMass; + if (ivAirX < 0.f) + ivAirX = 0.f; + + double amfSys = zn_ductLkI.af_amf + zn_sysAirI.af_amf + amfX; + double airX = ivAirX + amfSys / dryAirMass; + + // internal gain + double mwIG = znLGain / PsyHCondWtr; // latent internal gains, lbm/hr + + // non-airnet infil, lbm/hr + double mwInf = nonAnIVAmf * Top.wOSh; + + // IZXFER (airnet) gains not including duct leakage and HVAC + double mwAN = (1.f - zn_fVent) * zn_airNetI[0].af_Wmf() + + (zn_fVent)*zn_airNetI[1].af_Wmf(); + + double mw = mwIG + mwInf + mwAN + mwX // total water vapor mass flow rate, lbm/hr + + zn_ductLkI.af_Wmf() + zn_sysAirI.af_Wmf(); + + // TODO: HPWH moisture removal? 2-16 + + [[maybe_unused]] int wCase = 0; // debug aid + // 0 = time constant OK, result OK + // 1 = short time constant (steady state sln used), result OK + // 2 = time constant OK, result limited + // 3 = short time constant (steady state sln used), result clamped + + double dryAirMassEff = dryAirMass * i.zn_HIRatio; // effective dry air mass + // may be adjusted below re short time steps + float f = airX * Top.tp_subhrDur / i.zn_HIRatio; + if (f > 1.f) + { dryAirMassEff *= f; + f = 1.f; + wCase += 1; + } + double _wz = mw * Top.tp_subhrDur / dryAirMassEff + wzls * (1. - f); + + double XLGain = 0.; // excess gain to space due to "condensation" or "evaporation", Btuh + // if predicted humidity ratio is physically impossible, assume + // magic (sensible) heat transfer + // + = into zone (= "condensation") + double wSat = psyHumRat3(_tz); // maximum w air at this temp can hold + double wzClamp = bracket(double(PsyWmin), _wz, wSat); + if (_wz != wzClamp) + { XLGain = (_wz - wzClamp) * dryAirMassEff * PsyHCondWtr / Top.tp_subhrDur; + _wz = wzClamp; + wCase += 2; // limits applied + } + if (pXLGain) + *pXLGain = XLGain; + + if (bFinal) + { // set ZNR members + zn_rho = rho; + zn_dryAirMass = dryAirMass; + zn_dryAirMassEff = dryAirMassEff; + zn_ivAirX = ivAirX; + zn_airX = airX; + + // modified airX re convection coefficients + if (!i.zn_hcAirXIsSet) // if not fixed by user input + { i.zn_hcAirX = zn_airX; // note: does NOT include zn_hpwhAirX + if (zn_IsAirHVACActive() && !zn_HasAirHVAC()) + i.zn_hcAirX += 4.8f; // additional "virtual" air flow for non-air zones + } + + wz = _wz; + znXLGain = XLGain; // zone total excess latent gain, Btuh + + // latent gains to space + zn_qlHvac = zn_sysAirI.af_QLat(wzls); + if (fabs(zn_qlHvac) < .1) + zn_qlHvac = 0.; // drop tiny values (re report aesthetics) +#if 0 && defined( _DEBUG) if (zn_qlHvac > 0.) printf("\nqlHvac > 0"); #endif - zn_qlIz = (1.f - zn_fVent) * zn_airNetI[0].af_QLat(wzls) + - (zn_fVent)*zn_airNetI[1].af_QLat(wzls) + zn_ductLkI.af_QLat(wzls); + zn_qlIz = (1.f - zn_fVent) * zn_airNetI[0].af_QLat(wzls) + + (zn_fVent)*zn_airNetI[1].af_QLat(wzls) + + zn_ductLkI.af_QLat(wzls); - zn_twb = psyTWetBulb(_tz, _wz); - zn_relHum = psyRelHum3(_wz, wSat); + zn_twb = psyTWetBulb(_tz, _wz); + zn_relHum = psyRelHum3(_wz, wSat); - // zone humidity ratio now known - // finalize return air state - zn_sysAirO.as_w = wz; + // zone humidity ratio now known + // finalize return air state + zn_sysAirO.as_w = wz; #if 0 x float relHum = psyRelHum2(tz, zn_twb); @@ -1857,7 +1817,7 @@ x if (tz > 50 && fabs(zn_relHum - relHum) > .0001) x printf("hit"); #endif -#if 0 && defined(_DEBUG) +#if 0 && defined( _DEBUG) x moisture balance experiment x generally does not balance, 5 - 10 - 2012 x double znLDelta = zn_dryAirMass * (wz - wzls) * PsyHCondWtr; @@ -1866,584 +1826,602 @@ x if (frDiff(znLDelta, znLX) > .01) x printf("Mismatch\n"); #endif -#if defined(DEBUGDUMP) - if (DbDo(dbdZM)) { - DbPrintf("%s W: mwIG=%0.3f mwInf=%0.3f mwAN=%0.3f mwDuctLk=%0.3f " - "mwSys=%0.3f mwSum=%0.3f\n" - " tdb=%0.2f airX=%0.3f hcAirX=%0.3f dryAirMass=%0.2f " - "XLGain=%0.2f W=%0.6f twb=%0.2f rh=%0.4f\n", - Name(), mwIG, mwInf, mwAN, zn_ductLkI.af_Wmf(), - zn_sysAirI.af_Wmf(), mw, _tz, zn_airX, i.zn_hcAirX, - zn_dryAirMass, znXLGain, wz, zn_twb, zn_relHum); - DbPrintf(" rho=%0.4f rho0ls=%0.4f wzls=%0.6f dryAirMassEff=%0.2f " - "qlHvac=%0.2f qlIz=%0.2f\n", - zn_rho, zn_rho0ls, wzls, zn_dryAirMassEff, zn_qlHvac, zn_qlIz); +#if defined( DEBUGDUMP) + if (DbDo(dbdZM)) + { + DbPrintf("%s W: mwIG=%0.3f mwInf=%0.3f mwAN=%0.3f mwDuctLk=%0.3f mwSys=%0.3f mwSum=%0.3f\n" + " tdb=%0.2f airX=%0.3f hcAirX=%0.3f dryAirMass=%0.2f XLGain=%0.2f W=%0.6f twb=%0.2f rh=%0.4f\n", + Name(), mwIG, mwInf, mwAN, zn_ductLkI.af_Wmf(), zn_sysAirI.af_Wmf(), mw, + _tz, zn_airX, i.zn_hcAirX, zn_dryAirMass, znXLGain, wz, zn_twb, zn_relHum); + DbPrintf(" rho=%0.4f rho0ls=%0.4f wzls=%0.6f dryAirMassEff=%0.2f qlHvac=%0.2f qlIz=%0.2f\n", + zn_rho, zn_rho0ls, wzls, zn_dryAirMassEff, zn_qlHvac, zn_qlIz); #endif - } - } // bFinal - return _wz; + } + } // bFinal + + return _wz; -} // ZNR::zn_AirXMoistureBal +} // ZNR::zn_AirXMoistureBal //---------------------------------------------------------------------------------- -#if defined(OLDHUM) -RC ZNR::zn_AirX( // total air exchanges etc - double amfX /*=0.*/, // additional air flow, lbm - double _tz /*=-99.*/) // zone air dry bulb, F - // default = current tz -{ - RC rc = RCOK; - - if (_tz < -98.) - _tz = tz; - - zn_rho = psyDenMoistAir(_tz, wzls); // moist air density - zn_dryAirMass = - max(i.znVol * psyDenDryAir2(zn_rho, wzls), .1); // dry air mass, lbm - zn_dryAirMassEff = - zn_dryAirMass; // effective dry air mass, lbm - // zn_AirXMoistureBal() may adjust re short time steps - - // TODO: improve other infil models? - zn_ivAirX = (zn_AnAmf() + zn_NonAnIVAmf()) / zn_dryAirMass; - // air changes due to infil + vent - // includes flows induced by ducts / HVAC - // but not those flows - if (zn_ivAirX < 0.f) - zn_ivAirX = 0.f; - - double amfSys = zn_ductLkI.af_amf + zn_sysAirI.af_amf + amfX; - zn_airX = zn_ivAirX + amfSys / zn_dryAirMass; - - // modified airX re convection coefficients - if (!i.zn_hcAirXIsSet) // if not fixed by user input - { - i.zn_hcAirX = zn_airX; // note: does NOT include zn_hpwhAirX - if (zn_IsAirHVACActive() && !zn_HasAirHVAC()) - i.zn_hcAirX += 4.8f; // additional "virtual" air flow for non-air zones - } - - return rc; -} // ZNR::zn_AirX +#if defined( OLDHUM) +RC ZNR::zn_AirX( // total air exchanges etc + double amfX /*=0.*/, // additional air flow, lbm + double _tz /*=-99.*/) // zone air dry bulb, F + // default = current tz +{ + RC rc = RCOK; + + if (_tz < -98.) + _tz = tz; + + zn_rho = psyDenMoistAir(_tz, wzls); // moist air density + zn_dryAirMass = max(i.znVol * psyDenDryAir2(zn_rho, wzls), .1); // dry air mass, lbm + zn_dryAirMassEff = zn_dryAirMass; // effective dry air mass, lbm + // zn_AirXMoistureBal() may adjust re short time steps + + // TODO: improve other infil models? + zn_ivAirX = (zn_AnAmf() + zn_NonAnIVAmf()) / zn_dryAirMass; + // air changes due to infil + vent + // includes flows induced by ducts / HVAC + // but not those flows + if (zn_ivAirX < 0.f) + zn_ivAirX = 0.f; + + double amfSys = zn_ductLkI.af_amf + zn_sysAirI.af_amf + amfX; + zn_airX = zn_ivAirX + amfSys / zn_dryAirMass; + + // modified airX re convection coefficients + if (!i.zn_hcAirXIsSet) // if not fixed by user input + { + i.zn_hcAirX = zn_airX; // note: does NOT include zn_hpwhAirX + if (zn_IsAirHVACActive() && !zn_HasAirHVAC()) + i.zn_hcAirX += 4.8f; // additional "virtual" air flow for non-air zones + } + + return rc; +} // ZNR::zn_AirX //----------------------------------------------------------------------------- -double ZNR::zn_HumRat( // zone moisture ratio - double amfX, // additional system flow (e.g. air handler), lbm/h - double mwX, // air handler moisture flow, lbm/hr - double _tz, double *pXLGain /*=nullptr*/) +double ZNR::zn_HumRat( // zone moisture ratio + double amfX, // additional system flow (e.g. air handler), lbm/h + double mwX, // air handler moisture flow, lbm/hr + double _tz, + double* pXLGain /*=nullptr*/) -// returns zone humidity ratio + // returns zone humidity ratio -// see also znW() re CNE zone moisture balance + + // see also znW() re CNE zone moisture balance { - if (_tz < -98.) - _tz = tz; + if (_tz < -98.) + _tz = tz; #if 1 - RC rc = zn_AirX(amfX, _tz); + RC rc = zn_AirX(amfX, _tz); #else - double rho = psyDenMoistAir(_tz, wzls); // moist air density - double dryAirMass = - max(i.znVol * psyDenDryAir2(rho, wzls), .1); // dry air mass, lbm - zn_dryAirMass = dryAirMass; - zn_dryAirMassEff = - dryAirMass; // effective dry air mass, lbm - // zn_AirXMoistureBal() may adjust re short time steps - - // TODO: improve other infil models? - zn_ivAirX = (zn_AnAmf() + zn_NonAnIVAmf()) / dryAirMass; - // air changes due to infil + vent - // includes flows induced by ducts / HVAC - // but not those flows - if (zn_ivAirX < 0.f) - zn_ivAirX = 0.f; - - double amfSys = zn_ductLkI.af_amf + zn_sysAirI.af_amf + amfX; - zn_airX = zn_ivAirX + amfSys / dryAirMass; - - // modified airX re convection coefficients - if (!i.zn_hcAirXIsSet) // if not fixed by user input - { - i.zn_hcAirX = zn_airX; // note: does NOT include zn_hpwhAirX - if (zn_IsAirHVACActive() && !zn_HasAirHVAC()) - i.zn_hcAirX += 4.8f; // additional "virtual" air flow for non-air zones - } -#endif - - // internal gain - double mwIG = znLGain / PsyHCondWtr; // latent internal gains, lbm/hr - - // non-airnet infil, lbm/hr - double mwInf = zn_NonAnIVAmf() * Top.wOSh; - - // IZXFER (airnet) gains not including duct leakage and HVAC - double mwAN = (1.f - zn_fVent) * zn_airNetI[0].af_Wmf() + - (zn_fVent)*zn_airNetI[1].af_Wmf(); - - double mw = mwIG + mwInf + mwAN + - mwX // total water vapor mass flow rate, lbm/hr - + zn_ductLkI.af_Wmf() + zn_sysAirI.af_Wmf(); - - // TODO: HPWH moisture removal? 2-16 - - int wCase = - 0; // debug aid - // 0 = time constant OK, result OK - // 1 = short time constant (steady state sln used), result OK - // 2 = time constant OK, result limited - // 3 = short time constant (steady state sln used), result clamped - - float f = zn_airX * Top.tp_subhrDur / i.zn_HIRatio; - if (f > 1.f) { - zn_dryAirMassEff *= f; - f = 1.f; - wCase += 1; - } - double _wz = mw * Top.tp_subhrDur / (zn_dryAirMassEff * i.zn_HIRatio) + - wzls * (1. - f); - - double wSat = psyHumRat3(_tz); // maximum w air at this temp can hold - double XLGain = - 0.; // excess gain to space due to "condensation" or "evaporation", Btuh - // if predicted humidity ratio is physically impossible, assume - // magic (sensible) heat transfer - // + = into zone (= "condensation") - double wzClamp = bracket(double(PsyWmin), _wz, wSat); - if (_wz != wzClamp) { - XLGain = (_wz - wzClamp) * zn_dryAirMassEff * i.zn_HIRatio * PsyHCondWtr / - Top.tp_subhrDur; - _wz = wzClamp; - wCase += 2; // limits applied - } - - if (pXLGain) - *pXLGain = XLGain; - - return _wz; - -} // ZNR::zn_HumRat + double rho = psyDenMoistAir(_tz, wzls); // moist air density + double dryAirMass = max(i.znVol * psyDenDryAir2(rho, wzls), .1); // dry air mass, lbm + zn_dryAirMass = dryAirMass; + zn_dryAirMassEff = dryAirMass; // effective dry air mass, lbm + // zn_AirXMoistureBal() may adjust re short time steps + + // TODO: improve other infil models? + zn_ivAirX = (zn_AnAmf() + zn_NonAnIVAmf()) / dryAirMass; + // air changes due to infil + vent + // includes flows induced by ducts / HVAC + // but not those flows + if (zn_ivAirX < 0.f) + zn_ivAirX = 0.f; + + double amfSys = zn_ductLkI.af_amf + zn_sysAirI.af_amf + amfX; + zn_airX = zn_ivAirX + amfSys / dryAirMass; + + // modified airX re convection coefficients + if (!i.zn_hcAirXIsSet) // if not fixed by user input + { + i.zn_hcAirX = zn_airX; // note: does NOT include zn_hpwhAirX + if (zn_IsAirHVACActive() && !zn_HasAirHVAC()) + i.zn_hcAirX += 4.8f; // additional "virtual" air flow for non-air zones + } +#endif + + // internal gain + double mwIG = znLGain / PsyHCondWtr; // latent internal gains, lbm/hr + + // non-airnet infil, lbm/hr + double mwInf = zn_NonAnIVAmf() * Top.wOSh; + + // IZXFER (airnet) gains not including duct leakage and HVAC + double mwAN = (1.f - zn_fVent) * zn_airNetI[0].af_Wmf() + + (zn_fVent)*zn_airNetI[1].af_Wmf(); + + double mw = mwIG + mwInf + mwAN + mwX // total water vapor mass flow rate, lbm/hr + + zn_ductLkI.af_Wmf() + zn_sysAirI.af_Wmf(); + + // TODO: HPWH moisture removal? 2-16 + + int wCase = 0; // debug aid + // 0 = time constant OK, result OK + // 1 = short time constant (steady state sln used), result OK + // 2 = time constant OK, result limited + // 3 = short time constant (steady state sln used), result clamped + + + float f = zn_airX * Top.tp_subhrDur / i.zn_HIRatio; + if (f > 1.f) + { + zn_dryAirMassEff *= f; + f = 1.f; + wCase += 1; + } + double _wz = mw * Top.tp_subhrDur / (zn_dryAirMassEff * i.zn_HIRatio) + wzls * (1. - f); + + double wSat = psyHumRat3(_tz); // maximum w air at this temp can hold + double XLGain = 0.; // excess gain to space due to "condensation" or "evaporation", Btuh + // if predicted humidity ratio is physically impossible, assume + // magic (sensible) heat transfer + // + = into zone (= "condensation") + double wzClamp = bracket(double(PsyWmin), _wz, wSat); + if (_wz != wzClamp) + { + XLGain = (_wz - wzClamp) * zn_dryAirMassEff * i.zn_HIRatio * PsyHCondWtr / Top.tp_subhrDur; + _wz = wzClamp; + wCase += 2; // limits applied + } + + if (pXLGain) + *pXLGain = XLGain; + + return _wz; + +} // ZNR::zn_HumRat #endif //------------------------------------------------------------------------------ -RC ZNR::zn_ComfortCR() // calculate comfort conditions, conv/radiant model +RC ZNR::zn_ComfortCR() // calculate comfort conditions, conv/radiant model // sets zn_comfPMV7730 and zn_comfPPD7730 { - RC rc = RCOK; + RC rc = RCOK; #ifdef COMFORT_MODEL - if (zn_pComf) { - float wComf; - if (i.znComfUseZoneRH) { - wComf = wz; - i.znComfRh = zn_relHum; - } else - wComf = psyHumRat2(tz, i.znComfRh); - - int ret = zn_pComf->CalcPMV_IP(tz, tr, wComf, i.znComfAirV, i.znComfMet, - i.znComfClo); - if (ret == 0) { - zn_comfPMV7730 = zn_pComf->GetPMV(); - zn_comfPPD7730 = zn_pComf->GetPPD() * 100.f; - } else - rc = errCrit(WRN, "Zone '%s': Comfort calculation failure", Name()); - } -#endif - return rc; -} // ZNR::zn_ComfortCR + if (zn_pComf) + { + float wComf; + if (i.znComfUseZoneRH) + { wComf = wz; + i.znComfRh = zn_relHum; + } + else + wComf = psyHumRat2( tz, i.znComfRh); + + int ret = zn_pComf->CalcPMV_IP( tz, tr, + wComf, i.znComfAirV, + i.znComfMet, i.znComfClo); + if (ret==0) + { zn_comfPMV7730 = zn_pComf->GetPMV(); + zn_comfPPD7730 = zn_pComf->GetPPD() * 100.f; + } + else + rc = errCrit( WRN, "Zone '%s': Comfort calculation failure", Name()); + } +#endif + return rc; +} // ZNR::zn_ComfortCR //----------------------------------------------------------------------------- -bool ZNR::zn_IsAirHVACActive() const // determine air motion +bool ZNR::zn_IsAirHVACActive() const // determine air motion // used re convective coefficient determination // see SBC::sb_HCZone case C_CONVMODELCH_ASHRAE // returns true iff air system is operating (causing air motion) // Note: relies on zn_qsHvac not initialized (prior step value persists) { - // TODO: could enhance re other air movers (e.g. whole house fans) - return zn_qsHvac != 0.; -} // ZNR::zn_IsAirMovingMech + // TODO: could enhance re other air movers (e.g. whole house fans) + return zn_qsHvac != 0.; +} // ZNR::zn_IsAirMovingMech //============================================================================= -/*virtual*/ RSYS::~RSYS() { - delete rs_pRgiHtg[0]; - delete rs_pRgiHtg[1]; - rs_pRgiHtg[0] = rs_pRgiHtg[1] = nullptr; - delete rs_pRgiClg; - rs_pRgiClg = nullptr; - delete rs_pCHDHW; - rs_pCHDHW = nullptr; - -} // RSYS::~RSYS - //---------------------------------------------------------------------------- -/*virtual*/ void RSYS::Copy(const record *pSrc, int options /*=0*/) { - rs_desc.Release(); - record::Copy(pSrc, options); - rs_desc.FixAfterCopy(); -} // RSYS::Copy -//------------------------------------------------------------------------------- -RC RSYS::rs_CkF() { - static_assert(RSYSMODES == - rsmCOUNT - 1); // cndefn.h #define must be consistent - // with cnrecs.def enum - int rc = RCOK; - - rc |= rs_CkFHeating(); - - rc |= rs_CkFCooling(); - - // OAV - if (rs_OAVType == C_RSYSOAVTYCH_NONE) - rc |= disallowN("when rsOAVType=None", RSYS_OAVRELIEFZI, RSYS_OAVTDBINLET, - RSYS_OAVTDIFF, RSYS_OAVAVFDS, RSYS_OAVAVFMINF, - RSYS_OAVFANPWR, 0); - else { - const char *whenOAV = "when rsOAVType is given"; - rc |= requireN(whenOAV, RSYS_OAVRELIEFZI, RSYS_OAVAVFDS, 0); - if (IsVal(RSYS_OAVTDIFF) && rs_OAVTdiff < 2.f) - oWarn("Dubious rsOAVTdiff (%0.2f F) may cause inadvertent vent heating." - "\n rsOAVTdiff is typically >= 5 F to allow for fan heat and " - "duct gains", - rs_OAVTdiff); - // rsOAVFanPwr (W/cfm): error if >5, warn if >2 - rc |= limitCheck(RSYS_OAVFANPWR, 0., 5., 0., 2.); - } - - return rc; -} // RSYS::rs_CkF -//----------------------------------------------------------------------------- -RC RSYS::rs_CkFHeating() { - RC rc = RCOK; +/*virtual*/ RSYS::~RSYS() +{ + delete rs_pRgiHtg[0]; + delete rs_pRgiHtg[1]; + rs_pRgiHtg[0] = rs_pRgiHtg[1] = nullptr; + delete rs_pRgiClg; + rs_pRgiClg = nullptr; + delete rs_pCHDHW; + rs_pCHDHW = nullptr; - rc |= rs_CkFAuxHeat(); // check aux heat inputs +} // RSYS::~RSYS +//---------------------------------------------------------------------------- +/*virtual*/ void RSYS::Copy( const record* pSrc, int options/*=0*/) +{ + rs_desc.Release(); + record::Copy( pSrc, options); + rs_desc.FixAfterCopy(); +} // RSYS::Copy +//------------------------------------------------------------------------------- +RC RSYS::rs_CkF() +{ + static_assert(RSYSMODES == rsmCOUNT - 1); // cndefn.h #define must be consistent + // with cnrecs.def enum + int rc = RCOK; + + rc |= rs_CkFHeating(); + + rc |= rs_CkFCooling(); + + // OAV + if (rs_OAVType == C_RSYSOAVTYCH_NONE) + rc |= disallowN( "when rsOAVType=None", + RSYS_OAVRELIEFZI, RSYS_OAVTDBINLET, RSYS_OAVTDIFF, + RSYS_OAVAVFDS, RSYS_OAVAVFMINF, RSYS_OAVFANPWR, + 0); + else + { const char* whenOAV = "when rsOAVType is given"; + rc |= requireN( whenOAV, RSYS_OAVRELIEFZI, RSYS_OAVAVFDS, 0); + if (IsVal( RSYS_OAVTDIFF) && rs_OAVTdiff < 2.f) + oWarn( "Dubious rsOAVTdiff (%0.2f F) may cause inadvertent vent heating." + "\n rsOAVTdiff is typically >= 5 F to allow for fan heat and duct gains", + rs_OAVTdiff); + // rsOAVFanPwr (W/cfm): error if >5, warn if >2 + rc |= limitCheck( RSYS_OAVFANPWR, 0., 5., 0., 2.); + } -#if 0 + return rc; +} // RSYS::rs_CkF +//----------------------------------------------------------------------------- +RC RSYS::rs_CkFHeating() +{ + RC rc = RCOK; + + rc |= rs_CkFAuxHeat(); // check aux heat inputs + + +#if 0 // all heating non-ASHP fields EXCEPT RSYS_CAPH, RSYS_FANPWRH, RSYS_FXCAPHTARG // = heating fields disallowed for FANCOIL static constexpr int16_t htgFNs[]{ RSYS_AFUE, RSYS_CAPNOMH, RSYS_TDDESH, RSYS_FEFFH, 0 }; #endif - // ASHP heating FNs (all types) - static constexpr int16_t ASHP_HtgFNs[]{RSYS_HSPF, RSYS_CAP47, - RSYS_COP47, RSYS_CAP35, - RSYS_COP35, RSYS_CAP17, - RSYS_COP17, RSYS_ASHPLOCKOUTT, - RSYS_CAPRAT1747, RSYS_CAPRAT9547, - RSYS_CAPRATCH, RSYS_CDH, - RSYS_DEFROSTMODEL, 0}; - - // ASHPVC (VCHP2) active FNs - static constexpr int16_t ASHPVC_HtgFNs[]{RSYS_LOADFMIN47, - RSYS_LOADFMIN17, - RSYS_LOADFMIN05, - RSYS_COPMIN47, - RSYS_COPMIN35, - RSYS_COPMIN17, - RSYS_COPMIN05, - RSYS_CAPRAT0547, - RSYS_CAP05, - RSYS_COP05, - 0}; - - if (!rs_CanHeat()) { - const char *whenTyNoHt = strtprintf( - "when rsType=%s (heating not available)", getChoiTx(RSYS_TYPE)); - rc |= ignoreX(whenTyNoHt, RSYS_CAPH, RSYS_FANPWRH, RSYS_FXCAPHTARG, - RSYS_AFUE, RSYS_CAPNOMH, RSYS_TDDESH, RSYS_FEFFH, - RSYS_CHDHWSYSI, ASHP_HtgFNs, ASHPVC_HtgFNs); - FldSet(RSYS_CAPNOMH, 0.f); // insurance - - return rc; - } - const char *whenTy = strtprintf("when rsType=%s", getChoiTx(RSYS_TYPE)); - - if (!rs_IsCHDHW()) { - disallow(whenTy, RSYS_CHDHWSYSI); - } - - if (rs_IsFanCoil()) { // fancoil htg - rc |= require(whenTy, RSYS_CAPH); - rc |= ignoreX(whenTy, RSYS_AFUE, RSYS_CAPNOMH, RSYS_FEFFH, ASHP_HtgFNs, - ASHPVC_HtgFNs); - rs_AFUE = 0.f; - } else if (rs_IsWSHP()) { - rc |= requireN(whenTy, RSYS_TDBOUT, 0); - - rc |= disallowN(whenTy, RSYS_AFUE, 0); - - if (!IsSet(RSYS_CAPH) && !IsSet(RSYS_CAP95)) - rc |= - oer("at least one of rsCapH and rsCapC must be specified %s", whenTy); - - if (IsSetCount(RSYS_CAPH, RSYS_CAP95, RSYS_CAPRATCH, 0) == 3 && - !(IsAusz(RSYS_CAPH) && IsAusz(RSYS_CAP95))) - // cannot give all 3 values unless both caps are autosized - rc |= oer("rsCapH, rsCapC, and rsCapRatCH cannot all be specfied %s", - whenTy); - - rc |= ignoreX(whenTy, RSYS_HSPF, RSYS_CAP47, RSYS_CAP35, RSYS_COP35, - RSYS_CAP17, RSYS_COP17, RSYS_ASHPLOCKOUTT, RSYS_CAPRAT1747, - RSYS_CAPRAT9547, RSYS_DEFROSTMODEL, ASHPVC_HtgFNs); - } else if (rs_IsASHP()) { - if (!IsSet(RSYS_CAP47) && !IsSet(RSYS_CAP95)) - rc |= oer("at least one of rsCap47 and rsCapC must be specified %s", - whenTy); - - if (IsSetCount(RSYS_CAP47, RSYS_CAP95, RSYS_CAPRAT9547, 0) == 3 && - !(IsAusz(RSYS_CAP47) && IsAusz(RSYS_CAP95))) - // cannot give all 3 values unless both caps are autosized - rc |= oer("rsCap47, rsCapC, and rsCapRat9547 cannot all be specfied %s", - whenTy); - - if (rs_IsASHPHydronic()) { // ASHPHydronic: air-to-water heat pump - // approximated with air-to-air model - rc |= requireN(whenTy, RSYS_COP47, RSYS_COP17, 0); - rc |= disallowN(whenTy, RSYS_HSPF, RSYS_CAPH, RSYS_AFUE, - RSYS_DEFROSTMODEL, 0); - if (!IsSet(RSYS_FANPWRH)) - rs_fanPwrH = 0.f; // hydronic: no fan power - } else { // ASHP other than ASHPHydronic - - rc |= disallowN(whenTy, RSYS_CAPH, RSYS_AFUE, 0); - - if (rs_IsASHPPkgRoom()) { - rc |= disallowN(whenTy, RSYS_HSPF, RSYS_CAP05, RSYS_COP05, RSYS_CAP17, - RSYS_COP17, RSYS_CAP35, RSYS_COP35, 0); - rc |= requireN(whenTy, RSYS_COP47, 0); - - if (!IsSet(RSYS_FANPWRH)) - rs_fanPwrH = 0.f; // fan power included in primary by default - - // other defaults derived in rs_TopRSys1() - } else { // ASHP (non-hydronic, non-pkgroom): only HSPF required - // capacities defaulted from cooling cap95 - // COPs defaulted from HSPF - // if both COP47 and COP17 are specified, HSPF is not used - - rc |= requireN(whenTy, RSYS_HSPF, 0); - - if (IsAusz(RSYS_CAP47)) - rc |= disallowN("when rsCap47 is AUTOSIZE", RSYS_CAP05, RSYS_CAP17, - RSYS_CAP35, 0); - else { - if (IsSet(RSYS_CAP17)) - rc |= disallowN("when rsCap17 is given", RSYS_CAPRAT1747, 0); - if (IsSet(RSYS_CAP05)) - rc |= disallowN("when rsCap05 is given", RSYS_CAPRAT0547, 0); - } - - if (!rs_IsASHPVC()) { - rc |= ignore("when rsType is not ASHPVC (VCHP2)", ASHPVC_HtgFNs); - rs_loadFMin05 = rs_loadFMin17 = rs_loadFMin47 = 1.f; - } else { // default loadFMins - FldCopyIf(RSYS_LOADFMIN47, RSYS_LOADFMIN17); - FldCopyIf(RSYS_LOADFMIN47, RSYS_LOADFMIN05); - } - } - } - - // all air source heat pumps - if (!IsAusz(RSYS_CAP47)) - // rs_cap47 not AUTOSIZEd (altho may be expression), use it as rs_capNomH - // default - // see DefaultCapNomsIf() - FldCopyIf(RSYS_CAP47, RSYS_CAPNOMH); - } else if (rs_IsCHDHW()) { // combined heat and DHW - rc |= requireX(whenTy, RSYS_CHDHWSYSI); - rc |= disallowX(whenTy, RSYS_TDDESH); - if (IsAusz(RSYS_CAPH)) - rc |= oer("rsCapH cannot be AUTOSIZE %s", whenTy); - else - rc |= ignoreX(whenTy, RSYS_CAPH); - // rs_CdH? - rc |= disallowX("when rsType is CombinedHeatDHW or ACCombinedHeatDHW", - ASHP_HtgFNs, ASHPVC_HtgFNs); - } else { // not CHDHW or HP of any type - rc |= disallowX( - "when rsType is not ASHP, ASHPHydronic, ASHPPkgRoom, or ASHPVC (VCHP2)", - ASHP_HtgFNs, ASHPVC_HtgFNs); - - // default AFUE (CULT default = 0) - if (!IsSet(RSYS_AFUE)) - rs_AFUE = rs_IsElecHeat() ? 1.f : 0.9; - - if (!IsAusz(RSYS_CAPH)) - // rs_capH not AUTOSIZEd (altho may be expression), use it as rs_capNomH - // default - FldCopyIf(RSYS_CAPH, RSYS_CAPNOMH); - } - - return rc; -} // RSYS::rs_CkFHeating + // ASHP heating FNs (all types) + static constexpr int16_t ASHP_HtgFNs[]{ RSYS_HSPF, RSYS_CAP47, RSYS_COP47, + RSYS_CAP35, RSYS_COP35, RSYS_CAP17, RSYS_COP17, RSYS_ASHPLOCKOUTT, + RSYS_CAPRAT1747, RSYS_CAPRAT9547, RSYS_CAPRATCH, RSYS_CDH, + RSYS_DEFROSTMODEL, 0 }; + + // ASHPVC (VCHP2) active FNs + static constexpr int16_t ASHPVC_HtgFNs[]{ RSYS_LOADFMIN47, RSYS_LOADFMIN17, RSYS_LOADFMIN05, + RSYS_COPMIN47, RSYS_COPMIN35, RSYS_COPMIN17, RSYS_COPMIN05, + RSYS_CAPRAT0547, RSYS_CAP05, RSYS_COP05, 0 }; + + + if (!rs_CanHeat()) + { + const char* whenTyNoHt = strtprintf("when rsType=%s (heating not available)", + getChoiTx(RSYS_TYPE)); + rc |= ignoreX(whenTyNoHt, RSYS_CAPH, RSYS_FANPWRH, RSYS_FXCAPHTARG, + RSYS_AFUE, RSYS_CAPNOMH, RSYS_TDDESH, RSYS_FEFFH, RSYS_CHDHWSYSI, + ASHP_HtgFNs, ASHPVC_HtgFNs); + FldSet(RSYS_CAPNOMH, 0.f); // insurance + + return rc; + } + const char* whenTy = strtprintf("when rsType=%s", getChoiTx(RSYS_TYPE)); + + if (!rs_IsCHDHW()) + { + disallow(whenTy, RSYS_CHDHWSYSI); + } + + if (rs_IsFanCoil()) + { // fancoil htg + rc |= require(whenTy, RSYS_CAPH); + rc |= ignoreX(whenTy, RSYS_AFUE, RSYS_CAPNOMH, RSYS_FEFFH, + ASHP_HtgFNs, ASHPVC_HtgFNs); + rs_AFUE = 0.f; + } + else if (rs_IsWSHP()) + { + rc |= requireN(whenTy, RSYS_TDBOUT, 0); + + rc |= disallowN(whenTy, RSYS_AFUE, 0); + + if (!IsSet(RSYS_CAPH) && !IsSet(RSYS_CAP95)) + rc |= oer("at least one of rsCapH and rsCapC must be specified %s", whenTy); + + if (IsSetCount(RSYS_CAPH, RSYS_CAP95, RSYS_CAPRATCH, 0) == 3 + && !(IsAusz(RSYS_CAPH) && IsAusz(RSYS_CAP95))) + // cannot give all 3 values unless both caps are autosized + rc |= oer("rsCapH, rsCapC, and rsCapRatCH cannot all be specfied %s", whenTy); + + rc |= ignoreX(whenTy, RSYS_HSPF, RSYS_CAP47, + RSYS_CAP35, RSYS_COP35, RSYS_CAP17, RSYS_COP17, RSYS_ASHPLOCKOUTT, + RSYS_CAPRAT1747, RSYS_CAPRAT9547, RSYS_DEFROSTMODEL, ASHPVC_HtgFNs); + } + else if (rs_IsASHP()) + { + if (!IsSet(RSYS_CAP47) && !IsSet(RSYS_CAP95)) + rc |= oer("at least one of rsCap47 and rsCapC must be specified %s", whenTy); + + if (IsSetCount(RSYS_CAP47, RSYS_CAP95, RSYS_CAPRAT9547, 0) == 3 + && !(IsAusz(RSYS_CAP47) && IsAusz(RSYS_CAP95))) + // cannot give all 3 values unless both caps are autosized + rc |= oer("rsCap47, rsCapC, and rsCapRat9547 cannot all be specfied %s", whenTy); + + if (rs_IsASHPHydronic()) + { // ASHPHydronic: air-to-water heat pump + // approximated with air-to-air model + rc |= requireN(whenTy, RSYS_COP47, RSYS_COP17, 0); + rc |= disallowN(whenTy, RSYS_HSPF, RSYS_CAPH, RSYS_AFUE, RSYS_DEFROSTMODEL, 0); + if (!IsSet(RSYS_FANPWRH)) + rs_fanPwrH = 0.f; // hydronic: no fan power + } + else + { // ASHP other than ASHPHydronic + + rc |= disallowN(whenTy, RSYS_CAPH, RSYS_AFUE, 0); + + if (rs_IsASHPPkgRoom()) + { + rc |= disallowN(whenTy, RSYS_HSPF, RSYS_CAP05, RSYS_COP05, + RSYS_CAP17, RSYS_COP17, RSYS_CAP35, RSYS_COP35, 0); + rc |= requireN(whenTy, RSYS_COP47, 0); + + if (!IsSet(RSYS_FANPWRH)) + rs_fanPwrH = 0.f; // fan power included in primary by default + + // other defaults derived in rs_TopRSys1() + } + else + { // ASHP (non-hydronic, non-pkgroom): only HSPF required + // capacities defaulted from cooling cap95 + // COPs defaulted from HSPF + // if both COP47 and COP17 are specified, HSPF is not used + + rc |= requireN(whenTy, RSYS_HSPF, 0); + + if (IsAusz(RSYS_CAP47)) + rc |= disallowN("when rsCap47 is AUTOSIZE", + RSYS_CAP05, RSYS_CAP17, RSYS_CAP35, 0); + else + { if (IsSet(RSYS_CAP17)) + rc |= disallowN("when rsCap17 is given", RSYS_CAPRAT1747, 0); + if (IsSet(RSYS_CAP05)) + rc |= disallowN("when rsCap05 is given", RSYS_CAPRAT0547, 0); + } + + if (!rs_IsASHPVC()) + { rc |= ignore("when rsType is not ASHPVC (VCHP2)", + ASHPVC_HtgFNs); + rs_loadFMin05 = rs_loadFMin17 = rs_loadFMin47 = 1.f; + } + else + { // default loadFMins + FldCopyIf(RSYS_LOADFMIN47, RSYS_LOADFMIN17); + FldCopyIf(RSYS_LOADFMIN47, RSYS_LOADFMIN05); + } + } + } + + // all air source heat pumps + if (!IsAusz(RSYS_CAP47)) + // rs_cap47 not AUTOSIZEd (altho may be expression), use it as rs_capNomH default + // see DefaultCapNomsIf() + FldCopyIf(RSYS_CAP47, RSYS_CAPNOMH); + } + else if (rs_IsCHDHW()) + { // combined heat and DHW + rc |= requireX(whenTy, RSYS_CHDHWSYSI); + rc |= disallowX(whenTy, RSYS_TDDESH); + if (IsAusz( RSYS_CAPH)) + rc |= oer("rsCapH cannot be AUTOSIZE %s", whenTy); + else + rc |= ignoreX(whenTy, RSYS_CAPH); + // rs_CdH? + rc |= disallowX("when rsType is CombinedHeatDHW or ACCombinedHeatDHW", + ASHP_HtgFNs, ASHPVC_HtgFNs); + } + else + { // not CHDHW or HP of any type + rc |= disallowX("when rsType is not ASHP, ASHPHydronic, ASHPPkgRoom, or ASHPVC (VCHP2)", + ASHP_HtgFNs, ASHPVC_HtgFNs); + + // default AFUE (CULT default = 0) + if (!IsSet(RSYS_AFUE)) + rs_AFUE = rs_IsElecHeat() ? 1.f : 0.9; + + if (!IsAusz(RSYS_CAPH)) + // rs_capH not AUTOSIZEd (altho may be expression), use it as rs_capNomH default + FldCopyIf(RSYS_CAPH, RSYS_CAPNOMH); + } + + return rc; +} // RSYS::rs_CkFHeating //----------------------------------------------------------------------------- -RC RSYS::rs_CkFCooling() { - // compression cooling FNs - static constexpr int16_t Clg_FNs[] = { - RSYS_SEER, RSYS_EER95, RSYS_COP95, RSYS_CAP82, - RSYS_COP82, RSYS_CAPRAT8295, RSYS_CAP115, RSYS_COP115, - RSYS_CAPRAT11595, RSYS_FCHG, RSYS_CDC, 0}; - - // FNs meaningful only for variable capacity - // ignored for non-VC ASHP - // disallowed for others - static constexpr int16_t VC_ClgFNs[] = {RSYS_LOADFMIN115, - RSYS_LOADFMIN95, - RSYS_LOADFMIN82, - RSYS_COPMIN115, - RSYS_COPMIN95, - RSYS_COPMIN82, - 0}; - - int rc = RCOK; - - if (!rs_CanCool()) { // no cooling capability - const char *whenNoCl = strtprintf("when rsType=%s (cooling not available)", - getChoiTx(RSYS_TYPE)); - rc |= ignoreX(whenNoCl, RSYS_CAP95, RSYS_FANPWRC, RSYS_CAPNOMC, - RSYS_VFPERTON, Clg_FNs, VC_ClgFNs); - FldSet(RSYS_CAPNOMC, 0.f); // insurance - return rc; - } - - const char *whenTy = strtprintf("when rsType=%s", getChoiTx(RSYS_TYPE)); - if (rs_IsPkgRoom()) { - rc |= requireN(whenTy, RSYS_EER95, 0); - rc |= disallowN(whenTy, RSYS_SEER, RSYS_COP95, 0); - if (!IsSet(RSYS_FANPWRC)) - rs_fanPwrC = 0.f; // fan power included in primary by default - } else if (rs_IsFanCoil()) { // fancoil cooling - rc |= require(whenTy, RSYS_CAP95); - rc |= ignoreX(whenTy, Clg_FNs, VC_ClgFNs); - } else if (rs_IsWSHP()) { // note checks of RSYS_TDBOUT and RSYS_CAP95 in - // rs_CkFHeating() - - rc |= requireN(whenTy, RSYS_EER95, 0); - - rc |= ignoreX(whenTy, RSYS_SEER, VC_ClgFNs); - } else - rc |= requireN(whenTy, RSYS_SEER, 0); - - if (!IsAusz(RSYS_CAP95)) { // rs_cap95 not AUTOSIZEd (altho may be - // expression), use as rs_capNomC default - FldCopyIf(RSYS_CAP95, RSYS_CAPNOMC); - - if (IsSet(RSYS_CAP82)) - rc |= disallowN("when rsCap82 is given", RSYS_CAPRAT8295, 0); - if (IsSet(RSYS_CAP115)) - rc |= disallowN("when rsCap115 is given", RSYS_CAPRAT11595, 0); - } else { // rsCap95 is autosize - - rc |= disallowN("when rsCap95 is AUTOSIZE", RSYS_CAP82, RSYS_CAP115, 0); - } - - if (rs_IsASHPVC()) { - FldCopyIf(RSYS_LOADFMIN95, RSYS_LOADFMIN82); - FldCopyIf(RSYS_LOADFMIN95, RSYS_LOADFMIN115); - } else { - rc |= ignoreX("when rsType is not ASHPVC (VCHP2)", VC_ClgFNs); - rs_loadFMin82 = rs_loadFMin95 = rs_loadFMin115 = 1.f; - } - - return rc; - -} // RSYS::rs_CkFCooling +RC RSYS::rs_CkFCooling() +{ + // compression cooling FNs + static constexpr int16_t Clg_FNs[] = { RSYS_SEER, RSYS_EER95, RSYS_COP95, + RSYS_CAP82, RSYS_COP82, RSYS_CAPRAT8295, RSYS_CAP115, RSYS_COP115, RSYS_CAPRAT11595, + RSYS_FCHG, RSYS_CDC, 0 }; + + // FNs meaningful only for variable capacity + // ignored for non-VC ASHP + // disallowed for others + static constexpr int16_t VC_ClgFNs[] = { RSYS_LOADFMIN115, RSYS_LOADFMIN95, RSYS_LOADFMIN82, + RSYS_COPMIN115, RSYS_COPMIN95, RSYS_COPMIN82, 0 }; + + int rc = RCOK; + + if (!rs_CanCool()) + { // no cooling capability + const char* whenNoCl = strtprintf("when rsType=%s (cooling not available)", + getChoiTx(RSYS_TYPE)); + rc |= ignoreX(whenNoCl, RSYS_CAP95, RSYS_FANPWRC, RSYS_CAPNOMC, RSYS_VFPERTON, + Clg_FNs, VC_ClgFNs); + FldSet(RSYS_CAPNOMC, 0.f); // insurance + return rc; + } + + const char* whenTy = strtprintf("when rsType=%s", getChoiTx(RSYS_TYPE)); + if (rs_IsPkgRoom()) + { + rc |= requireN(whenTy, RSYS_EER95, 0); + rc |= disallowN(whenTy, RSYS_SEER, RSYS_COP95, 0); + if (!IsSet(RSYS_FANPWRC)) + rs_fanPwrC = 0.f; // fan power included in primary by default + } + else if (rs_IsFanCoil()) + { // fancoil cooling + rc |= require(whenTy, RSYS_CAP95); + rc |= ignoreX(whenTy, Clg_FNs, VC_ClgFNs); + } + else if (rs_IsWSHP()) + { // note checks of RSYS_TDBOUT and RSYS_CAP95 in rs_CkFHeating() + + rc |= requireN(whenTy, RSYS_EER95, 0); + + rc |= ignoreX(whenTy, RSYS_SEER, + VC_ClgFNs); + } + else + rc |= requireN(whenTy, RSYS_SEER, 0); + + if (!IsAusz(RSYS_CAP95)) + { // rs_cap95 not AUTOSIZEd (altho may be expression), use as rs_capNomC default + FldCopyIf(RSYS_CAP95, RSYS_CAPNOMC); + + if (IsSet(RSYS_CAP82)) + rc |= disallowN("when rsCap82 is given", RSYS_CAPRAT8295, 0); + if (IsSet(RSYS_CAP115)) + rc |= disallowN("when rsCap115 is given", RSYS_CAPRAT11595, 0); + } + else + { // rsCap95 is autosize + + rc |= disallowN("when rsCap95 is AUTOSIZE", + RSYS_CAP82, RSYS_CAP115, 0); + } + + if (rs_IsASHPVC()) + { + FldCopyIf(RSYS_LOADFMIN95, RSYS_LOADFMIN82); + FldCopyIf(RSYS_LOADFMIN95, RSYS_LOADFMIN115); + } + else + { + rc |= ignoreX("when rsType is not ASHPVC (VCHP2)", + VC_ClgFNs); + rs_loadFMin82= rs_loadFMin95 = rs_loadFMin115 = 1.f; + } + + return rc; + +} // RSYS::rs_CkFCooling //----------------------------------------------------------------------------- -RC RSYS::rs_CkFCooling2() // additional cooling checks +RC RSYS::rs_CkFCooling2() // additional cooling checks // call after all values set (including calculated defaults) // returns RCOK if all check passed // else nz value { - RC rc = RCOK; - - if (!rs_IsFanCoil() && !rs_IsWSHP() && - !rs_IsVCClg()) { // check that SEER and EER are plausible - if (rs_SEER <= rs_EER95) - rc |= oer("rsSEER (%g) must be > rsEER (%g)", rs_SEER, rs_EER95); - } - - // if (rs_VCClg()) -- no check for all rs_CanCool() types (insurance) - { // checks to prevent crazy extrapolation results - // cap82 > cap95 typically; occasional cap82 < cap95 examples seen so use - // 0.8 - rc |= rs_CkFRatio(RSYS_CAP82, RSYS_CAP95, RSYS_CAPRAT8295, 0.8f, 2.f); - // cap115 < cap95 always - rc |= rs_CkFRatio(RSYS_CAP115, RSYS_CAP95, RSYS_CAPRAT11595, .2f, 1.f); - } - - return rc; -} // RSYS::rs_CkFCooling2 + RC rc = RCOK; + + if (!rs_IsFanCoil() && !rs_IsWSHP() && !rs_IsVCClg()) + { // check that SEER and EER are plausible + if (rs_SEER <= rs_EER95) + rc |= oer("rsSEER (%g) must be > rsEER (%g)", rs_SEER, rs_EER95); + } + + // if (rs_VCClg()) -- no check for all rs_CanCool() types (insurance) + { // checks to prevent crazy extrapolation results + // cap82 > cap95 typically; occasional cap82 < cap95 examples seen so use 0.8 + rc |= rs_CkFRatio(RSYS_CAP82, RSYS_CAP95, RSYS_CAPRAT8295, 0.8f, 2.f); + // cap115 < cap95 always + rc |= rs_CkFRatio(RSYS_CAP115, RSYS_CAP95, RSYS_CAPRAT11595, .2f, 1.f); + } + + return rc; +} // RSYS::rs_CkFCooling2 //----------------------------------------------------------------------------- -RC RSYS::rs_CkFCd( // default and check Cd values - int mode) // rsmCOOL or rsmHEAT +RC RSYS::rs_CkFCd( // default and check Cd values + int mode) // rsmCOOL or rsmHEAT // returns RCOK iff rs_CdX is acceptable // else !RCOK (user input out of range) { - RC rc = RCOK; - if (mode == rsmCOOL) { - if (!IsSet(RSYS_CDC)) - rs_CdC = rs_IsVCClg() || rs_IsWSHP() ? 0.25f : 0.f; - else - rc |= limitCheckFix(RSYS_CDC, 0.f, .5f); - } else { // not rsmCOOL, assume rsmHEAT - if (!IsSet(RSYS_CDH)) - rs_CdH = - rs_IsASHPHydronic() || rs_IsASHPPkgRoom() || rs_IsASHPVC() || - rs_IsWSHP() - ? 0.25f // hydronic / pkgRoom / WSHP: no HSPF source for default - // ASHPVC: relationship to HSPF not known - : bracket(.05f, .25f - 0.2f * (rs_HSPF - 6.8f) / (10.f - 6.8f), - .25f); - else - rc |= limitCheckFix(RSYS_CDH, 0.f, .5f); - } - return rc; -} // RSYS::rs_CkFCd + RC rc = RCOK; + if (mode == rsmCOOL) + { if (!IsSet(RSYS_CDC)) + rs_CdC = rs_IsVCClg() || rs_IsWSHP() ? 0.25f : 0.f; + else + rc |= limitCheckFix(RSYS_CDC, 0.f, .5f); + } + else + { // not rsmCOOL, assume rsmHEAT + if (!IsSet(RSYS_CDH)) + rs_CdH = rs_IsASHPHydronic() || rs_IsASHPPkgRoom() || rs_IsASHPVC() || rs_IsWSHP() + ? 0.25f // hydronic / pkgRoom / WSHP: no HSPF source for default + // ASHPVC: relationship to HSPF not known + : bracket(.05f, .25f - 0.2f*(rs_HSPF - 6.8f) / (10.f - 6.8f), .25f); + else + rc |= limitCheckFix(RSYS_CDH, 0.f, .5f); + } + return rc; +} // RSYS::rs_CkFCd //----------------------------------------------------------------------------- -RC RSYS::rs_CkFAuxHeat() // check aux heat -{ - RC rc = RCOK; - rs_capAuxHInp = 0.f; - rs_effAuxH = 1.f; // insurance - if (!rs_CanHaveAuxHeat()) { - rc |= disallowN( - "when rsType is not ASHP, ASHPHydronic, ASHPPkgRoom, or VCHP2", - RSYS_TYPEAUXH, RSYS_CAPAUXH, RSYS_FXCAPAUXHTARG, RSYS_AFUEAUXH, - RSYS_CTRLAUXH, 0); - rs_AFUEAuxH = 0.f; // clear possibly confusing default - } else if (rs_typeAuxH == C_AUXHEATTY_NONE) { - rc |= disallowN("when rsTypeAuxH = None", RSYS_CTRLAUXH, RSYS_CAPAUXH, - RSYS_FXCAPAUXHTARG, RSYS_AFUEAUXH, 0); - rs_capAuxH = 0.f; - } else if (!IsAusz(RSYS_CAPAUXH) && rs_capAuxH == 0.f) { // no auxiliary - rs_typeAuxH = C_AUXHEATTY_NONE; - } else { // save input value of rs_capAuxH - // WHY: rs_capAuxH changed during HP autosize - // saved value used to restore original - rs_capAuxHInp = rs_capAuxH; - if (IsAusz(RSYS_CAPAUXH)) - rs_capAuxH = 0.f; // overwrite NANDLE - if (rs_IsFuelAuxH()) { - if (!IsSet(RSYS_AFUEAUXH)) - rs_AFUEAuxH = 0.9f; // change CULT default (1) to value - // appropriate for furnace - rs_effAuxH = rs_AFUEAuxH; - if (!IsSet(RSYS_CTRLAUXH)) - rs_ctrlAuxH = C_AUXHEATCTRL_ALT; - } - } - - return rc; -} // RSYS::rs_CkFAuxHeat +RC RSYS::rs_CkFAuxHeat() // check aux heat +{ + RC rc = RCOK; + rs_capAuxHInp = 0.f; + rs_effAuxH = 1.f; // insurance + if (!rs_CanHaveAuxHeat()) + { rc |= disallowN("when rsType is not ASHP, ASHPHydronic, ASHPPkgRoom, or VCHP2", + RSYS_TYPEAUXH, RSYS_CAPAUXH, RSYS_FXCAPAUXHTARG, RSYS_AFUEAUXH, + RSYS_CTRLAUXH, 0); + rs_AFUEAuxH = 0.f; // clear possibly confusing default + } + else if (rs_typeAuxH == C_AUXHEATTY_NONE) + { rc |= disallowN("when rsTypeAuxH = None", + RSYS_CTRLAUXH, RSYS_CAPAUXH, RSYS_FXCAPAUXHTARG, RSYS_AFUEAUXH, 0); + rs_capAuxH = 0.f; + } + else if (!IsAusz(RSYS_CAPAUXH) && rs_capAuxH == 0.f) + { // no auxiliary + rs_typeAuxH = C_AUXHEATTY_NONE; + } + else + { // save input value of rs_capAuxH + // WHY: rs_capAuxH changed during HP autosize + // saved value used to restore original + rs_capAuxHInp = rs_capAuxH; + if (IsAusz(RSYS_CAPAUXH)) + rs_capAuxH = 0.f; // overwrite NANDLE + if (rs_IsFuelAuxH()) + { + if (!IsSet(RSYS_AFUEAUXH)) + rs_AFUEAuxH = 0.9f; // change CULT default (1) to value + // appropriate for furnace + rs_effAuxH = rs_AFUEAuxH; + if (!IsSet(RSYS_CTRLAUXH)) + rs_ctrlAuxH = C_AUXHEATCTRL_ALT; + } + } + + return rc; +} // RSYS::rs_CkFAuxHeat //----------------------------------------------------------------------------- -RC RSYS::rs_CkFRatio(int fn1, // field 1 - int fn2, // field 2 - int fnRat, // alternate: ratio field - float vMin, // min allowed value for v1/v2 or vRat - float vMax) // max allowed value for v1/v2 or vRat +RC RSYS::rs_CkFRatio( + int fn1, // field 1 + int fn2, // field 2 + int fnRat, // alternate: ratio field + float vMin, // min allowed value for v1/v2 or vRat + float vMax) // max allowed value for v1/v2 or vRat // Expects either (fn1 and fn2) OR fnRat to be set (but not all 3) // Prior checking must verify that (see e.g. rs_CkF) // mutua { - RC rc = IsSet(fn1) ? limitCheckRatio(fn1, fn2, vMin, vMax) - : limitCheck(fnRat, vMin, vMax); - return rc; -} // RSYS::rs_CkFRatio + RC rc = IsSet( fn1) + ? limitCheckRatio(fn1, fn2, vMin, vMax) + : limitCheck(fnRat, vMin, vMax); + return rc; +} // RSYS::rs_CkFRatio //----------------------------------------------------------------------------- #if 0 // incomplete idea, 11-21 @@ -2463,403 +2441,427 @@ RC RSYS::rs_CheckCapAuxH() // check for sufficient aux heat capacity } // RSYS::rs_CheckCapAuxH #endif //----------------------------------------------------------------------------- -RC RSYS::rs_TopRSys1() // check RSYS, initial set up for run -{ - RC rc = RCOK; - - if (!IsSet(RSYS_TDDESH)) - rs_tdDesH = rs_IsHP() ? 30.f // lower default temp rise for ASHP - : 50.f; // (changed later for CHDHW) - - if (rs_IsASHPPkgRoom()) { - if (!IsSet(RSYS_ASHPLOCKOUTT)) - rs_ASHPLockOutT = 45.f; // pkg room ASHP: use resistance at lower temps - rc |= rs_SetupASHP(); - } - - if (rs_IsVC()) { // VCHP default motor type is BPM (others are PSC from cult) - if (!IsSet(RSYS_FAN + FAN_MOTTY)) - rs_fan.fn_motTy = C_MOTTYCH_BPM; - } - - if (rs_CanCool()) { // cooling model air flow correlations have limited - // validity range - // verify air flow 150 - 550 cfm/ton (per Proctor Engineering) - rc |= limitCheck(RSYS_VFPERTON, 150., 550.); - - // rsFanPwrC (W/cfm): error if >5, warn if >2 - rc |= limitCheck(RSYS_FANPWRC, 0., 5., 0., 2.); - - if (rs_IsFanCoil()) { // nothing to check? - - } else { // compression cooling - - rc |= rs_CkFCd(rsmCOOL); - - if (rs_IsWSHP()) { - rc |= limitCheck(RSYS_CAPRATCH, 0.3, 2.); - - } else { // default/harmonize 95 F COP - // inter-default rs_COP95 <-> rs_EER95 - // if both input, values can be different - // rs_COP95 used for VCHP2, rs_EER95 used for single speed - // EER can default from SEER - if (rs_IsPkgRoom()) { // pkg: derive SEER from EER - // abram conant fit, 6-20 - rs_SEER = 1.07f * rs_EER95; - // rs_COP95 not allowed - } else if (IsSet(RSYS_EER95)) { - if (!IsSet(RSYS_COP95)) - rs_COP95 = rs_EER95 / BtuperWh; - } else { - if (IsSet(RSYS_COP95)) - rs_EER95 = rs_COP95 * BtuperWh; - else if (!rs_IsVCClg()) { // estimate missing EER from SEER - // California ACM method - rs_EER95 = rs_SEER < 13.f ? 10.f + 0.84f * (rs_SEER - 11.5f) - : rs_SEER < 16.f ? 11.3f + 0.57f * (rs_SEER - 13.f) - : 13.f; - rs_COP95 = rs_EER95 / BtuperWh; - } - } - } - } - - // final rs_CanCool() checks: EER vs SEER and cap82 vs cap95 vs cap115 - rc |= rs_CkFCooling2(); - } - - if (IsAusz(RSYS_CAP95)) { - rs_auszC.az_active = TRUE; - rs_fxCapCAsF = 1.2f; // working oversize factor - // ensures sufficient capacity - // during autosize search - } - - if (rs_CanHeat()) { - // rsFanPwrH (W/cfm): error if >5, warn if >2 - rc |= limitCheck(RSYS_FANPWRH, 0., 5., 0., 2.); - - if (rs_IsHP()) { - rc |= rs_CkFCd(rsmHEAT); - - if (IsAusz(RSYS_CAPH) || IsAusz(RSYS_CAP47) || - IsAusz(RSYS_CAPAUXH)) { // rs_capH for WSHP, rs_cap47 for ASHP - rs_auszH.az_active = TRUE; // ASHP autosizes rs_capH - // capacities derived in rs_AuszFinal - rs_fxCapHAsF = 1.2f; // working oversize factor - } - - if (rs_IsASHP()) { - rc |= rs_CkFRatio(RSYS_CAP17, RSYS_CAP47, RSYS_CAPRAT1747, .2f, 1.2f); - rc |= rs_CkFRatio(RSYS_CAP05, RSYS_CAP47, RSYS_CAPRAT0547, .1f, 1.2f); - } - } else if (IsAusz(RSYS_CAPH)) { // other checking? - rs_auszH.az_active = TRUE; - rs_fxCapHAsF = 1.4f; - } - } - - // loop all zones served by this RSYS - rs_areaServed = 0.; - rs_zonesServed = 0; - ZNR *zp; - RLUPC(ZrB, zp, rs_IsZoneServed(zp)) { - rs_areaServed += zp->i.znArea; - rs_zonesServed++; - } - - return rc; -} // RSYS::rs_TopRSys1 +RC RSYS::rs_TopRSys1() // check RSYS, initial set up for run +{ + RC rc = RCOK; + + if (!IsSet(RSYS_TDDESH)) + rs_tdDesH = rs_IsHP() ? 30.f // lower default temp rise for ASHP + : 50.f; // (changed later for CHDHW) + + if (rs_IsASHPPkgRoom()) + { if (!IsSet(RSYS_ASHPLOCKOUTT)) + rs_ASHPLockOutT = 45.f; // pkg room ASHP: use resistance at lower temps + rc |= rs_SetupASHP(); + } + + if (rs_IsVC()) + { // VCHP default motor type is BPM (others are PSC from cult) + if (!IsSet(RSYS_FAN + FAN_MOTTY)) + rs_fan.fn_motTy = C_MOTTYCH_BPM; + } + + if (rs_CanCool()) + { // cooling model air flow correlations have limited validity range + // verify air flow 150 - 550 cfm/ton (per Proctor Engineering) + rc |= limitCheck(RSYS_VFPERTON, 150., 550.); + + // rsFanPwrC (W/cfm): error if >5, warn if >2 + rc |= limitCheck(RSYS_FANPWRC, 0., 5., 0., 2.); + + if (rs_IsFanCoil()) + { // nothing to check? + + } + else + { // compression cooling + + rc |= rs_CkFCd(rsmCOOL); + + if (rs_IsWSHP()) + { + rc |= limitCheck(RSYS_CAPRATCH, 0.3, 2.); + + } + else + { // default/harmonize 95 F COP + // inter-default rs_COP95 <-> rs_EER95 + // if both input, values can be different + // rs_COP95 used for VCHP2, rs_EER95 used for single speed + // EER can default from SEER + if (rs_IsPkgRoom()) + { // pkg: derive SEER from EER + // abram conant fit, 6-20 + rs_SEER = 1.07f * rs_EER95; + // rs_COP95 not allowed + } + else if (IsSet(RSYS_EER95)) + { + if (!IsSet(RSYS_COP95)) + rs_COP95 = rs_EER95 / BtuperWh; + } + else + { + if (IsSet(RSYS_COP95)) + rs_EER95 = rs_COP95 * BtuperWh; + else if (!rs_IsVCClg()) + { // estimate missing EER from SEER + // California ACM method + rs_EER95 = rs_SEER < 13.f ? 10.f + 0.84f * (rs_SEER - 11.5f) + : rs_SEER < 16.f ? 11.3f + 0.57f * (rs_SEER - 13.f) + : 13.f; + rs_COP95 = rs_EER95 / BtuperWh; + } + } + } + } + + // final rs_CanCool() checks: EER vs SEER and cap82 vs cap95 vs cap115 + rc |= rs_CkFCooling2(); + } + + if (IsAusz( RSYS_CAP95)) + { rs_auszC.az_active = TRUE; + rs_fxCapCAsF = 1.2f; // working oversize factor + // ensures sufficient capacity + // during autosize search + } + + if (rs_CanHeat()) + { + // rsFanPwrH (W/cfm): error if >5, warn if >2 + rc |= limitCheck(RSYS_FANPWRH, 0., 5., 0., 2.); + + if (rs_IsHP()) + { + rc |= rs_CkFCd(rsmHEAT); + + if (IsAusz(RSYS_CAPH) || IsAusz(RSYS_CAP47) || IsAusz(RSYS_CAPAUXH)) + { // rs_capH for WSHP, rs_cap47 for ASHP + rs_auszH.az_active = TRUE; // ASHP autosizes rs_capH + // capacities derived in rs_AuszFinal + rs_fxCapHAsF = 1.2f; // working oversize factor + } + + if (rs_IsASHP()) + { + rc |= rs_CkFRatio(RSYS_CAP17, RSYS_CAP47, RSYS_CAPRAT1747, .2f, 1.2f); + rc |= rs_CkFRatio(RSYS_CAP05, RSYS_CAP47, RSYS_CAPRAT0547, .1f, 1.2f); + } + } + else if (IsAusz(RSYS_CAPH)) + { // other checking? + rs_auszH.az_active = TRUE; + rs_fxCapHAsF = 1.4f; + } + } + + // loop all zones served by this RSYS + rs_areaServed = 0.; + rs_zonesServed = 0; + ZNR* zp; + RLUPC( ZrB, zp, rs_IsZoneServed( zp)) + { rs_areaServed += zp->i.znArea; + rs_zonesServed++; + } + + return rc; +} // RSYS::rs_TopRSys1 //----------------------------------------------------------------------------- -RC RSYS::rs_TopRSys2() // final set up for run -{ - RC rc = RCOK; - - rs_SetWorkingPtrs(); // MTR and other inter-object pointers - - DUCTSEG *ds; - memset(rs_ducts, 0, sizeof(rs_ducts)); - RLUP(DsR, ds) { - ds->ds_SetRunConstants(); - if (ds->ownTi == ss) { // if duct segment part of this system - int iSR = !ds->ds_IsSupply(); // 1=return 0=supply - if (!rs_CanHaveDucts(0) && !rs_CanHaveDucts(1)) { - ZNR *zpx = ds->ds_GetExZone(); - if (zpx && ds->ds_exArea > 0.f) - oWarn("DUCTSEG '%s' is unused (rsDSEH and rsDSEC both given)\n" - " but its surface area is included in ZONE '%s'", - ds->Name(), zpx->Name()); - else - oInfo("DUCTSEG '%s' is unused (rsDSEH and rsDSEC both given)\n" - " and has no effect", - ds->Name()); - } else - for (int iHC = 0; iHC < 2; iHC++) { - int iDS = rs_Dsi(iSR, iHC); - if (iDS > 0) { - rc |= oer("DUCTSEG trouble -- more than one %s duct", - iSR ? "return" : "supply"); - break; - } - if (rs_CanHaveDucts(iHC)) - rs_ducts[iHC].dsi[iSR] = ds->ss; - } - } - } - - // combined heat / DHW - if (rs_IsCHDHW()) - rc |= rs_SetupCHDHW(); - - if (rc == RCOK) - rs_SetRunConstants(); - - return rc; -} // RSYS::rs_TopRSys2 +RC RSYS::rs_TopRSys2() // final set up for run +{ + RC rc = RCOK; + + rs_SetWorkingPtrs(); // MTR and other inter-object pointers + + DUCTSEG* ds; + memset( rs_ducts, 0, sizeof( rs_ducts)); + RLUP( DsR, ds) + { ds->ds_SetRunConstants(); + if (ds->ownTi == ss) + { // if duct segment part of this system + int iSR = !ds->ds_IsSupply(); // 1=return 0=supply + if (!rs_CanHaveDucts( 0) && !rs_CanHaveDucts( 1)) + { ZNR* zpx = ds->ds_GetExZone(); + if (zpx && ds->ds_exArea > 0.f) + oWarn( "DUCTSEG '%s' is unused (rsDSEH and rsDSEC both given)\n" + " but its surface area is included in ZONE '%s'", + ds->Name(), zpx->Name()); + else + oInfo( "DUCTSEG '%s' is unused (rsDSEH and rsDSEC both given)\n" + " and has no effect", + ds->Name()); + } + else for (int iHC=0; iHC<2; iHC++) + { int iDS = rs_Dsi( iSR, iHC); + if ( iDS > 0) + { rc |= oer( "DUCTSEG trouble -- more than one %s duct", + iSR ? "return" : "supply"); + break; + } + if (rs_CanHaveDucts( iHC)) + rs_ducts[ iHC].dsi[ iSR] = ds->ss; + } + } + } + + // combined heat / DHW + if (rs_IsCHDHW()) + rc |= rs_SetupCHDHW(); + + if (rc == RCOK) + rs_SetRunConstants(); + + return rc; +} // RSYS::rs_TopRSys2 //----------------------------------------------------------------------------- -RC RSYS::rs_FazInit( // init before autosize (once) and main sim - int isAusz) // TRUE = autosize, FALSE = main simulation +RC RSYS::rs_FazInit( // init before autosize (once) and main sim + int isAusz) // TRUE = autosize, FALSE = main simulation { - RC rc = RCOK; + RC rc = RCOK; - // autosizing init - // set handy local flag indicating autosizing now underway - rs_isAuszH = rs_auszH.az_fazInit(&rs_capH, FALSE, this, RSYS_CAPH, isAusz, - "RSYS[%s] capH"); - rs_isAuszC = rs_auszC.az_fazInit(&rs_cap95, FALSE, this, RSYS_CAP95, isAusz, - "RSYS[%s] cap95"); + // autosizing init + // set handy local flag indicating autosizing now underway + rs_isAuszH = rs_auszH.az_fazInit(&rs_capH, FALSE, this, RSYS_CAPH, isAusz, "RSYS[%s] capH"); + rs_isAuszC = rs_auszC.az_fazInit(&rs_cap95, FALSE, this, RSYS_CAP95, isAusz, "RSYS[%s] cap95"); - if (!rs_isAuszH) - rc |= rs_SetupCapH(); + if (!rs_isAuszH) + rc |= rs_SetupCapH(); - if (!rs_isAuszC) - rc |= rs_SetupCapC(); + if (!rs_isAuszC) + rc |= rs_SetupCapC(); - return rc; -} // RSYS::rs_FazInit + return rc; +} // RSYS::rs_FazInit //----------------------------------------------------------------------------- -RC RSYS::rs_RddInit( - int isAusz) // init before each autosize design day and main sim +RC RSYS::rs_RddInit( int isAusz) // init before each autosize design day and main sim { - isAusz; - return RCOK; -} // RSYS::rs_RddInit + isAusz; + return RCOK; +} // RSYS::rs_RddInit //----------------------------------------------------------------------------- -void RSYS::rs_RddiInit() // init before each autosize design day ITERATION -{ - // TODO: redundant calls occur and must be harmless! - - int auszMode = rs_IsAutoSizing(); - if (auszMode == rsmHEAT) // if autosizing something - { - if (Top.tp_pass1A) { // pass1A: warmup with fixed air flow - float cfmPerFt2 = rs_IsHP() ? 0.6f : 0.4f; - rs_SetupCapH(rs_areaServed * cfmPerFt2); // rs_capH derived from AVF - } else { // pass1B: rs_capH has latest value - if (Top.tp_auszDsDayItr < 2) - rs_capH = - rs_auszH.az_a; // 1st iteration cap may be 0 (see rs_pass1AtoB) - rs_SetupCapH(); - } - } else if (auszMode == rsmCOOL) { - if (Top.tp_pass1A) { // pass1A: warmup with fixed air flow - constexpr float cfmPerFt2 = 0.6f; - rs_amfC = AVFtoAMF(rs_areaServed * cfmPerFt2); - rs_SetupCapC(rs_areaServed * cfmPerFt2); // rs_cap95 derived from AVF - } else { // pass1B: rs_cap95 has latest value - if (Top.tp_auszDsDayItr < 2) - rs_cap95 = - rs_auszC.az_a; // 1st iteration cap may be 0 (see rs_pass1AtoB) - rs_SetupCapC(); - } - } -} // RSYS::rs_RddiInit +void RSYS::rs_RddiInit() // init before each autosize design day ITERATION +{ +// TODO: redundant calls occur and must be harmless! + + int auszMode = rs_IsAutoSizing(); + if (auszMode == rsmHEAT) // if autosizing something + { if (Top.tp_pass1A) + { // pass1A: warmup with fixed air flow + float cfmPerFt2 = rs_IsHP() ? 0.6f : 0.4f; + rs_SetupCapH( rs_areaServed * cfmPerFt2); // rs_capH derived from AVF + } + else + { // pass1B: rs_capH has latest value + if (Top.tp_auszDsDayItr < 2) + rs_capH = rs_auszH.az_a; // 1st iteration cap may be 0 (see rs_pass1AtoB) + rs_SetupCapH(); + } + } + else if (auszMode == rsmCOOL) + { if (Top.tp_pass1A) + { // pass1A: warmup with fixed air flow + constexpr float cfmPerFt2 = 0.6f; + rs_amfC = AVFtoAMF( rs_areaServed * cfmPerFt2); + rs_SetupCapC( rs_areaServed * cfmPerFt2); // rs_cap95 derived from AVF + } + else + { // pass1B: rs_cap95 has latest value + if (Top.tp_auszDsDayItr < 2) + rs_cap95 = rs_auszC.az_a; // 1st iteration cap may be 0 (see rs_pass1AtoB) + rs_SetupCapC(); + } + } +} // RSYS::rs_RddiInit //----------------------------------------------------------------------------- -RC RSYS::rs_endP1DsdIter( // autosizing end of day - int auszMode) // rsmHEAT, rsmCOOL -{ - RC rc = RCOK; - if (auszMode == rsmHEAT) { // note: rs_fxCapHDay = 20 if no load - float f = rs_fxCapHAsF / max(rs_fxCapHDay, .01f); - if (Top.tp_pass1A) { // pass1A (warmup): system always has fixed size - // after 1st iter, change rs_capH at will (used only for convergence - // test) rs_capH re-inited in rs_RddiInit - if (Top.tp_auszDsDayItr > 1) - rs_capH *= f; - } else { // pass1B: rs_capH sizes system - // change in small steps (can be unstable) - rs_capH *= bracket(0.9f, f, 1.1f); - - // never allow smaller than 1000 Btuh or any prior pass1B result - if (rs_capH < 1000.f) - rs_capH = 1000.f; - else if (rs_capH < rs_auszH.az_b) - rs_capH = rs_auszH.az_b; - } - - setToMax(rs_auszH.ldPk, rs_capH); // peak - - rs_amfH = rs_AMFForHtgCap(rs_capH); // consistent AMF - } else if (auszMode == rsmCOOL) { // note: rs_fxCapCDay = 20 if no load - float f = rs_fxCapCAsF / max(rs_fxCapCDay, .01f); - if (Top.tp_pass1A) { // pass1A (warmup): system always has fixed size - // after 1st iter, change rs_cap95 at will (used only for convergence - // test) rs_cap95 re-inited in rs_RddiInit - if (Top.tp_auszDsDayItr > 1) - rs_cap95 *= f; - } else { // pass1B: rs_cap95 sizes system - // change in small steps (can be unstable) - // when f is close to 1, move very slowly - // err = 0.01 -> fX = .001 - // err = 0.02 -> fX = .004 - // err = 0.1 -> fX = .1 - float err = f - 1.f; - float errX = err * fabs(err) * 10.f; - float fX = bracket(.9f, errX + 1.f, 1.1f); - rs_cap95 *= fX; - - float cap95min = max(rs_auszC.az_b, 1200.f); - if (rs_cap95 < cap95min) - rs_cap95 = cap95min; // never < 0.1 ton - // never < previous design day peak - } - - setToMax(rs_auszC.ldPk, rs_cap95); // peak - - rs_amfC = rs_AMFForClgCap(rs_cap95); // consistent AMF - } - return rc; - -} // RSYS::rs_endP1DsdIter +RC RSYS::rs_endP1DsdIter( // autosizing end of day + int auszMode) // rsmHEAT, rsmCOOL +{ + RC rc = RCOK; + if (auszMode == rsmHEAT) + { // note: rs_fxCapHDay = 20 if no load + float f = rs_fxCapHAsF / max( rs_fxCapHDay, .01f); + if (Top.tp_pass1A) + { // pass1A (warmup): system always has fixed size + // after 1st iter, change rs_capH at will (used only for convergence test) + // rs_capH re-inited in rs_RddiInit + if (Top.tp_auszDsDayItr > 1) + rs_capH *= f; + } + else + { // pass1B: rs_capH sizes system + // change in small steps (can be unstable) + rs_capH *= bracket( 0.9f, f, 1.1f); + + // never allow smaller than 1000 Btuh or any prior pass1B result + if (rs_capH < 1000.f) + rs_capH = 1000.f; + else if (rs_capH < rs_auszH.az_b) + rs_capH = rs_auszH.az_b; + } + + setToMax( rs_auszH.ldPk, rs_capH); // peak + + rs_amfH = rs_AMFForHtgCap( rs_capH); // consistent AMF + } + else if (auszMode == rsmCOOL) + { // note: rs_fxCapCDay = 20 if no load + float f = rs_fxCapCAsF / max( rs_fxCapCDay, .01f); + if (Top.tp_pass1A) + { // pass1A (warmup): system always has fixed size + // after 1st iter, change rs_cap95 at will (used only for convergence test) + // rs_cap95 re-inited in rs_RddiInit + if (Top.tp_auszDsDayItr > 1) + rs_cap95 *= f; + } + else + { // pass1B: rs_cap95 sizes system + // change in small steps (can be unstable) + // when f is close to 1, move very slowly + // err = 0.01 -> fX = .001 + // err = 0.02 -> fX = .004 + // err = 0.1 -> fX = .1 + float err = f - 1.f; + float errX = err * fabs( err) * 10.f; + float fX = bracket( .9f, errX+1.f, 1.1f); + rs_cap95 *= fX; + + float cap95min = max(rs_auszC.az_b, 1200.f); + if (rs_cap95 < cap95min) + rs_cap95 = cap95min; // never < 0.1 ton + // never < previous design day peak + } + + setToMax( rs_auszC.ldPk, rs_cap95); // peak + + rs_amfC = rs_AMFForClgCap( rs_cap95); // consistent AMF + } + return rc; + +} // RSYS::rs_endP1DsdIter //----------------------------------------------------------------------------- -RC RSYS::rs_pass1AtoB() // call at transition from autoSize pass 1 part A to - // part B for each design day +RC RSYS::rs_pass1AtoB() // call at transition from autoSize pass 1 part A to part B for each design day -// called between iterations of a design day (between tp_SimDays()'s without -// reInit) at the change from const-temp size-finding open-ended models to the -// real models. +// called between iterations of a design day (between tp_SimDays()'s without reInit) +// at the change from const-temp size-finding open-ended models to the real models. { - // at entry, value is set to max converged part A value (.a). + // at entry, value is set to max converged part A value (.a). - // here do any optimizations to make value a better estimate of rated size. + // here do any optimizations to make value a better estimate of rated size. - // on return, caller will increase value to any larger converged part B value - // (.b) then call RSYS::begP1b, next. + // on return, caller will increase value to any larger converged part B value (.b) + // then call RSYS::begP1b, next. - int auszMode = rs_IsAutoSizing(); - if (auszMode == rsmCOOL) - rs_cap95 = 0.f; - else - rs_capH = 0.f; + int auszMode = rs_IsAutoSizing(); + if (auszMode == rsmCOOL) + rs_cap95 = 0.f; + else + rs_capH = 0.f; - return RCOK; -} // RSYS::rs_pass1AtoB + return RCOK; +} // RSYS::rs_pass1AtoB //----------------------------------------------------------------------------------------------------------------------------- -RC RSYS::rs_begP1b() // called b4 start of pass 1 part b iterations for des day, - // after value set to max part B value seen. +RC RSYS::rs_begP1b() // called b4 start of pass 1 part b iterations for des day, after value set to max part B value seen. { -// reSetup models being autoSized, cuz AUSZ stuff and/or rs_pass1AtoB above may -// have changed values. +// reSetup models being autoSized, cuz AUSZ stuff and/or rs_pass1AtoB above may have changed values. #if 0 x RC rc = rs_SetupSizes(); // just above x return rc; #else - return RCOK; + return RCOK; #endif -} // RSYS::rs_begP1b +} // RSYS::rs_begP1b //----------------------------------------------------------------------------- -RC RSYS::rs_endAutosize() // call at end successful autoSize for possible - // additional checks/messages +RC RSYS::rs_endAutosize() // call at end successful autoSize for possible additional checks/messages { - return RCOK; -} // rs_endAutosize + return RCOK; +} // rs_endAutosize //----------------------------------------------------------------------------- -void RSYS::rs_AuszFinal() // called at end of successful autosize (after all - // xx_endAutosize) +void RSYS::rs_AuszFinal() // called at end of successful autosize (after all xx_endAutosize) { - RSYS *rsi = RSiB.GetAtSafe(ss); + RSYS* rsi = RSiB.GetAtSafe( ss); - // member function sets _As, _AsNov, and input record members for each AUSZ's - // 'x'. + // member function sets _As, _AsNov, and input record members for each AUSZ's 'x'. - if (rs_isAuszC) - rs_cap95 = rs_fxCapCTarg * rs_auszC.ldPkAs1 / rs_fxCapCAsF; - // max of design days w/o working oversize factor - // apply user oversize factor + if (rs_isAuszC) + rs_cap95 = rs_fxCapCTarg * rs_auszC.ldPkAs1 / rs_fxCapCAsF; + // max of design days w/o working oversize factor + // apply user oversize factor - if (rs_isAuszH) { - float capHBase = - rs_auszH.ldPkAs1 / rs_fxCapHAsF; // max of all design days - // remove working cap factor + if (rs_isAuszH) + { float capHBase = rs_auszH.ldPkAs1 / rs_fxCapHAsF; // max of all design days + // remove working cap factor - rs_capH = capHBase * rs_fxCapHTarg; // apply user's oversize factor + rs_capH = capHBase * rs_fxCapHTarg; // apply user's oversize factor - if (rs_IsASHP()) { - if (IsAusz(RSYS_CAP47)) { // find cap47 that produces required output - // (re-derives dependent values) - rs_speedF = 1.f; // force full speed (used in e.g. rs_CapEffASHP()) - rs_SizeHtASHP(rs_capH, Top.heatDsTDbO); -#if defined(_DEBUG) - // capacity consistency check - float capCheck = rs_CapEffASHP(Top.heatDsTDbO); - float capExpected = Top.heatDsTDbO >= rs_ASHPLockOutT - ? rs_capH - : rs_fanHeatH; // fan heat only when lockout - if (frDiff(capCheck, capExpected, 1.f) > .001f) - oWarn( - "ASHP heating capacity (%.1f) autosize mismatch (expected %.1f)", - capCheck, capExpected); -#endif - } - } - - if (IsAusz(RSYS_CAPAUXH)) { // ASHP aux heat autosize cap = full load w/ - // user oversize - rs_capAuxH = capHBase * rs_fxCapAuxHTarg; - } - } - - // re rs_capAuxH NOT autosized - // restore input value - // WHY: HP autosize process modifies - if (!IsAusz(RSYS_CAPAUXH)) - rs_capAuxH = rs_capAuxHInp; // value as input - rsi->rs_capAuxH = rs_capAuxH; // copy back to input record - - if (rs_IsASHP()) { - if (rs_isAuszC) { - if (IsAusz(RSYS_CAP47)) { // both autosized - ASHPConsistentCaps(rs_cap95, rs_cap47, IsSet(RSYS_CAPRAT9547), - rs_capRat9547); - } - // else leave rs_cap95 autosized, rs_cap47 as input - } - rs_SetupASHP(); // default other params as needed - rsi->rs_cap47 = rs_cap47; // copy back to input record WHY? - - } else if (rs_IsWSHP()) { - if (rs_isAuszC) { - if (IsAusz(RSYS_CAPH)) { // both autosized - WSHPPerf.whp_ConsistentCaps(rs_cap95, rs_capH, IsSet(RSYS_CAPRATCH), - rs_capRatCH); - } - // else leave rs_cap95 autosized, rs_capH as input - } - - rs_SetupWSHP(); // default other params as needed - } - - // all capacities now known - if (rs_isAuszC) { - float coolOver = 1.f; // no coiling oversize (rs_fxCapC included in process) - rs_auszC.az_final(this, rsi, coolOver); - } - if (rs_isAuszH) { - float heatOver = 1.f; // ditto heating - rs_auszH.az_final(this, rsi, heatOver); - } + if (rs_IsASHP()) + { if (IsAusz( RSYS_CAP47)) + { // find cap47 that produces required output + // (re-derives dependent values) + rs_speedF = 1.f; // force full speed (used in e.g. rs_CapEffASHP()) + rs_SizeHtASHP( rs_capH, Top.heatDsTDbO); +#if defined( _DEBUG) + // capacity consistency check + float capCheck = rs_CapEffASHP(Top.heatDsTDbO); + float capExpected = Top.heatDsTDbO >= rs_ASHPLockOutT + ? rs_capH + : rs_fanHeatH; // fan heat only when lockout + if (frDiff(capCheck, capExpected, 1.f) > .001f) + oWarn("ASHP heating capacity (%.1f) autosize mismatch (expected %.1f)", + capCheck, capExpected); +#endif + } + } + + if (IsAusz(RSYS_CAPAUXH)) + { // ASHP aux heat autosize cap = full load w/ user oversize + rs_capAuxH = capHBase * rs_fxCapAuxHTarg; + } + } + + // re rs_capAuxH NOT autosized + // restore input value + // WHY: HP autosize process modifies + if (!IsAusz(RSYS_CAPAUXH)) + rs_capAuxH = rs_capAuxHInp; // value as input + rsi->rs_capAuxH = rs_capAuxH; // copy back to input record + + if (rs_IsASHP()) + { if (rs_isAuszC) + { if (IsAusz( RSYS_CAP47)) + { // both autosized + ASHPConsistentCaps( rs_cap95, rs_cap47, IsSet(RSYS_CAPRAT9547), rs_capRat9547); + } + // else leave rs_cap95 autosized, rs_cap47 as input + } + rs_SetupASHP(); // default other params as needed + rsi->rs_cap47 = rs_cap47; // copy back to input record WHY? + + } + else if (rs_IsWSHP()) + { if (rs_isAuszC) + { + if (IsAusz(RSYS_CAPH)) + { // both autosized + WSHPPerf.whp_ConsistentCaps(rs_cap95, rs_capH, IsSet(RSYS_CAPRATCH), rs_capRatCH); + } + // else leave rs_cap95 autosized, rs_capH as input + } + + rs_SetupWSHP(); // default other params as needed + + } + + // all capacities now known + if (rs_isAuszC) + { float coolOver = 1.f; // no coiling oversize (rs_fxCapC included in process) + rs_auszC.az_final( this, rsi, coolOver); + } + if (rs_isAuszH) + { float heatOver = 1.f; // ditto heating + rs_auszH.az_final( this, rsi, heatOver); + } #if 0 x NO -- leave to allow reporting of load at design temp @@ -2868,212 +2870,220 @@ x if (rs_IsASHP()) x rsi->rs_capH = rs_capH = 0.f; #endif - rs_DefaultCapNomsIf(); // capture possible nominal cap change(s) - // no calc effect - // insurance: no rs_auszX.final() changes expected - // (coolOver = heatOver = 1) + rs_DefaultCapNomsIf(); // capture possible nominal cap change(s) + // no calc effect + // insurance: no rs_auszX.final() changes expected + // (coolOver = heatOver = 1) -} // RSYS::rs_AuszFinal +} // RSYS::rs_AuszFinal //----------------------------------------------------------------------------- -float RSYS::rs_ClgCapNomTons( // nominal cooling capacity - float nearest /*=-1.f*/) // if >0, round to nearest specified increment - // e.g. 0.5 = round to nearest .5 ton +float RSYS::rs_ClgCapNomTons( // nominal cooling capacity + float nearest /*=-1.f*/) // if >0, round to nearest specified increment + // e.g. 0.5 = round to nearest .5 ton // returns capacity, tons (1 ton = 12000 Btuh) { - float capNomTons = fabs(rs_cap95) / 12000.f; - if (nearest > 0.f) - capNomTons = nearest * floor(0.5f + capNomTons / nearest); - return capNomTons; -} // RSYS::rs_ClgCapNomTons + float capNomTons = fabs( rs_cap95)/12000.f; + if (nearest > 0.f) + capNomTons = nearest*floor( 0.5f + capNomTons / nearest); + return capNomTons; +} // RSYS::rs_ClgCapNomTons //----------------------------------------------------------------------------- -float RSYS::rs_ClgCapForAMF(float amf) const // air mass flow rate, lbm/hr +float RSYS::rs_ClgCapForAMF( + float amf) const // air mass flow rate, lbm/hr // returns rated total capacity at 95F, Btuh -{ - float avf = AMFtoAVF(amf); // vol flow, cfm std air - float cap95 = 12000.f * avf / rs_vfPerTon; - return cap95; -} // RSYS::rs_ClgCapForAMF +{ float avf = AMFtoAVF( amf); // vol flow, cfm std air + float cap95 = 12000.f * avf / rs_vfPerTon; + return cap95; +} // RSYS::rs_ClgCapForAMF //----------------------------------------------------------------------------- float RSYS::rs_AMFForClgCap( - float cap95) const // total cooling capacity at 95F, Btuh + float cap95) const // total cooling capacity at 95F, Btuh // returns air mass flow, lbm/hr { - float avf = cap95 * rs_vfPerTon / 12000.f; - float amf = AVFtoAMF(avf); - return amf; -} // RSYS::rs_AMFForClgCap + float avf = cap95 * rs_vfPerTon / 12000.f; + float amf = AVFtoAMF( avf); + return amf; +} // RSYS::rs_AMFForClgCap //----------------------------------------------------------------------------- -float RSYS::rs_HtgCapForAMF(float amf) const // air mass flow rate, lbm/hr +float RSYS::rs_HtgCapForAMF( + float amf) const // air mass flow rate, lbm/hr // returns rated heating capacity, Btuh { - float capH = amf * rs_tdDesH * Top.tp_airSH; - return capH; -} // RSYS::rs_ClgCapForAMF + float capH = amf * rs_tdDesH * Top.tp_airSH; + return capH; +} // RSYS::rs_ClgCapForAMF //----------------------------------------------------------------------------- -float RSYS::rs_AMFForHtgCap(float capH) const // heating capacity +float RSYS::rs_AMFForHtgCap( + float capH) const // heating capacity // returns air mass flow, lbm/hr { - float amf = capH / (rs_tdDesH * Top.tp_airSH); - return amf; -} // RSYS::rs_AMFForHtgCap + float amf = capH / (rs_tdDesH * Top.tp_airSH); + return amf; +} // RSYS::rs_AMFForHtgCap //----------------------------------------------------------------------------- -int RSYS::rs_CanHaveDucts(int iHC) const // 0=htg, 1=clg +int RSYS::rs_CanHaveDucts( + int iHC) const // 0=htg, 1=clg // return nz iff { - int ret = !IsSet(iHC ? RSYS_DSEC : RSYS_DSEH); - return ret; -} // RSYS::rs_CanHaveDucts + int ret = !IsSet( iHC ? RSYS_DSEC : RSYS_DSEH); + return ret; +} // RSYS::rs_CanHaveDucts //----------------------------------------------------------------------------- -ZNR *RSYS::rs_GetOAVReliefZn() const // get relief zone for OAV +ZNR* RSYS::rs_GetOAVReliefZn() const // get relief zone for OAV // returns pointer to ZNR where this RSYS discards zone return air // NULL if RSYS cannot OAV -{ - ZNR *zp = rs_CanOAV() ? ZrB.GetAtSafe(rs_OAVReliefZi) : NULL; - return zp; -} // RSYS::rs_GetOAVReliefZn +{ ZNR* zp = rs_CanOAV() + ? ZrB.GetAtSafe( rs_OAVReliefZi) + : NULL; + return zp; +} // RSYS::rs_GetOAVReliefZn //----------------------------------------------------------------------------- -void RSYS::rs_OAVSetup() // 1 time setup for OAV -{ - if (rs_OAVType == C_RSYSOAVTYCH_NONE) { // insurance - rs_OAVAvfDs = rs_OAVFanPwr = 0.f; - } else { - [[maybe_unused]] ZNR *zp = rs_GetOAVReliefZn(); - if (rs_OAVType == - C_RSYSOAVTYCH_VARFLOW) { // variable flow (aka "NightBreeze") - - // rs_OAVAvfDs is required - - if (!IsSet(RSYS_OAVFANPWR)) { // W/cfm per DEG Eq 1 (updated 11-20-2013) - // curve is good only to 1600 cfm - float avfX = bracket(1.f, rs_OAVAvfDs, 1600.f); - rs_OAVFanPwr = 44.616f * pow(1.00175684f, avfX) / avfX; - } - } else { // fixed flow (aka "SmartVent") - - // rs_OAVAvfDs is required - - if (!IsSet(RSYS_OAVFANPWR)) - rs_OAVFanPwr = rs_fanPwrC; // same as cooling - } - } -} // RSYS::rs_OAVSetup +void RSYS::rs_OAVSetup() // 1 time setup for OAV +{ + if (rs_OAVType == C_RSYSOAVTYCH_NONE) + { // insurance + rs_OAVAvfDs = rs_OAVFanPwr = 0.f; + } + else + { + [[maybe_unused]] ZNR* zp = rs_GetOAVReliefZn(); + if (rs_OAVType == C_RSYSOAVTYCH_VARFLOW) + { // variable flow (aka "NightBreeze") + + // rs_OAVAvfDs is required + + if (!IsSet( RSYS_OAVFANPWR)) + { // W/cfm per DEG Eq 1 (updated 11-20-2013) + // curve is good only to 1600 cfm + float avfX = bracket( 1.f, rs_OAVAvfDs, 1600.f); + rs_OAVFanPwr = 44.616f * pow( 1.00175684f, avfX) / avfX; + } + } + else + { // fixed flow (aka "SmartVent") + + // rs_OAVAvfDs is required + + if (!IsSet( RSYS_OAVFANPWR)) + rs_OAVFanPwr = rs_fanPwrC; // same as cooling + } + } +} // RSYS::rs_OAVSetup //----------------------------------------------------------------------------- -void RSYS::rs_OAVAirFlow() // OAV air flow calcs +void RSYS::rs_OAVAirFlow() // OAV air flow calcs // call at beg of each day // sets rs_OAVAvfD and rs_OAVFanPwrD { - rs_avfOAV = rs_fanHeatOAV = 0.f; - - if (rs_OAVType == - C_RSYSOAVTYCH_VARFLOW) { // NightBreeze algorithm, reference = - // "Method for Developing Fan Energy Use For a Variable - // Speed Ventilation Cooling Title 24 Measure" - // Davis Energy Group - // 11/20/2013 "Updated" version - // Air flow and fan power based on prior days average tDb - // use double due to potentially huge exp( x) - float afUnlim = - float(1. / pow(1. + exp(653. - 8.152 * Wthr.d.wd_taDbAvg), 0.010654)); - float af = max(rs_OAVAvfMinF, afUnlim); - rs_avfOAV = af * rs_OAVAvfDs; // cfm - rs_fanHeatOAV = rs_OAVAvfDs * rs_OAVFanPwr * pow(af, 2.767f) * 3.413f; -#if defined(_DEBUG) - // 9-29-2010 model for comparison - float tMax = Wthr.d.wd_taDbPvPk; - [[maybe_unused]] float afOld = 0.f; - if (tMax > .0000001f) { - double d = 17.91554 - 3.67538 * log(tMax); - afOld = 1.f / max(1.f, float(d)); - } - // float d = af - afOld; - if (rs_fanHeatOAV > 4000.f) - printf("Excess OAV fan heat\n"); -#endif - } else if (rs_OAVType == C_RSYSOAVTYCH_FIXEDFLOW) { - rs_avfOAV = rs_OAVAvfDs; - rs_fanHeatOAV = rs_avfOAV * rs_OAVFanPwr * 3.413f; // fan power, Btuh - } - rs_amfOAV = AVFtoAMF(rs_avfOAV); // mass flow - - // else rs_OAVAvfD = rs_OAVFanPwrD = 0.f from above -} // RSYS::rs_OAVAirFlow + rs_avfOAV = rs_fanHeatOAV = 0.f; + + if (rs_OAVType == C_RSYSOAVTYCH_VARFLOW) + { // NightBreeze algorithm, reference = + // "Method for Developing Fan Energy Use For a Variable + // Speed Ventilation Cooling Title 24 Measure" + // Davis Energy Group + // 11/20/2013 "Updated" version + // Air flow and fan power based on prior days average tDb + // use double due to potentially huge exp( x) + float afUnlim = float( 1./pow( 1.+exp( 653.-8.152*Wthr.d.wd_taDbAvg), 0.010654)); + float af = max( rs_OAVAvfMinF, afUnlim); + rs_avfOAV = af * rs_OAVAvfDs; // cfm + rs_fanHeatOAV = rs_OAVAvfDs * rs_OAVFanPwr * pow( af, 2.767f) * 3.413f; +#if defined( _DEBUG) + // 9-29-2010 model for comparison + float tMax = Wthr.d.wd_taDbPvPk; + [[maybe_unused]] float afOld = 0.f; + if (tMax > .0000001f) + { double d = 17.91554 - 3.67538*log( tMax); + afOld = 1.f/max( 1.f, float( d)); + } + // float d = af - afOld; + if (rs_fanHeatOAV > 4000.f) + printf( "Excess OAV fan heat\n"); +#endif + } + else if (rs_OAVType == C_RSYSOAVTYCH_FIXEDFLOW) + { rs_avfOAV = rs_OAVAvfDs; + rs_fanHeatOAV = rs_avfOAV * rs_OAVFanPwr * 3.413f; // fan power, Btuh + } + rs_amfOAV = AVFtoAMF( rs_avfOAV); // mass flow + + // else rs_OAVAvfD = rs_OAVFanPwrD = 0.f from above +} // RSYS::rs_OAVAirFlow //----------------------------------------------------------------------------- int RSYS::rs_OAVAttempt() // returns // -1 not possible { - if (rs_SupplyAirState(RSYS::rsmOAV) != 3) { - rs_mode = rsmOFF; - return -1; // OAV not available - } - - double tSup = rs_asSup.as_tdb; - - ZNR *zp; - int okOAV = 0; - RLUPC(ZrB, zp, rs_IsZoneServed(zp)) { - okOAV += zp->zn_OAVAttempt(tSup); - if (okOAV < 0) - break; - } - if (okOAV > 0) { // at least 1 zone wants OAV - // find zone air temp with available air - // OAV not OK if any zone above zn_tzspC - double fSize = rs_amfReq[0] > 0. // fraction of total air that is avaailable - ? min(rs_amf / rs_amfReq[0], 1.) - : 0.; - RLUPC(ZrB, zp, - rs_IsZoneServed(zp)) { // set zn_rsAmfSup, zn_rsAmfRet, and zn_rsFSize - zp->zn_SetRSYSAmf(fSize, 0); - double mCp = zp->zn_rsAmfSup * Top.tp_airSH; - double tzx = zp->zn_TAirCR(mCp * tSup, mCp, 0.); - if (tzx > - zp->zn_tzspC /* && RSYS can cool */) { // OAV cannot hold tz below - // cooling set point, disable - okOAV = 0; - break; - } - } - } - - if (okOAV <= 0) { // OAV not OK, revert any partial results - RLUPC(ZrB, zp, rs_IsZoneServed(zp)) { - zp->zn_rsAmfSup = zp->zn_rsAmfRet = 0.; - zp->zn_rsFSize = 0.; - // zp->tz at floating temp - zp->zn_tzsp = 0.f; - zp->zn_rsAmfSysReq[0] = 0.f; - ZnresB[zp->ss].curr.S.nShVentH = - 0; // vent heating not possible if no vent - } - rs_mode = rsmOFF; - return -1; - } - - // OAV can hold acceptable temp in all zones - // leave rs_mode as rsmOAV - RLUPC(ZrB, zp, rs_IsZoneServed(zp)) { - zp->zn_hcMode = rsmOAV; // say this zone has been successfully OAVed - // most further this-step HVAC modelling is skipped - if (ZnresB[zp->ss].curr.S.nShVentH) { - const int WARNMAX = 20; - int warnCount = - ZnresB[zp->ss].zr_GetAllIntervalTotal(ZNRES_IVL_SUB_NSHVENTH); - if (warnCount <= WARNMAX) - warn("Zone '%s', %s: unhelpful vent heating (supply temp = %0.2f)%s", - zp->Name(), Top.When(C_IVLCH_S), tSup, - warnCount == WARNMAX - ? "\n Suppressing further vent heating messages for this zone" - : ""); - } - } - - return 0; -} // RSYS::rs_OAVAttempt + if (rs_SupplyAirState( RSYS::rsmOAV) != 3) + { rs_mode = rsmOFF; + return -1; // OAV not available + } + + double tSup = rs_asSup.as_tdb; + + ZNR* zp; + int okOAV = 0; + RLUPC( ZrB, zp, rs_IsZoneServed( zp)) + { okOAV += zp->zn_OAVAttempt( tSup); + if (okOAV < 0) + break; + } + if (okOAV > 0) + { // at least 1 zone wants OAV + // find zone air temp with available air + // OAV not OK if any zone above zn_tzspC + double fSize = rs_amfReq[ 0] > 0. // fraction of total air that is avaailable + ? min( rs_amf / rs_amfReq[ 0], 1.) + : 0.; + RLUPC( ZrB, zp, rs_IsZoneServed( zp)) + { // set zn_rsAmfSup, zn_rsAmfRet, and zn_rsFSize + zp->zn_SetRSYSAmf( fSize, 0); + double mCp = zp->zn_rsAmfSup*Top.tp_airSH; + double tzx = zp->zn_TAirCR( mCp*tSup, mCp, 0.); + if (tzx > zp->zn_tzspC /* && RSYS can cool */) + { // OAV cannot hold tz below cooling set point, disable + okOAV = 0; + break; + } + } + } + + if (okOAV <= 0) + { // OAV not OK, revert any partial results + RLUPC( ZrB, zp, rs_IsZoneServed( zp)) + { zp->zn_rsAmfSup = zp->zn_rsAmfRet = 0.; + zp->zn_rsFSize = 0.; + // zp->tz at floating temp + zp->zn_tzsp = 0.f; + zp->zn_rsAmfSysReq[ 0] = 0.f; + ZnresB[ zp->ss].curr.S.nShVentH = 0; // vent heating not possible if no vent + } + rs_mode = rsmOFF; + return -1; + } + + // OAV can hold acceptable temp in all zones + // leave rs_mode as rsmOAV + RLUPC( ZrB, zp, rs_IsZoneServed( zp)) + { zp->zn_hcMode = rsmOAV; // say this zone has been successfully OAVed + // most further this-step HVAC modelling is skipped + if (ZnresB[ zp->ss].curr.S.nShVentH) + { const int WARNMAX = 20; + int warnCount = ZnresB[ zp->ss].zr_GetAllIntervalTotal( ZNRES_IVL_SUB_NSHVENTH); + if (warnCount <= WARNMAX) + warn( "Zone '%s', %s: unhelpful vent heating (supply temp = %0.2f)%s", + zp->Name(), Top.When( C_IVLCH_S), tSup, + warnCount == WARNMAX + ? "\n Suppressing further vent heating messages for this zone" : ""); + } + } + + return 0; +} // RSYS::rs_OAVAttempt //----------------------------------------------------------------------------- int ZNR::zn_OAVAttempt( - double tSup) // available supply air temp (at register), F + double tSup) // available supply air temp (at register), F // sets zn_tzsp and zn_rsAmfSysReq[ 0] @@ -3082,250 +3092,253 @@ int ZNR::zn_OAVAttempt( // 0 no OAV required, zone temp OK // 1 OAV useful { - zn_rsAmfSysReq[0] = 0.; // insurance: no air requested + zn_rsAmfSysReq[ 0] = 0.; // insurance: no air requested - if (tz < zn_tzspH /* && RSYS could heat*/) - return -9999; // zone needs heat, don't OAV + if (tz < zn_tzspH /* && RSYS could heat*/) + return -9999; // zone needs heat, don't OAV - RSYS *rs = zn_GetRSYS(); -#if 1 // spec change, 10-9-2013 - zn_tzsp = max(zn_tzspD, rs->rs_OAVTdbInlet + rs->rs_OAVTdiff); + RSYS* rs = zn_GetRSYS(); +#if 1 // spec change, 10-9-2013 + zn_tzsp = max( zn_tzspD, rs->rs_OAVTdbInlet + rs->rs_OAVTdiff); #else - x zn_tzsp = max(zn_tzspD, tSup + rs->rs_OAVTdiff); -#endif - if (zn_tzsp > zn_tzspC /*&& RSYS could cool*/) - return -9999; // no amount of OAV will prevent cooling - if (tz < zn_tzsp) - return 0; // no OAV required - - // dry air mass flow to hold at set point - double amfOAV; - if (tSup >= zn_tzsp) { // OAV causes heating, run full volume -#if defined(_DEBUG) - if (tSup > 100.) - printf("Excessive OAV supply temp %0.1f\n", tSup); -#endif - ZnresB[ss].curr.S.nShVentH = 1; - amfOAV = DBL_MAX; - } else - amfOAV = zn_AmfHvacCR(zn_tzsp, tSup); - zn_rsAmfSysReq[0] = rs->rs_ZoneAirRequest(amfOAV, 0); +x zn_tzsp = max( zn_tzspD, tSup + rs->rs_OAVTdiff); +#endif + if (zn_tzsp > zn_tzspC /*&& RSYS could cool*/) + return -9999; // no amount of OAV will prevent cooling + if (tz < zn_tzsp) + return 0; // no OAV required + + // dry air mass flow to hold at set point + double amfOAV; + if (tSup >= zn_tzsp) + { // OAV causes heating, run full volume +#if defined( _DEBUG) + if (tSup > 100.) + printf( "Excessive OAV supply temp %0.1f\n", tSup); +#endif + ZnresB[ ss].curr.S.nShVentH = 1; + amfOAV = DBL_MAX; + } + else + amfOAV = zn_AmfHvacCR( zn_tzsp, tSup); + zn_rsAmfSysReq[ 0] = rs->rs_ZoneAirRequest( amfOAV, 0); - return 1; // OAV is possible + return 1; // OAV is possible -} // ZNR::zn_OAVAttempt +} // ZNR::zn_OAVAttempt //----------------------------------------------------------------------------- void RSYS::rs_SetRunConstants() // set model vals that do not vary during simulation // *and* do not depend on (possibly) autosized capacities // redundant calls OK { - // temp rise due to cooling fan, F - // constant even if capacity is changed (e.g. during autosize) - // (cuz air flow is proportional to capacity via rs_vfPerTon) - rs_fanDeltaTC = rs_fanPwrC * 3.413f / (60.f * .075f * Top.tp_airSH); - - rs_speedFMin = 1.f; // altered iff variable speed - - // duct non-leak fractions - // Note: never 0! - for (int iHC = 0; iHC < 2; iHC++) - for (int iSR = 0; iSR < 2; iSR++) { - int iDS = rs_Dsi(iSR, iHC); - rs_ducts[iHC].ductLkXF[iSR] = - iDS > 0 ? 1.f - min(DsR[iDS].ds_leakF, .99f) : 1.f; - } + // temp rise due to cooling fan, F + // constant even if capacity is changed (e.g. during autosize) + // (cuz air flow is proportional to capacity via rs_vfPerTon) + rs_fanDeltaTC = rs_fanPwrC * 3.413f / (60.f * .075f * Top.tp_airSH); + + rs_speedFMin = 1.f; // altered iff variable speed + + // duct non-leak fractions + // Note: never 0! + for (int iHC=0; iHC<2; iHC++) + for (int iSR=0; iSR<2; iSR++) + { int iDS = rs_Dsi( iSR, iHC); + rs_ducts[ iHC].ductLkXF[ iSR] = iDS > 0 + ? 1.f - min( DsR[ iDS].ds_leakF, .99f) + : 1.f; + } - rs_OAVSetup(); + rs_OAVSetup(); -} // rs_SetRunConstants +} // rs_SetRunConstants //----------------------------------------------------------------------------- -void RSYS::rs_SetWorkingPtrs() // set runtime pointers to meters etc. +void RSYS::rs_SetWorkingPtrs() // set runtime pointers to meters etc. // WHY: simplifies runtime code { - rs_pMtrElec = MtrB.GetAtSafe(rs_elecMtri); // elec mtr or NULL - rs_pMtrFuel = MtrB.GetAtSafe(rs_fuelMtri); // fuel mtr or NULL - rs_pMtrHeat = rs_IsElecHeat() ? rs_pMtrElec : rs_pMtrFuel; // heat mtr or NULL - rs_pMtrAux = rs_IsFuelAuxH() ? rs_pMtrFuel : rs_pMtrElec; - rs_pLoadMtr[0] = LdMtrR.GetAtSafe(rs_loadMtri); - rs_pLoadMtr[1] = LdMtrR.GetAtSafe(rs_htgLoadMtri); - rs_pLoadMtr[2] = LdMtrR.GetAtSafe(rs_clgLoadMtri); - rs_pSrcSideLoadMtr[0] = LdMtrR.GetAtSafe(rs_srcSideLoadMtri); - rs_pSrcSideLoadMtr[1] = LdMtrR.GetAtSafe(rs_htgSrcSideLoadMtri); - rs_pSrcSideLoadMtr[2] = LdMtrR.GetAtSafe(rs_clgSrcSideLoadMtri); - rs_pCHDHWSYS = WsR.GetAtSafe(rs_CHDHWSYSi); -} // RSYS::rs_SetMTRPtrs + rs_pMtrElec = MtrB.GetAtSafe( rs_elecMtri); // elec mtr or NULL + rs_pMtrFuel = MtrB.GetAtSafe( rs_fuelMtri); // fuel mtr or NULL + rs_pMtrHeat = rs_IsElecHeat() ? rs_pMtrElec : rs_pMtrFuel; // heat mtr or NULL + rs_pMtrAux = rs_IsFuelAuxH() ? rs_pMtrFuel : rs_pMtrElec; + rs_pLoadMtr[ 0] = LdMtrR.GetAtSafe(rs_loadMtri); + rs_pLoadMtr[ 1] = LdMtrR.GetAtSafe(rs_htgLoadMtri); + rs_pLoadMtr[ 2] = LdMtrR.GetAtSafe(rs_clgLoadMtri); + rs_pSrcSideLoadMtr[ 0] = LdMtrR.GetAtSafe(rs_srcSideLoadMtri); + rs_pSrcSideLoadMtr[ 1] = LdMtrR.GetAtSafe(rs_htgSrcSideLoadMtri); + rs_pSrcSideLoadMtr[ 2] = LdMtrR.GetAtSafe(rs_clgSrcSideLoadMtri); + rs_pCHDHWSYS = WsR.GetAtSafe(rs_CHDHWSYSi); +} // RSYS::rs_SetMTRPtrs //----------------------------------------------------------------------------- -RC RSYS::rs_SetupSizes( // derive capacity-dependent values - BOOL bAlways /*=TRUE*/) // TRUE: always - // FALSE: iff autosize +RC RSYS::rs_SetupSizes( // derive capacity-dependent values + BOOL bAlways /*=TRUE*/) // TRUE: always + // FALSE: iff autosize // CAUTION: only call is with bAlways=TRUE // Review re autosize if used elsewhere 12-2012 { - RC rc = RCOK; - if (bAlways || rs_isAuszH) - rc |= rs_SetupCapH(); - if (bAlways || rs_isAuszC) - rc |= rs_SetupCapC(); - return rc; -} // RSYS::rs_SetupSizes + RC rc = RCOK; + if (bAlways || rs_isAuszH) + rc |= rs_SetupCapH(); + if (bAlways || rs_isAuszC) + rc |= rs_SetupCapC(); + return rc; +} // RSYS::rs_SetupSizes //----------------------------------------------------------------------------- -RC RSYS::rs_SetupCapH( // set heating members that do not vary during simulation - float avfH /*=-1*/, // heating AVF, cfm std air if known - // else derived from rs_capH - int options /*=0*/) // option bits - // 1: assume not autosize (ignore Top.tp_autoSizing) +RC RSYS::rs_SetupCapH( // set heating members that do not vary during simulation + float avfH /*=-1*/, // heating AVF, cfm std air if known + // else derived from rs_capH + int options /*=0*/) // option bits + // 1: assume not autosize (ignore Top.tp_autoSizing) // returns RCOK iff success { - RC rc = RCOK; - if (avfH > 0.f) { - rs_amfH = AVFtoAMF(avfH); - rs_capH = rs_amfH * Top.tp_airSH * rs_tdDesH; - } else { - float nomCap; - if (rs_IsASHP()) { - if (Top.tp_autoSizing && !(options & 1)) { - nomCap = rs_capH; // ASHP autosize derived from rs_capH - } else { - rc |= rs_SetupASHP(); // set default capacities/efficiencies, derive - // constants - // multiple calls OK (e.g. during autosizing) - // final call needed after autosize complete - nomCap = rs_cap47; - } - } else if (rs_IsWSHP()) { - rc |= rs_SetupWSHP(); // set default capacities/efficiencies, derive - // constants - // multiple calls OK (e.g. during autosizing) - // final call needed after autosize complete - nomCap = rs_capH; - } else if (rs_IsCHDHW()) { - nomCap = rs_capH; - - } else { // non-HP, non-CHDHW - nomCap = rs_capH; - if (!rs_CanHaveAuxHeat()) - rs_capAuxH = 0.f; // insurance - } - rs_amfH = - nomCap / - (rs_tdDesH * - Top.tp_airSH); // nominal full speed dry-air mass flow rate, lb/hr - avfH = AMFtoAVF(rs_amfH); - } - rs_fanHeatH = avfH * rs_fanPwrH * BtuperWh; - - rs_DefaultCapNomsIf(); // update nominal capacities (no calc effect) - - return rc; - -} // RSYS::rs_SetupCapH + RC rc = RCOK; + if (avfH > 0.f) + { rs_amfH = AVFtoAMF( avfH); + rs_capH = rs_amfH * Top.tp_airSH * rs_tdDesH; + } + else + { float nomCap; + if (rs_IsASHP()) + { if (Top.tp_autoSizing && !(options&1)) + { nomCap = rs_capH; // ASHP autosize derived from rs_capH + } + else + { rc |= rs_SetupASHP(); // set default capacities/efficiencies, derive constants + // multiple calls OK (e.g. during autosizing) + // final call needed after autosize complete + nomCap = rs_cap47; + } + } + else if (rs_IsWSHP()) + { + rc |= rs_SetupWSHP(); // set default capacities/efficiencies, derive constants + // multiple calls OK (e.g. during autosizing) + // final call needed after autosize complete + nomCap = rs_capH; + } + else if (rs_IsCHDHW()) + { + nomCap = rs_capH; + + } + else + { // non-HP, non-CHDHW + nomCap = rs_capH; + if (!rs_CanHaveAuxHeat()) + rs_capAuxH = 0.f; // insurance + } + rs_amfH = nomCap / (rs_tdDesH * Top.tp_airSH); // nominal full speed dry-air mass flow rate, lb/hr + avfH = AMFtoAVF( rs_amfH); + } + rs_fanHeatH = avfH * rs_fanPwrH * BtuperWh; + + rs_DefaultCapNomsIf(); // update nominal capacities (no calc effect) + + return rc; + +} // RSYS::rs_SetupCapH //----------------------------------------------------------------------------- -float RSYS::rs_FanHRtdPerTon( // fan heat included in ratings - float capNomTons) // total cooling or heating capacity, tons +float RSYS::rs_FanHRtdPerTon( // fan heat included in ratings + float capNomTons) // total cooling or heating capacity, tons // returns fan heat included in ratings, Btuh { - float fanHRtdPerTon = - rs_IsPkgRoom() || rs_adjForFanHt == C_NOYESCH_NO - ? 0.f // PkgRoom or user override: no fan adjustment - : rs_fan.fn_motTy == C_MOTTYCH_PSC - ? 500.f // PSC = Permanent Split Capacitor - // use .365 W/cfm - // .365 W/cfm * 400 cfm/ton * 3.412 Btuh/W = 498 Btuh + float fanHRtdPerTon = + rs_IsPkgRoom() || rs_adjForFanHt == C_NOYESCH_NO + ? 0.f // PkgRoom or user override: no fan adjustment + : rs_fan.fn_motTy == C_MOTTYCH_PSC ? 500.f // PSC = Permanent Split Capacitor + // use .365 W/cfm + // .365 W/cfm * 400 cfm/ton * 3.412 Btuh/W = 498 Btuh + + // else BPM = Brushless Permanent Magnet (aka ECM) + : rs_IsWSHP() ? 212.f // WSHP: rated with 0 external static, assume .156 W/cfm + : 283.f; // BPM other: representative value from Abram Conant - // else BPM = Brushless Permanent Magnet (aka ECM) - : rs_IsWSHP() - ? 212.f // WSHP: rated with 0 external static, assume .156 W/cfm - : 283.f; // BPM other: representative value from Abram Conant + return capNomTons * fanHRtdPerTon; - return capNomTons * fanHRtdPerTon; - -} // RSYS::rs_FanHRtdPerTon +} // RSYS::rs_FanHRtdPerTon //----------------------------------------------------------------------------- -void RSYS::rs_SetupFanC( // derive fan cooling fan info - float avfC /*=-1.f*/) // cooling AVF, cfm std air if known - // else derived from rs_cap95 +void RSYS::rs_SetupFanC( // derive fan cooling fan info + float avfC /*=-1.f*/) // cooling AVF, cfm std air if known + // else derived from rs_cap95 // sets rs_amfC, rs_fanHeatC, rs_fanRtdC // also can set rs_cap95 (if avfC > 0) { - float capNomTons; - if (avfC > 0.f) { - rs_amfC = AVFtoAMF(avfC); - rs_cap95 = rs_ClgCapForAMF(rs_amfC); - capNomTons = rs_ClgCapNomTons(); - } else { - capNomTons = rs_ClgCapNomTons(); - avfC = rs_vfPerTon * capNomTons; // avf (standard air) - rs_amfC = AVFtoAMF(avfC); // amf using standard air density, lbm/hr - } - - rs_fanHeatC = - avfC * rs_fanPwrC * BtuperWh; // full speed operating fan power, Btuh - - rs_fanHRtdC = - rs_FanHRtdPerTon(capNomTons); // fan heat included in rated capacity, Btuh - // rs_fanHRtdH is derived independently - -} // RSYS::rs_SetupFanC -//----------------------------------------------------------------------------- -RC RSYS::rs_SetupCapC( // derive constants that depend on capacity - float avfC /*=-1*/, // air flow if known, passed to rs_SetupFanC - [[maybe_unused]] int options /*=0*/) // option bits TBD -// sets RSYS cooling members that do not vary during simulation -{ - RC rc = RCOK; - - rs_SetupFanC(avfC); // sets rs_cap95 if avfC > 0., else sets rs_amfC - // derives rs_fanHRtdC - - // rs_cap95 now known = net total cooling cap at 95 F (> 0) + float capNomTons; + if (avfC > 0.f) + { rs_amfC = AVFtoAMF( avfC); + rs_cap95 = rs_ClgCapForAMF( rs_amfC); + capNomTons = rs_ClgCapNomTons(); + } + else + { capNomTons = rs_ClgCapNomTons(); + avfC = rs_vfPerTon * capNomTons; // avf (standard air) + rs_amfC = AVFtoAMF( avfC); // amf using standard air density, lbm/hr + } - // notes re capacity manipulations - // load = capSensT = capnf*shr - qFanOp - // capnf = (cap95 + qfanRat)*fChg*fSize*fCondCap - // let F = fChg*fCondCap*shr - // load = (cap95 + qFanRat)*F - qFanOp - // load = (cap95*(1 + fanRatX))*F - cap95*fanOpX - // load = cap95*(F*(1+fanRatX)-fanOpX - // cap95 = (load + fanOpX)/(F*(1 + fanRatX)) + rs_fanHeatC = avfC * rs_fanPwrC * BtuperWh; // full speed operating fan power, Btuh - // rs_capnfX used by several models - float cap95nf = -(fabs(rs_cap95) + - rs_fanHRtdC); // coil (gross) total capacity at 95 F, Btuh - // (< 0) - rs_capnfX = min(cap95nf * rs_fChg, -10.f); // apply charge adjustment + rs_fanHRtdC = rs_FanHRtdPerTon( capNomTons); // fan heat included in rated capacity, Btuh + // rs_fanHRtdH is derived independently - if (rs_Is1Spd()) { - // base value for SEERnf and EERnf calculations - // used only for single speed - float inpX = - 1.09f * rs_cap95 / rs_SEER - rs_fanHRtdC / 3.413f; // input power, W - rs_SEERnfX = inpX > 0.f ? rs_fChg * (1.09f * rs_cap95 + rs_fanHRtdC) / inpX - : rs_SEER; +} // RSYS::rs_SetupFanC +//----------------------------------------------------------------------------- +RC RSYS::rs_SetupCapC( // derive constants that depend on capacity + float avfC /*=-1*/, // air flow if known, passed to rs_SetupFanC + [[maybe_unused]] int options /*=0*/) // option bits TBD +// sets RSYS cooling members that do not vary during simulation +{ + RC rc = RCOK; - inpX = rs_cap95 / rs_EER95 - rs_fanHRtdC / 3.413f; - rs_EERnfX = inpX > 0.f ? -rs_capnfX / inpX : rs_EER95; // gross total EER - } + rs_SetupFanC( avfC); // sets rs_cap95 if avfC > 0., else sets rs_amfC + // derives rs_fanHRtdC + + // rs_cap95 now known = net total cooling cap at 95 F (> 0) + + // notes re capacity manipulations + // load = capSensT = capnf*shr - qFanOp + // capnf = (cap95 + qfanRat)*fChg*fSize*fCondCap + // let F = fChg*fCondCap*shr + // load = (cap95 + qFanRat)*F - qFanOp + // load = (cap95*(1 + fanRatX))*F - cap95*fanOpX + // load = cap95*(F*(1+fanRatX)-fanOpX + // cap95 = (load + fanOpX)/(F*(1 + fanRatX)) + + // rs_capnfX used by several models + float cap95nf = -(fabs(rs_cap95) + rs_fanHRtdC); // coil (gross) total capacity at 95 F, Btuh + // (< 0) + rs_capnfX = min( cap95nf * rs_fChg, -10.f); // apply charge adjustment + + if (rs_Is1Spd()) + { + // base value for SEERnf and EERnf calculations + // used only for single speed + float inpX = 1.09f * rs_cap95 / rs_SEER - rs_fanHRtdC / 3.413f; // input power, W + rs_SEERnfX = inpX > 0.f ? rs_fChg * (1.09f * rs_cap95 + rs_fanHRtdC) / inpX + : rs_SEER; + + inpX = rs_cap95 / rs_EER95 - rs_fanHRtdC / 3.413f; + rs_EERnfX = inpX > 0.f ? -rs_capnfX / inpX : rs_EER95; // gross total EER + } - if (rs_IsVCClg()) { - // default other cooling capacities if needed - if (!IsSet(RSYS_CAP82)) - rs_cap82 = rs_capRat8295 * rs_cap95; + if (rs_IsVCClg()) + { + // default other cooling capacities if needed + if (!IsSet(RSYS_CAP82)) + rs_cap82 = rs_capRat8295 * rs_cap95; - if (!IsSet(RSYS_CAP115)) - rs_cap115 = rs_capRat11595 * rs_cap95; + if (!IsSet(RSYS_CAP115)) + rs_cap115 = rs_capRat11595 * rs_cap95; - // cooling performance map - // currently (2-22) used for rs_IsVC() only - rc |= rs_SetupBtwxtClg(); - } + // cooling performance map + // currently (2-22) used for rs_IsVC() only + rc |= rs_SetupBtwxtClg(); + } - rs_DefaultCapNomsIf(); // update nominal capacities (no calc effect) + rs_DefaultCapNomsIf(); // update nominal capacities (no calc effect) - return rc; + return rc; -} // RSYS::rs_SetupCapC +} // RSYS::rs_SetupCapC //---------------------------------------------------------------------------- #if 0 RC RSYS::rs_SetDuctSizes() @@ -3336,231 +3349,236 @@ RC RSYS::rs_SetDuctSizes() { DUCTSEG* pDS = DsR[ iDS]; } } -} +} #endif //---------------------------------------------------------------------------- int RSYS::rs_IsAutoSizing() const // returns rsmOFF, rsmHEAT, or rsmCOOL { - return (Top.tp_dsDay == 1 && rs_isAuszH) ? rsmHEAT - : (Top.tp_dsDay == 2 && rs_isAuszC) ? rsmCOOL - : rsmOFF; // = 0 -} // RSYS::rs_IsAutoSizing + return (Top.tp_dsDay == 1 && rs_isAuszH) ? rsmHEAT + : (Top.tp_dsDay == 2 && rs_isAuszC) ? rsmCOOL + : rsmOFF; // = 0 +} // RSYS::rs_IsAutoSizing //---------------------------------------------------------------------------- -RC RSYS::rs_BegHour() { - RC rc = RCOK; - - if (Top.isBegDay) { // init capacity ratios for day - // track smallest fxCap for heating and cooling - // = largest load relative to capacity - // thus init to large value - rs_fxCapHDay = rs_fxCapCDay = 20.f; - - // testing/debugging aids - // insurance: do at sim beg so autosizing complete - if (Top.tp_isBegMainSim) { -#if 0 && defined(_DEBUG) +RC RSYS::rs_BegHour() +{ RC rc = RCOK; + + if (Top.isBegDay) + { // init capacity ratios for day + // track smallest fxCap for heating and cooling + // = largest load relative to capacity + // thus init to large value + rs_fxCapHDay = rs_fxCapCDay = 20.f; + + // testing/debugging aids + // insurance: do at sim beg so autosizing complete + if (Top.tp_isBegMainSim) + { +#if 0 && defined( _DEBUG) if (rs_IsASHP()) { // back-derive HSPF (self-check) rs_HSPFCheckASHP( 0x100 + 0); rs_PerfDataASHP(); // write CSV file of performance points } #endif - if (rs_perfMap == C_NOYESCH_YES) - rs_PerfMapAC(); // make performance map - } + if (rs_perfMap == C_NOYESCH_YES) + rs_PerfMapAC(); // make performance map + } - // OAV daily setup - rs_OAVAirFlow(); - } + // OAV daily setup + rs_OAVAirFlow(); + } - return rc; -} // RSYS::rs_BegHour + return rc; +} // RSYS::rs_BegHour //---------------------------------------------------------------------------- -RC RSYS::rs_BegSubhr() { - RC rc = RCOK; +RC RSYS::rs_BegSubhr() +{ + RC rc = RCOK; - rs_tSupLs = rs_asSup.as_tdb; - rs_mode = rsmOFF; // mode not yet known - rs_ClearSubhrResults(); - RsResR[ss].curr.S.rsr_Zero(); // clear associated RSYSRES results + rs_tSupLs = rs_asSup.as_tdb; + rs_mode = rsmOFF; // mode not yet known + rs_ClearSubhrResults(); + RsResR[ss].curr.S.rsr_Zero(); // clear associated RSYSRES results - rs_fxCap[0] = rs_fxCap[1] = 0.f; // excess capacity not known + rs_fxCap[ 0] = rs_fxCap[ 1] = 0.f; // excess capacity not known - if (!IsSet(RSYS_TDBOUT)) - rs_tdbOut = Top.tDbOSh; // default outdoor temp - // else set via input expression + if (!IsSet( RSYS_TDBOUT)) + rs_tdbOut = Top.tDbOSh; // default outdoor temp + // else set via input expression - if (!IsSet(RSYS_OAVTDBINLET)) - rs_OAVTdbInlet = Top.tDbOSh; - // else set via input expression + if (!IsSet( RSYS_OAVTDBINLET)) + rs_OAVTdbInlet = Top.tDbOSh; + // else set via input expression - return rc; -} // RSYS::rs_BegSubhr + return rc; +} // RSYS::rs_BegSubhr //---------------------------------------------------------------------------- -RC RSYS::rs_EndSubhr() { - RC rc = RCOK; - - // track excess capacity for day - // used in re autosizing - if (rs_fxCap[0] > 0.f) { - if (rs_mode == rsmHEAT) { - if (rs_fxCap[0] < rs_fxCapHDay) - rs_fxCapHDay = rs_fxCap[0]; - } else if (rs_mode == rsmCOOL) { - if (rs_fxCap[0] < rs_fxCapCDay) - rs_fxCapCDay = rs_fxCap[0]; - } - } - - // populate results - // Note: MTRs and LOADMTs are cleared in ::doBegIvl() - RSYSRES_IVL_SUB &R = RsResR[ss].curr.S; - - R.fhTot = R.fhParasitic = - rs_parFuel * Top.tp_subhrDur; // assign fuel parasitics to heating - if (rs_parElec > 0.f) { // assign electrical parasitics per current mode or - // last active mode - float ePar = rs_parElec * BtuperWh * Top.tp_subhrDur; - int modeX = rs_mode != rsmOFF ? rs_mode : rs_modeLastActive; - if (modeX <= rsmHEAT) - R.ehTot = R.ehParasitic = ePar; // rsmOFF and rsmHEAT -> heating - else if (modeX == rsmCOOL) - R.ecTot = R.ecParasitic = ePar; - else - R.evTot = R.evParasitic = ePar; - } - - R.hrsOn = rs_runF * Top.tp_subhrDur; - float qFan = rs_outFan * Top.tp_subhrDur; - float eFan = rs_inFan * Top.tp_subhrDur; - - if (rs_mode == rsmHEAT) { - R.hrsOnAux = rs_runFAux * Top.tp_subhrDur; - R.qhPrimary = rs_outSen * Top.tp_subhrDur; - R.qhDefrost = rs_outDefrost * Top.tp_subhrDur; - R.qhAux = rs_outAux * Top.tp_subhrDur; - R.qhFan = qFan; - R.qhNet = R.qhPrimary + R.qhDefrost + R.qhAux + R.qhFan; - - // heating input electricity and fuel (rs_xxx values are Btuh) - (rs_IsElecHeat() ? R.ehPrimary : R.fhPrimary) = - rs_inPrimary * Top.tp_subhrDur; - if (rs_IsFuelAuxH()) { - R.fhDefrost = rs_inDefrost * Top.tp_subhrDur; - R.fhAux = rs_inAux * Top.tp_subhrDur; - } else { - R.ehDefrost = rs_inDefrost * Top.tp_subhrDur; - R.ehAux = rs_inAux * Top.tp_subhrDur; - } - R.ehFan = eFan; - R.ehTot += R.ehPrimary + R.ehDefrost + R.ehAux + R.ehFan; - /* + R.ehParasitic see above */ - R.fhTot += R.fhPrimary + R.fhDefrost + R.fhAux /* + R.fhParasitic*/; - - for (int iM = 0; iM < 2; iM++) { // accumulate values to LOADMETERs - // [0] accums both heating and cooling - // [1] accums heating - if (rs_pLoadMtr[iM]) // primary = coil loads - rs_pLoadMtr[iM]->S.qHtg += R.qhPrimary; - if (rs_pSrcSideLoadMtr[iM]) // source-side (= "outdoor coil" or - // environment source) - // + = from env: >0 during heating = coil heating - compressor - // electricity - rs_pSrcSideLoadMtr[iM]->S.qHtg += R.qhPrimary - R.ehPrimary; - } - } else if (rs_mode == rsmCOOL) { - R.qcSen = rs_outSen * Top.tp_subhrDur; - R.qcLat = rs_outLat * Top.tp_subhrDur; - R.qcFan = qFan; - R.qcSenNet = R.qcSen + R.qcFan; - - R.ecPrimary = rs_inPrimary * Top.tp_subhrDur; - R.ecFan = eFan; - - R.ecTot += R.ecPrimary + R.ecFan /* + R.ecParasitic, see above */; - - for (int iM = 0; iM < 3; iM += 2) { // accumulate values to LOADMETERs - // [0] accums both heating and cooling - // [2] accums cooling - if (rs_pLoadMtr[iM]) // primary = coil loads - rs_pLoadMtr[iM]->S.qClg += R.qcSen + R.qcLat; - if (rs_pSrcSideLoadMtr[iM]) // source-side (= "outdoor coil" or - // environment source) - // + = from env: <0 when cooling = total cooling + compressor - // electricity - rs_pSrcSideLoadMtr[iM]->S.qClg += R.qcSen + R.qcLat - R.ecPrimary; - } - } else if (rs_mode == rsmOAV) { // sensible "output" = fan power - R.evFan = eFan; - R.evTot += R.evFan; /* + R.evParasitic, see above */ - } - - // verify RSYSRES_IVL_SUB layout at compile time - // fixed sequence allows array access by rs_mode (see code below) - // rsmHEAT/rsmCOOL/rsmOAV definitions must be consistent with member - // sequences. -#define QZONECHK(m, oDif) \ - static_assert(&(((RSYSRES_IVL_SUB *)0)->m) - \ - &(((RSYSRES_IVL_SUB *)0)->qhZoneSen) == \ - oDif, \ - "Bad seq " #m) - QZONECHK(qhZoneSen, (rsmHEAT - 1) * 2); - QZONECHK(qhZoneLat, (rsmHEAT - 1) * 2 + 1); - QZONECHK(qcZoneSen, (rsmCOOL - 1) * 2); - QZONECHK(qcZoneLat, (rsmCOOL - 1) * 2 + 1); - QZONECHK(qvZoneSen, (rsmOAV - 1) * 2); - QZONECHK(qvZoneLat, (rsmOAV - 1) * 2 + 1); - static_assert(rsmCOUNT == 4, "Bad rsm enum"); +RC RSYS::rs_EndSubhr() +{ + RC rc = RCOK; + + // track excess capacity for day + // used in re autosizing + if (rs_fxCap[ 0] > 0.f) + { if (rs_mode == rsmHEAT) + { if (rs_fxCap[ 0] < rs_fxCapHDay) + rs_fxCapHDay = rs_fxCap[ 0]; + } + else if (rs_mode == rsmCOOL) + { if (rs_fxCap[ 0] < rs_fxCapCDay) + rs_fxCapCDay = rs_fxCap[ 0]; + } + } + + // populate results + // Note: MTRs and LOADMTs are cleared in ::doBegIvl() + RSYSRES_IVL_SUB& R = RsResR[ ss].curr.S; + + R.fhTot = R.fhParasitic = rs_parFuel * Top.tp_subhrDur; // assign fuel parasitics to heating + if (rs_parElec > 0.f) + { // assign electrical parasitics per current mode or last active mode + float ePar = rs_parElec * BtuperWh * Top.tp_subhrDur; + int modeX = rs_mode != rsmOFF ? rs_mode : rs_modeLastActive; + if (modeX <= rsmHEAT) + R.ehTot = R.ehParasitic = ePar; // rsmOFF and rsmHEAT -> heating + else if (modeX == rsmCOOL) + R.ecTot = R.ecParasitic = ePar; + else + R.evTot = R.evParasitic = ePar; + } + + R.hrsOn = rs_runF * Top.tp_subhrDur; + float qFan = rs_outFan * Top.tp_subhrDur; + float eFan = rs_inFan * Top.tp_subhrDur; + + if (rs_mode == rsmHEAT) + { + R.hrsOnAux = rs_runFAux * Top.tp_subhrDur; + R.qhPrimary = rs_outSen * Top.tp_subhrDur; + R.qhDefrost = rs_outDefrost * Top.tp_subhrDur; + R.qhAux = rs_outAux * Top.tp_subhrDur; + R.qhFan = qFan; + R.qhNet = R.qhPrimary + R.qhDefrost + R.qhAux + R.qhFan; + + // heating input electricity and fuel (rs_xxx values are Btuh) + (rs_IsElecHeat() ? R.ehPrimary : R.fhPrimary) = rs_inPrimary * Top.tp_subhrDur; + if (rs_IsFuelAuxH()) + { R.fhDefrost = rs_inDefrost * Top.tp_subhrDur; + R.fhAux = rs_inAux * Top.tp_subhrDur; + } + else + { R.ehDefrost = rs_inDefrost * Top.tp_subhrDur; + R.ehAux = rs_inAux * Top.tp_subhrDur; + } + R.ehFan = eFan; + R.ehTot += R.ehPrimary + R.ehDefrost + R.ehAux + R.ehFan; + /* + R.ehParasitic see above */ + R.fhTot += R.fhPrimary + R.fhDefrost + R.fhAux /* + R.fhParasitic*/; + + for (int iM = 0; iM < 2; iM++) + { // accumulate values to LOADMETERs + // [0] accums both heating and cooling + // [1] accums heating + if (rs_pLoadMtr[ iM]) // primary = coil loads + rs_pLoadMtr[ iM]->S.qHtg += R.qhPrimary; + if (rs_pSrcSideLoadMtr[ iM]) // source-side (= "outdoor coil" or environment source) + // + = from env: >0 during heating = coil heating - compressor electricity + rs_pSrcSideLoadMtr[ iM]->S.qHtg += R.qhPrimary - R.ehPrimary; + } + } + else if (rs_mode == rsmCOOL) + { + R.qcSen = rs_outSen * Top.tp_subhrDur; + R.qcLat = rs_outLat * Top.tp_subhrDur; + R.qcFan = qFan; + R.qcSenNet = R.qcSen + R.qcFan; + + R.ecPrimary = rs_inPrimary * Top.tp_subhrDur; + R.ecFan = eFan; + + R.ecTot += R.ecPrimary + R.ecFan /* + R.ecParasitic, see above */; + + for (int iM = 0; iM < 3; iM += 2) + { // accumulate values to LOADMETERs + // [0] accums both heating and cooling + // [2] accums cooling + if (rs_pLoadMtr[iM]) // primary = coil loads + rs_pLoadMtr[iM]->S.qClg += R.qcSen + R.qcLat; + if (rs_pSrcSideLoadMtr[iM]) // source-side (= "outdoor coil" or environment source) + // + = from env: <0 when cooling = total cooling + compressor electricity + rs_pSrcSideLoadMtr[iM]->S.qClg += R.qcSen + R.qcLat - R.ecPrimary; + } + } + else if (rs_mode == rsmOAV) + { // sensible "output" = fan power + R.evFan = eFan; + R.evTot += R.evFan; /* + R.evParasitic, see above */ + } + + // verify RSYSRES_IVL_SUB layout at compile time + // fixed sequence allows array access by rs_mode (see code below) + // rsmHEAT/rsmCOOL/rsmOAV definitions must be consistent with member sequences. +#if 0 // TODO: Fix for non-MSVC compilers +#define QZONECHK( m, oDif) static_assert( &(((RSYSRES_IVL_SUB *)0)->m)-&(((RSYSRES_IVL_SUB *)0)->qhZoneSen) == oDif, "Bad seq " #m) + QZONECHK(qhZoneSen, (rsmHEAT - 1) * 2); + QZONECHK(qhZoneLat, (rsmHEAT - 1) * 2 + 1); + QZONECHK(qcZoneSen, (rsmCOOL - 1) * 2); + QZONECHK(qcZoneLat, (rsmCOOL - 1) * 2 + 1); + QZONECHK(qvZoneSen, (rsmOAV - 1) * 2); + QZONECHK(qvZoneLat, (rsmOAV - 1) * 2 + 1); + static_assert(rsmCOUNT == 4, "Bad rsm enum"); #undef QZONECHK +#endif + if (rs_mode != rsmOFF) + { // heat transfers from this RSYS to all zones (including duct losses) + const ZNR* zp; + int iMX = 2 * (rs_mode - 1); + RLUPC(ZrB, zp, rs_IsZoneServed(zp)) + { + (&R.qhZoneSen)[iMX] += float(zp->zn_qsHvac * Top.tp_subhrDur); + (&R.qhZoneLat)[iMX] += float(zp->zn_qlHvac * Top.tp_subhrDur); + } + } - if (rs_mode != rsmOFF) { // heat transfers from this RSYS to all zones - // (including duct losses) - const ZNR *zp; - int iMX = 2 * (rs_mode - 1); - RLUPC(ZrB, zp, rs_IsZoneServed(zp)) { - (&R.qhZoneSen)[iMX] += float(zp->zn_qsHvac * Top.tp_subhrDur); - (&R.qhZoneLat)[iMX] += float(zp->zn_qlHvac * Top.tp_subhrDur); - } - } - - return rc; -} // RSYS::rs_EndSubhr + return rc; +} // RSYS::rs_EndSubhr //----------------------------------------------------------------------------- RC RSYS::rs_AfterSubhr() // end-subhour after-exprs/reports stuff for loads // set 'prior interval' variables etc: if done sooner, probes come out wrong. { - rs_modeLs = rs_mode; - if (rs_mode != rsmOFF) - rs_modeLastActive = rs_mode; - rs_loadFLs = rs_loadF; - return RCOK; -} // RSYS::rs_AfterSubhr + rs_modeLs = rs_mode; + if (rs_mode != rsmOFF) + rs_modeLastActive = rs_mode; + rs_loadFLs = rs_loadF; + return RCOK; +} // RSYS::rs_AfterSubhr //----------------------------------------------------------------------------- RC RSYS::rs_AfterHour() // end-hour after-exprs/reports stuff for loads // set 'prior interval' variables etc: if done sooner, probes come out wrong. { - RC rc = RCOK; + RC rc = RCOK; - if (Top.isEndDay) { // last step of day - int auszMode = rs_IsAutoSizing(); - if (auszMode != rsmOFF) - rc = rs_endP1DsdIter(auszMode); - } + if (Top.isEndDay) + { // last step of day + int auszMode = rs_IsAutoSizing(); + if (auszMode != rsmOFF) + rc = rs_endP1DsdIter( auszMode); + } - // prior interval values: none + // prior interval values: none - return rc; -} // RSYS::rs_AfterHour + return rc; +} // RSYS::rs_AfterHour //----------------------------------------------------------------------------- void RSYS::rs_HeatingOutletAirState( - int auszMode /*=rsmOff*/) // active autosizing, if any - // rsmOFF: none (normal simulation) - // rsmHEAT: heating - // rsmCOOL: do not call + int auszMode /*=rsmOff*/) // active autosizing, if any + // rsmOFF: none (normal simulation) + // rsmHEAT: heating + // rsmCOOL: do not call // derives RSYS outlet conditions (before distribution losses) for heating mode @@ -3574,92 +3592,104 @@ void RSYS::rs_HeatingOutletAirState( // (plus intermediate correlation results) { - // rs_asOut = rs_asIn; // init to entering state (by caller) - - rs_tdbCoilIn = rs_asOut.as_tdb; - - // determine current heating capacity and efficiency - - float capHt = 0.f; // current capacity, Btuh - // = rs_capHt expect for variable speed - - if (auszMode == rsmHEAT && - Top.tp_pass1A) { // autosize warmup: assume fixed temp rise - rs_capHt = capHt = - rs_asOut.as_CalcQSen2(rs_asRet.as_tdb + rs_tdDesH, rs_amf); - rs_effHt = 1.f; // need nz value, else ASHP assumes compressor off - } else if (rs_IsASHP()) { - if (auszMode != rsmOFF) { // ASHP heat autosize (based on rs_capH) - rs_effHt = 1.f; - rs_capHt = capHt = rs_capH; - rs_capAuxH = rs_capH; // same cap for aux during autosizing - // used below if needed - } else { // ASHP simulation (not autosize) - // run full model - capHt = rs_CapEffASHP2(); // sets rs_capHt and rs_EffHt - } - } else if (rs_IsWSHP()) { - const float airMassFlowF = 1.f; // temporary assumption - float capF, inpF; - /*rc |=*/WSHPPerf.whp_HeatingFactors(capF, inpF, rs_tdbOut, rs_tdbCoilIn, - airMassFlowF); - rs_capHt = capHt = (rs_capH - rs_fanHRtdH) * capF; // gross heating capacity - float inpX = - ((rs_capH / rs_COP47) - rs_fanHRtdH) * inpF; // gross input power - rs_effHt = rs_capHt / inpX * rs_fEffH; - } else if (rs_IsCHDHW()) { - if (rs_capHt == 0.f) { // do initial setup only once - rs_CurCapHtCHDHW(); - } - capHt = rs_capHt * rs_speedF; - rs_effHt = 1.f; - } else { // not heat pump of any type - rs_effHt = rs_IsFanCoil() ? 1.f : rs_AFUE * rs_fEffH; - rs_capHt = capHt = rs_capH; // includes fan heat - } - - // add heat to air stream - rs_asOut.as_AddQSen2(capHt, rs_amf); - - // auxiliary heat supply air state - // rs_capAuxH is known - if (rs_CanHaveAuxHeat() && rs_speedF == 1.f) { - // double tdbWas = rs_asOutAux.as_tdb; - float fanHeatAux; - if (rs_ctrlAuxH == - C_AUXHEATCTRL_CYCLE) { // aux cycles: aux out = prim out + aux heat - // fan heat already in prim out - rs_asOutAux = rs_asOut; - fanHeatAux = 0.f; - } else { // aux alternate or locked out: prim off - // fan heat must be added - rs_asOutAux = rs_asIn; - fanHeatAux = rs_fanHeatH; - } - - rs_asOutAux.as_AddQSen2(rs_capAuxH + fanHeatAux, rs_amf); - rs_asSupAux = rs_asOutAux; - rs_SupplyDSEAndDucts(rs_asSupAux); - } - -#if defined(RSYS_FIXEDOAT) - rs_asSup.as_Set(120., .001); -#endif - return; -} // RSYS::rs_HeatingOutletAirState + // rs_asOut = rs_asIn; // init to entering state (by caller) + + rs_tdbCoilIn = rs_asOut.as_tdb; + + // determine current heating capacity and efficiency + + float capHt = 0.f; // current capacity, Btuh + // = rs_capHt expect for variable speed + + if (auszMode == rsmHEAT && Top.tp_pass1A) + { // autosize warmup: assume fixed temp rise + rs_capHt = capHt = rs_asOut.as_CalcQSen2(rs_asRet.as_tdb + rs_tdDesH, rs_amf); + rs_effHt = 1.f; // need nz value, else ASHP assumes compressor off + } + else if (rs_IsASHP()) + { + if (auszMode != rsmOFF) + { // ASHP heat autosize (based on rs_capH) + rs_effHt = 1.f; + rs_capHt = capHt = rs_capH; + rs_capAuxH = rs_capH; // same cap for aux during autosizing + // used below if needed + } + else + { // ASHP simulation (not autosize) + // run full model + capHt = rs_CapEffASHP2(); // sets rs_capHt and rs_EffHt + } + } + else if (rs_IsWSHP()) + { + const float airMassFlowF = 1.f; // temporary assumption + float capF, inpF; + /*rc |=*/ WSHPPerf.whp_HeatingFactors(capF, inpF, rs_tdbOut, rs_tdbCoilIn, airMassFlowF); + rs_capHt = capHt = (rs_capH - rs_fanHRtdH) * capF; // gross heating capacity + float inpX = ((rs_capH / rs_COP47) - rs_fanHRtdH) * inpF; // gross input power + rs_effHt = rs_capHt / inpX * rs_fEffH; + } + else if (rs_IsCHDHW()) + { + if (rs_capHt == 0.f) + { // do initial setup only once + rs_CurCapHtCHDHW(); + } + capHt = rs_capHt * rs_speedF; + rs_effHt = 1.f; + } + else + { // not heat pump of any type + rs_effHt = rs_IsFanCoil() ? 1.f : rs_AFUE * rs_fEffH; + rs_capHt = capHt = rs_capH; // includes fan heat + } + + // add heat to air stream + rs_asOut.as_AddQSen2( capHt, rs_amf); + + // auxiliary heat supply air state + // rs_capAuxH is known + if (rs_CanHaveAuxHeat() && rs_speedF == 1.f) + { + // double tdbWas = rs_asOutAux.as_tdb; + float fanHeatAux; + if (rs_ctrlAuxH == C_AUXHEATCTRL_CYCLE) + { // aux cycles: aux out = prim out + aux heat + // fan heat already in prim out + rs_asOutAux = rs_asOut; + fanHeatAux = 0.f; + } + else + { // aux alternate or locked out: prim off + // fan heat must be added + rs_asOutAux = rs_asIn; + fanHeatAux = rs_fanHeatH; + } + + rs_asOutAux.as_AddQSen2(rs_capAuxH + fanHeatAux, rs_amf); + rs_asSupAux = rs_asOutAux; + rs_SupplyDSEAndDucts(rs_asSupAux); + } + +#if defined( RSYS_FIXEDOAT) + rs_asSup.as_Set(120., .001); +#endif + return; +} // RSYS::rs_HeatingOutletAirState //----------------------------------------------------------------------------- -void RSYS::rs_CoolingSHR() // derive cooling sensible heat ratio +void RSYS::rs_CoolingSHR() // derive cooling sensible heat ratio // call: rs_tdbCoilIn = coil entering dry bulb, F // rs_twbCoilIn = coil entering wet bulb, F // rs_tdbOut = outdoor drybulb seen by condenser, F // rs_vfPerTon = indoor std air cfm / ton // return: rs_SHR = SHR under current conditions { -#if 0 && defined(_DEBUG) +#if 0 && defined( _DEBUG) x if (!Top.isWarmup) x printf("Hit\n"); #endif -#if 0 && defined(_DEBUG) +#if 0 && defined( _DEBUG) if (!Top.isWarmup) { if (fabs(rs_asIn.as_tdb - 80.) < .2 @@ -3668,164 +3698,169 @@ x printf("Hit\n"); printf("Hit\n"); } #endif -#if 0 && defined(_DEBUG) +#if 0 && defined( _DEBUG) rs_tdbOut = 95.f; rs_tdbCoilIn = 80.f; rs_twbCoilIn = 67.f; rs_vfPerTon = 400.f; #endif - rs_SHR = CoolingSHR(rs_tdbOut, rs_tdbCoilIn, rs_twbCoilIn, rs_vfPerTon); + rs_SHR = CoolingSHR(rs_tdbOut, rs_tdbCoilIn, rs_twbCoilIn, rs_vfPerTon); -} // RSYS::rs_CoolingSHR +} // RSYS::rs_CoolingSHR //----------------------------------------------------------------------------- -static float CoolingCapF1Spd( // capacity factor for 1 spd model - float SHR, // sensible heat ratio for current conditions - float tdbOut, // outdoor dry bulb, F - float tdbCoilIn, // coil entering dry bulb, F - float twbCoilIn, // coil entering wet bulb, F - float vfPerTon) // coil air flow std air cfm / ton +static float CoolingCapF1Spd( // capacity factor for 1 spd model + float SHR, // sensible heat ratio for current conditions + float tdbOut, // outdoor dry bulb, F + float tdbCoilIn, // coil entering dry bulb, F + float twbCoilIn, // coil entering wet bulb, F + float vfPerTon) // coil air flow std air cfm / ton // return: fCondCap = current conditions factor for total gross capacity { - float fCondCap; - if (SHR > 0.9999f) - fCondCap = // dry coil - 0.009483100f * tdbCoilIn - // + 0.f * twbCoilIn - - 0.000600600f * tdbOut - 0.000148900f * vfPerTon - - 0.000032600f * tdbCoilIn * tdbOut + - 0.000011900f * tdbCoilIn * vfPerTon - // + 0.f * twbCoilIn * tdbOut - // + 0.f * twbCoilIn * vfPerTon - - 0.000005050f * tdbOut * vfPerTon - // + 0.f * twbCoilIn * twbCoilIn - - 52.561740000f / vfPerTon + 0.430751600f; - else - fCondCap = // wet coil - // 0.f * * tdbCoilIn - +0.009645900f * twbCoilIn + 0.002536900f * tdbOut + - 0.000171500f * vfPerTon - // + 0.f * tdbCoilIn* tdbOut - // + 0.f * tdbCoilIn* vfPerTon - - 0.000095900f * twbCoilIn * tdbOut + - 0.000008180f * twbCoilIn * vfPerTon - 0.000007550f * tdbOut * vfPerTon + - 0.000105700f * twbCoilIn * twbCoilIn - 53.542300000f / vfPerTon + - 0.381567150f; - - return fCondCap; - -} // CoolingCapF1Spd + float fCondCap; + if (SHR > 0.9999f) + fCondCap = // dry coil + 0.009483100f * tdbCoilIn + // + 0.f * twbCoilIn + - 0.000600600f * tdbOut + - 0.000148900f * vfPerTon + - 0.000032600f * tdbCoilIn * tdbOut + + 0.000011900f * tdbCoilIn * vfPerTon + // + 0.f * twbCoilIn * tdbOut + // + 0.f * twbCoilIn * vfPerTon + - 0.000005050f * tdbOut * vfPerTon + // + 0.f * twbCoilIn * twbCoilIn + - 52.561740000f / vfPerTon + + 0.430751600f; + else + fCondCap = // wet coil + // 0.f * * tdbCoilIn + + 0.009645900f * twbCoilIn + + 0.002536900f * tdbOut + + 0.000171500f * vfPerTon + // + 0.f * tdbCoilIn* tdbOut + // + 0.f * tdbCoilIn* vfPerTon + - 0.000095900f * twbCoilIn * tdbOut + + 0.000008180f * twbCoilIn * vfPerTon + - 0.000007550f * tdbOut * vfPerTon + + 0.000105700f * twbCoilIn * twbCoilIn + - 53.542300000f / vfPerTon + + 0.381567150f; + + return fCondCap; + +} // CoolingCapF1Spd //----------------------------------------------------------------------------- -float RSYS::rs_CoolingCapF1Spd( // capacity factor for 1 spd model - float tdbOut /*=0.f*/) +float RSYS::rs_CoolingCapF1Spd( // capacity factor for 1 spd model + float tdbOut /*=0.f*/) // return: fCondCap = current conditions factor for total gross capacity { - if (tdbOut == 0.f) - tdbOut = rs_tdbOut; + if (tdbOut == 0.f) + tdbOut = rs_tdbOut; - rs_fCondCap = - CoolingCapF1Spd(rs_SHR, tdbOut, rs_tdbCoilIn, rs_twbCoilIn, rs_vfPerTon); - return rs_fCondCap; + rs_fCondCap = CoolingCapF1Spd(rs_SHR, tdbOut, rs_tdbCoilIn, rs_twbCoilIn, rs_vfPerTon); + return rs_fCondCap; -} // RSYS::rs_CoolingCapF1Spd +} // RSYS::rs_CoolingCapF1Spd //----------------------------------------------------------------------------- -static float CoolingInpF1Spd( // input factor for 1 spd model - float SHR, // sensible heat ratio for current conditions - float tdbOut, // outdoor dry bulb, F - float tdbCoilIn, // coil entering dry bulb, F - float twbCoilIn, // coil entering wet bulb, F - float vfPerTon, // coil air flow std air cfm / ton - float &fInpSEER) +static float CoolingInpF1Spd( // input factor for 1 spd model + float SHR, // sensible heat ratio for current conditions + float tdbOut, // outdoor dry bulb, F + float tdbCoilIn, // coil entering dry bulb, F + float twbCoilIn, // coil entering wet bulb, F + float vfPerTon, // coil air flow std air cfm / ton + float& fInpSEER) // return: fCondInp = current conditions factor for compressor input { - float fBase, fInpEER; - if (SHR > 0.9999f) { - fBase = 0.0046103f * tdbCoilIn // dry coil - // + 0.f * twbCoilIn - + 0.0125598f * tdbOut - 0.000512f * vfPerTon - - 0.0000357f * tdbCoilIn * tdbOut + 0.0000105f * tdbCoilIn * vfPerTon; - // + 0.f * twbCoilIn * tdbOut - // + 0.f * twbCoilIn * vfPerTon - // + 0.f * tdbOut * vfPerTon - // + 0.f * twbCoilIn * twbCoilIn - // + 0.f / vfPerTon; - fInpSEER = fBase - 0.316172311f; - fInpEER = fBase - 0.475306500f; - } else { - fBase = // 0.f * tdbCoilIn - -0.0202256f * twbCoilIn + 0.0236703f * tdbOut - - 0.0006638f * vfPerTon - // + 0.f * tdbCoilIn* tdbOut - // + 0.f * tdbCoilIn* vfPerTon - - 0.0001841f * twbCoilIn * tdbOut + 0.0000214f * twbCoilIn * vfPerTon - - 0.00000812f * tdbOut * vfPerTon + 0.0002971f * twbCoilIn * twbCoilIn - - 27.95672f / vfPerTon; - fInpSEER = fBase + 0.209951063f; - fInpEER = fBase + 0.015003100f; - } - - return fInpEER; -} // CoolingInpF1Spd + float fBase, fInpEER; + if (SHR > 0.9999f) + { fBase = 0.0046103f * tdbCoilIn // dry coil + // + 0.f * twbCoilIn + + 0.0125598f * tdbOut + - 0.000512f * vfPerTon + - 0.0000357f * tdbCoilIn * tdbOut + + 0.0000105f * tdbCoilIn * vfPerTon; + // + 0.f * twbCoilIn * tdbOut + // + 0.f * twbCoilIn * vfPerTon + // + 0.f * tdbOut * vfPerTon + // + 0.f * twbCoilIn * twbCoilIn + // + 0.f / vfPerTon; + fInpSEER = fBase - 0.316172311f; + fInpEER = fBase - 0.475306500f; + } + else + { + fBase = // 0.f * tdbCoilIn + - 0.0202256f * twbCoilIn + + 0.0236703f * tdbOut + - 0.0006638f * vfPerTon + // + 0.f * tdbCoilIn* tdbOut + // + 0.f * tdbCoilIn* vfPerTon + - 0.0001841f * twbCoilIn * tdbOut + + 0.0000214f * twbCoilIn * vfPerTon + - 0.00000812f * tdbOut * vfPerTon + + 0.0002971f * twbCoilIn * twbCoilIn + - 27.95672f / vfPerTon; + fInpSEER = fBase + 0.209951063f; + fInpEER = fBase + 0.015003100f; + } + + return fInpEER; +} // CoolingInpF1Spd //----------------------------------------------------------------------------- -float RSYS::rs_CoolingEff1Spd( // cooling efficiency, 1 spd model - float tdbOut /*=0.f*/) +float RSYS::rs_CoolingEff1Spd( // cooling efficiency, 1 spd model + float tdbOut /*=0.f*/) // sets rs_fCondSEER, rs_SEERnf, rs_fCondEER, rs_EERnf, rs_EERt // returns input power modifier { - if (tdbOut == 0.f) - tdbOut = rs_tdbOut; + if (tdbOut == 0.f) + tdbOut = rs_tdbOut; - float fInpSEER; - float fInpEER = CoolingInpF1Spd(rs_SHR, tdbOut, rs_tdbCoilIn, rs_twbCoilIn, - rs_vfPerTon, fInpSEER); + float fInpSEER; + float fInpEER = CoolingInpF1Spd(rs_SHR, tdbOut, rs_tdbCoilIn, rs_twbCoilIn, rs_vfPerTon, fInpSEER); - rs_fCondSEER = rs_fCondCap / fInpSEER; - rs_SEERnf = rs_SEERnfX * rs_fCondSEER; + rs_fCondSEER = rs_fCondCap / fInpSEER; + rs_SEERnf = rs_SEERnfX * rs_fCondSEER; - rs_fCondEER = rs_fCondCap / fInpEER; - rs_EERnf = rs_EERnfX * rs_fCondEER; + rs_fCondEER = rs_fCondCap / fInpEER; + rs_EERnf = rs_EERnfX * rs_fCondEER; - rs_EERt = tdbOut < 82.f ? rs_SEERnf - : tdbOut < 95.f - ? rs_SEERnf + (tdbOut - 82.f) * (rs_EERnf - rs_SEERnf) / 13.f - : rs_EERnf; + rs_EERt = tdbOut < 82.f ? rs_SEERnf + : tdbOut < 95.f ? rs_SEERnf + (tdbOut - 82.f) * (rs_EERnf - rs_SEERnf) / 13.f + : rs_EERnf; - return fInpEER; + return fInpEER; -} // RSYS::rs_CoolingEff1Spd +} // RSYS::rs_CoolingEff1Spd //----------------------------------------------------------------------------- -void RSYS::rs_CoolingEnteringAirFactorsVS( // adjustments for entering (indoor) - // air state - float &capF, // returned: capacity factor - float &inpF) const // returned: compressor input power factor +void RSYS::rs_CoolingEnteringAirFactorsVS( // adjustments for entering (indoor) air state + float& capF, // returned: capacity factor + float& inpF) const // returned: compressor input power factor { - float capFN = CoolingCapF1Spd(rs_SHR, rs_tdbOut, rs_tdbCoilIn, rs_twbCoilIn, - rs_vfPerTon); - float capFD = CoolingCapF1Spd(0.7f, rs_tdbOut, 80.f, 67.f, 400.f); - capF = capFN / capFD; + float capFN = CoolingCapF1Spd(rs_SHR, rs_tdbOut, rs_tdbCoilIn, rs_twbCoilIn, rs_vfPerTon); + float capFD = CoolingCapF1Spd(0.7f, rs_tdbOut, 80.f, 67.f, 400.f); + capF = capFN / capFD; - float fSink; - float inpFN = CoolingInpF1Spd(rs_SHR, rs_tdbOut, rs_tdbCoilIn, rs_twbCoilIn, - rs_vfPerTon, fSink); - float inpFD = CoolingInpF1Spd(0.7f, rs_tdbOut, 80.f, 67.f, 400.f, fSink); + float fSink; + float inpFN = CoolingInpF1Spd(rs_SHR, rs_tdbOut, rs_tdbCoilIn, rs_twbCoilIn, rs_vfPerTon, fSink); + float inpFD = CoolingInpF1Spd(0.7f, rs_tdbOut, 80.f, 67.f, 400.f, fSink); - inpF = inpFN / inpFD; + inpF = inpFN / inpFD; -} // RSYS::rs_CoolingEnteringAirFactorsVS +} // RSYS::rs_CoolingEnteringAirFactorsVS //----------------------------------------------------------------------------- -void RSYS::rs_CoolingOutletAirState( // cooling outlet (leaving) air state - int auszMode /*=rsmOff*/) // active autosizing, if any - // rsmOFF: none (normal simulation) - // rsmCOOL: cooling - // rsmHEAT: do not call +void RSYS::rs_CoolingOutletAirState( // cooling outlet (leaving) air state + int auszMode /*=rsmOff*/) // active autosizing, if any + // rsmOFF: none (normal simulation) + // rsmCOOL: cooling + // rsmHEAT: do not call -// derives RSYS outlet conditions (before supply distribution losses) for -// cooling mode +// derives RSYS outlet conditions (before supply distribution losses) for cooling mode // call: rs_asIn = entering state (return duct leaving state) // rs_asOut = rs_asIn (initialized for calc) @@ -3839,7 +3874,7 @@ void RSYS::rs_CoolingOutletAirState( // cooling outlet (leaving) air state // (plus intermediate correlation results) { -#if 0 && defined(_DEBUG) +#if 0 && defined( _DEBUG) static bool bDoneExport = false; if (!bDoneExport) { @@ -3848,126 +3883,120 @@ void RSYS::rs_CoolingOutletAirState( // cooling outlet (leaving) air state } #endif -#if 0 && defined(_DEBUG) +#if 0 && defined( _DEBUG) x if (Top.jDay == 242 && Top.iHr == 14) x printf("\nhit"); #endif - // fan heat: coil entering conditions - // rs_asOut = rs_asIn per caller - if (rs_fan.fanTy == C_FANTYCH_BLOWTHRU) { - rs_asOut.as_AddQSen(rs_speedF * rs_fanHeatC, rs_amf); - rs_twbCoilIn = rs_asOut.as_Twb(); - } else - rs_twbCoilIn = rs_twbIn; // use known value - rs_tdbCoilIn = rs_asOut.as_tdb; - - if (rs_IsFanCoil()) { - rs_SHR = rs_SHRtarget; // user input (may be expression) - rs_capnfX = rs_capTotCt = -rs_cap95; - rs_fCondCap = 1.f; - rs_capSenCt = rs_capTotCt * rs_SHR; - } else { // compression cooling - - rs_CoolingSHR(); - - // no fan (coil-only) capacities at current conditions (fan heat elsewhere) - // total, sensible - if (auszMode == rsmCOOL && - Top.tp_pass1A) { // autosizing warmup: assume coil can produce design - // delta-T - // (ignore sign of rs_tdDesC) - // why: produces (more) stable supply air state - rs_CoolingCapF1Spd(); // use 1 spd model for both 1 spd and VC - float coilDT = // temp diff across coil (< 0) - -fabs(rs_tdDesC) * rs_fCondCap - rs_fanDeltaTC; - rs_capSenCt = coilDT * rs_amf * Top.tp_airSH; - rs_capTotCt = rs_capSenCt / rs_SHR; // total, Btuh (note rs_SHR>0 always) - } else if (rs_IsWSHP()) { - const float airMassFlowF = 1.f; // temporary assumption - float capF, capSenF, inpF; - /* rc |= */ WSHPPerf.whp_CoolingFactors(capF, capSenF, inpF, rs_tdbOut, - rs_tdbCoilIn, rs_twbCoilIn, - airMassFlowF); - rs_capTotCt = - rs_capnfX * capF; // total gross capacity at current conditions, Btuh - rs_capSenCt = rs_SHRtarget * rs_capTotCt * - capSenF; // SHR set using air-to-air approach - if (rs_capSenCt > rs_capTotCt) { - rs_capSenCt = rs_capTotCt; - } - rs_SHR = rs_capSenCt / rs_capTotCt; - float inpX = ((rs_cap95 / (rs_EER95 / BtuperWh)) - rs_fanHRtdC) * inpF; - rs_effCt = -rs_capTotCt / inpX; - } else if (rs_Is1Spd()) { // 1 spd, not autosizing warmup, use full model - rs_CoolingCapF1Spd(); // use 1 spd model for both 1 spd - rs_capTotCt = - rs_capnfX * - rs_fCondCap; // total gross capacity at current conditions, Btuh - rs_capSenCt = - rs_SHR * - rs_capTotCt; // sensible gross capacity at current conditions, Btuh - - rs_CoolingEff1Spd(); // derive rs_EERt - - rs_effCt = rs_SHR * rs_fEffC * rs_EERt / - BtuperWh; // compressor efficiency (COP) - // = (Btuh sensible)/(Btuh compressor) - } else { - ASSERT(rs_IsVCClg()); - // variable capacity - // total capacities and input, Btuh - // include rated fan power - // cap values are net Btuh, <0 (w/o rs_fChg adjust) - float capClg, inpClg, capClgMin, inpClgMin; - /* RC rc = */ rs_GetPerfBtwxt(rs_pRgiClg, rs_tdbOut, capClg, inpClg, - capClgMin, inpClgMin); - - capClgMin = bracket(capClg, capClgMin, 0.05f * capClg); - inpClgMin = bracket(-capClgMin / 10.f, inpClgMin, inpClg); - - rs_speedFMin = capClg >= 0.f - ? 1.f - : capClgMin / capClg; // rs_speedFMin = min/max ratio - // rs_fChg adjustment cancels -#if defined(_DEBUG) - if (rs_speedFMin <= 0.f || rs_speedFMin > 1.f) - printf("\nBad rs_speedFMin"); -#endif - - // use correlations to get adjusments for entering air state - float fCondInp; - rs_CoolingEnteringAirFactorsVS(rs_fCondCap, fCondInp); + // fan heat: coil entering conditions + // rs_asOut = rs_asIn per caller + if (rs_fan.fanTy == C_FANTYCH_BLOWTHRU) + { rs_asOut.as_AddQSen(rs_speedF*rs_fanHeatC, rs_amf); + rs_twbCoilIn = rs_asOut.as_Twb(); + } + else + rs_twbCoilIn = rs_twbIn; // use known value + rs_tdbCoilIn = rs_asOut.as_tdb; - rs_capTotCt // total coil capacity (= gross) at current conditions and - // speed, Btuh - = (capClg - rs_fanHRtdC) * rs_fChg * rs_fCondCap * rs_speedF; - rs_capSenCt = rs_SHR * rs_capTotCt; // sensible coil capacity at current - // conditions and speed, Btuh + if (rs_IsFanCoil()) + { + rs_SHR = rs_SHRtarget; // user input (may be expression) + rs_capnfX = rs_capTotCt = -rs_cap95; + rs_fCondCap = 1.f; + rs_capSenCt = rs_capTotCt * rs_SHR; + } + else + { // compression cooling + + rs_CoolingSHR(); + + // no fan (coil-only) capacities at current conditions (fan heat elsewhere) + // total, sensible + if (auszMode == rsmCOOL && Top.tp_pass1A) + { // autosizing warmup: assume coil can produce design delta-T + // (ignore sign of rs_tdDesC) + // why: produces (more) stable supply air state + rs_CoolingCapF1Spd(); // use 1 spd model for both 1 spd and VC + float coilDT = // temp diff across coil (< 0) + -fabs(rs_tdDesC) * rs_fCondCap - rs_fanDeltaTC; + rs_capSenCt = coilDT * rs_amf * Top.tp_airSH; + rs_capTotCt = rs_capSenCt / rs_SHR; // total, Btuh (note rs_SHR>0 always) + } + else if (rs_IsWSHP()) + { + const float airMassFlowF = 1.f; // temporary assumption + float capF, capSenF, inpF; + /* rc |= */ WSHPPerf.whp_CoolingFactors(capF, capSenF, inpF, + rs_tdbOut, rs_tdbCoilIn, rs_twbCoilIn, airMassFlowF); + rs_capTotCt = rs_capnfX * capF; // total gross capacity at current conditions, Btuh + rs_capSenCt = rs_SHRtarget * rs_capTotCt * capSenF; // SHR set using air-to-air approach + if (rs_capSenCt > rs_capTotCt) + { + rs_capSenCt = rs_capTotCt; + } + rs_SHR = rs_capSenCt / rs_capTotCt; + float inpX = ((rs_cap95 / (rs_EER95 / BtuperWh)) - rs_fanHRtdC) * inpF; + rs_effCt = -rs_capTotCt / inpX; + } + else if (rs_Is1Spd()) + { // 1 spd, not autosizing warmup, use full model + rs_CoolingCapF1Spd(); // use 1 spd model for both 1 spd + rs_capTotCt = rs_capnfX * rs_fCondCap; // total gross capacity at current conditions, Btuh + rs_capSenCt = rs_SHR * rs_capTotCt; // sensible gross capacity at current conditions, Btuh - float inpX = - (inpClg - rs_fanHRtdC) * - fCondInp; // full speed input power at current conditions w/o fan - if (rs_speedF < 1.f) { - // capTotMinCt = (capClgMin - rs_fanHRtdC) * rs_fChg * rs_fCondCap * - // rs_speedFMin; - float inpXMin = - (inpClgMin - rs_fanHRtdC * rs_speedFMin) * - fCondInp; // min speed input power at current conditions w/o fan + rs_CoolingEff1Spd(); // derive rs_EERt - inpX = inpClgMin + (inpX - inpXMin) * (rs_speedF - rs_speedFMin) / - (1.f - rs_speedFMin); - } + rs_effCt = rs_SHR * rs_fEffC * rs_EERt / BtuperWh; // compressor efficiency (COP) + // = (Btuh sensible)/(Btuh compressor) + } + else + { + ASSERT(rs_IsVCClg()); + // variable capacity + // total capacities and input, Btuh + // include rated fan power + // cap values are net Btuh, <0 (w/o rs_fChg adjust) + float capClg, inpClg, capClgMin, inpClgMin; + /* RC rc = */ rs_GetPerfBtwxt(rs_pRgiClg, rs_tdbOut, + capClg, inpClg, capClgMin, inpClgMin); + + capClgMin = bracket(capClg, capClgMin, 0.05f * capClg); + inpClgMin = bracket(-capClgMin / 10.f, inpClgMin, inpClg); + + rs_speedFMin = capClg >= 0.f ? 1.f : capClgMin / capClg; // rs_speedFMin = min/max ratio + // rs_fChg adjustment cancels +#if defined( _DEBUG) + if (rs_speedFMin <= 0.f || rs_speedFMin > 1.f) + printf("\nBad rs_speedFMin"); +#endif + + // use correlations to get adjusments for entering air state + float fCondInp; + rs_CoolingEnteringAirFactorsVS(rs_fCondCap, fCondInp); + + rs_capTotCt // total coil capacity (= gross) at current conditions and speed, Btuh + = (capClg - rs_fanHRtdC) * rs_fChg * rs_fCondCap * rs_speedF; + rs_capSenCt = rs_SHR * rs_capTotCt; // sensible coil capacity at current conditions and speed, Btuh + + float inpX = (inpClg - rs_fanHRtdC) * fCondInp; // full speed input power at current conditions w/o fan + if (rs_speedF < 1.f) + { + // capTotMinCt = (capClgMin - rs_fanHRtdC) * rs_fChg * rs_fCondCap * rs_speedFMin; + float inpXMin = (inpClgMin - rs_fanHRtdC * rs_speedFMin) * fCondInp; // min speed input power at current conditions w/o fan + + inpX = inpClgMin + + (inpX - inpXMin) * (rs_speedF - rs_speedFMin) / (1.f - rs_speedFMin); + } - rs_effCt = abs(rs_capSenCt) / inpX; // compressor-only sensible COP - } - } + rs_effCt = abs(rs_capSenCt) / inpX; // compressor-only sensible COP + } + } - rs_capLatCt = rs_capTotCt - rs_capSenCt; // latent, Btuh + rs_capLatCt = rs_capTotCt - rs_capSenCt; // latent, Btuh - // outlet (leaving) conditions -#if defined(_DEBUG) - float hIn = rs_asIn.as_Enthalpy(); + // outlet (leaving) conditions +#if defined( _DEBUG) + float hIn = rs_asIn.as_Enthalpy(); #endif #if 0 @@ -3978,639 +4007,636 @@ x float qLat = rs_capLatCt; x int rx = rs_asOut.as_AddQSenLat( qSen, qLat, rs_amf, 0.95f); x rs_asOut = asSav; #endif - int ret = rs_asOut.as_AddQSenLat(rs_capSenCt, rs_capLatCt, rs_amf, 0.95f); - if (ret == 1) { // as_AddQSenLat() applied humidity limit - // update capacities - rs_capTotCt = rs_capSenCt + rs_capLatCt; - rs_fCondCap = rs_capTotCt / rs_capnfX; // back-calc consistent value - rs_SHR = rs_capTotCt != 0.f ? rs_capSenCt / rs_capTotCt : 0.f; - } + int ret = rs_asOut.as_AddQSenLat( rs_capSenCt, rs_capLatCt, rs_amf, 0.95f); + if (ret == 1) + { // as_AddQSenLat() applied humidity limit + // update capacities + rs_capTotCt = rs_capSenCt + rs_capLatCt; + rs_fCondCap = rs_capTotCt / rs_capnfX; // back-calc consistent value + rs_SHR = rs_capTotCt != 0.f ? rs_capSenCt / rs_capTotCt : 0.f; + } - // draw-thru fan heat - if (rs_fan.fanTy != C_FANTYCH_BLOWTHRU) - rs_asOut.as_AddQSen(rs_speedF * rs_fanHeatC, rs_amf); + // draw-thru fan heat + if (rs_fan.fanTy != C_FANTYCH_BLOWTHRU) + rs_asOut.as_AddQSen( rs_speedF*rs_fanHeatC, rs_amf); -#if defined(RSYS_FIXEDOAT) - rs_asOut.as_Set(50., .001); +#if defined( RSYS_FIXEDOAT) + rs_asOut.as_Set(50., .001); #endif - rs_capSenNetFS = - rs_capSenCt / rs_speedF + rs_fanHeatC; // net full speed sensible capacity + rs_capSenNetFS = rs_capSenCt / rs_speedF + rs_fanHeatC; // net full speed sensible capacity -#if defined(_DEBUG) - if (!Top.isWarmup) { - double tSup = rs_asOut.as_tdb; - if (tSup < 20. || tSup > 200.) - orWarn("unreasonable cooling supply temp %.2f F", tSup); +#if defined( _DEBUG) + if (!Top.isWarmup) + { double tSup = rs_asOut.as_tdb; + if (tSup < 20. || tSup > 200.) + orWarn("unreasonable cooling supply temp %.2f F", tSup); #if 0 if (rs_asIn.as_tdb - tSup < 10.) orWarn("low cooling deltaT -- tRet=%.2f F, tSup=%.2f, SHR=%.3f", rs_asIn.as_tdb, tSup, rs_SHR); #endif - float hOut = rs_asOut.as_Enthalpy(); - float hDelta = rs_amf * (hOut - hIn); - float capTot = rs_capSenCt + rs_capLatCt + rs_speedF * rs_fanHeatC; - float fDiff = frDiff(hDelta, capTot); - if (fDiff > .0002f) - orWarn("coil enthalpy mismatch (%.5f)", fDiff); - } + float hOut = rs_asOut.as_Enthalpy(); + float hDelta = rs_amf * (hOut - hIn); + float capTot = rs_capSenCt + rs_capLatCt + rs_speedF*rs_fanHeatC; + float fDiff = frDiff( hDelta, capTot); + if (fDiff > .0002f) + orWarn( "coil enthalpy mismatch (%.5f)", fDiff); + } #endif -} // RSYS::rs_CoolingOutletAirState +} // RSYS::rs_CoolingOutletAirState //---------------------------------------------------------------------------- -void RSYS::rs_GetPerfClg( // derive current cooling performance values - float &capTot, // total capacity, kBtuh - float &capSen, // sensible capacity, kBtuh - float &pwrIn) const // input power, kW +void RSYS::rs_GetPerfClg( // derive current cooling performance values + float& capTot, // total capacity, kBtuh + float& capSen, // sensible capacity, kBtuh + float& pwrIn) const // input power, kW // Used only for reporting (not simulation) 10-2018 // All returned values include fan heat / power { - pwrIn = (fabs(rs_capSenCt / rs_effCt) + rs_fanHeatC) / (BtuperWh * 1000.f); + pwrIn = (fabs( rs_capSenCt / rs_effCt) + rs_fanHeatC) + / (BtuperWh * 1000.f); - capTot = (fabs(rs_capTotCt) - rs_fanHeatC) / 1000.f; - capSen = (fabs(rs_capSenCt) - rs_fanHeatC) / 1000.f; + capTot = (fabs( rs_capTotCt)-rs_fanHeatC)/1000.f; + capSen = (fabs( rs_capSenCt)-rs_fanHeatC)/1000.f; #if 0 x // check: derived total capacity from air stream enthalpy change x float capTotX = rs_amf * (rs_asOut.as_Enthalpy() - rs_asIn.as_Enthalpy()); #endif -} // RSYS::rs_GetPerfClg +} // RSYS::rs_GetPerfClg //------------------------------------------------------------------------------- -RC RSYS::rs_PerfMapAC() // generate AC cooling performance map +RC RSYS::rs_PerfMapAC() // generate AC cooling performance map // testing aid { #if 1 - // Goodman - static float fAF[] = {.550f, .700f, .875f, 1.f, 1.125f, 0.f}; - static float tdbI[] = {70.f, 75.f, 80.f, 85.f, 0.f}; - static float twbI[] = {59.f, 63.f, 67.f, 71.f}; - static float tdbO[] = {65.f, 75.f, 85.f, 95.f, 105.f, 115.f, 0.f}; +// Goodman +static float fAF[] = { .550f, .700f, .875f, 1.f, 1.125f, 0.f }; +static float tdbI[] = { 70.f, 75.f, 80.f, 85.f, 0.f }; +static float twbI[] = { 59.f, 63.f, 67.f, 71.f }; +static float tdbO[] = { 65.f, 75.f, 85.f, 95.f, 105.f, 115.f, 0.f }; #else - // Carrier - static float fAF[] = {.875f, 1.f, 1.125f, 0.f}; - static float tdbI[] = {70.f, 75.f, 80.f, 0.f}; - static float twbI[] = {57.f, 62.f, 67.f, 72.f}; - static float tdbO[] = {75.f, 85.f, 95.f, 105.f, 115.f, 125.f, 0.f}; -#endif - - // write to file PM_.csv - const char *nameX = name; - if (IsBlank(nameX)) - nameX = "RSYS"; - FILE *f = fopen(strtprintf("PM_%s.csv", nameX), "wt"); - if (!f) - return RCBAD; // can't open file - - RSYS rsSave(*this); // save for restore at exit - // (we alter mbrs here) - - fprintf(f, "%s\n", rs_desc.CStrIfNotBlank(nameX)); - - fprintf( - f, - "cfm,IDB (F),IWB (F),ODB (F),CapTot (kBtuh),CapSen (kBtuh),Pwr (kW)\n"); - - float capNomTonsX = rs_ClgCapNomTons(); // exact - float capNomTons = rs_ClgCapNomTons(.5f); // nearest .5 ton - float vfPerTonNom = 400.f * capNomTons / capNomTonsX; - if (Top.tp_airSH <= 0.f) - Top.tp_Psychro(); // set air constants - - for (int iAF = 0; fAF[iAF] > 0.f; iAF++) { - rs_vfPerTon = vfPerTonNom * fAF[iAF]; - float cfm = rs_vfPerTon * capNomTonsX; - rs_SetRunConstants(); - rs_SetupSizes(); - for (int idbI = 0; tdbI[idbI] > 0.f; idbI++) { - for (int iwbI = 0; twbI[iwbI] > 0.f; iwbI++) { - if (twbI[iwbI] >= tdbI[idbI]) - continue; - // entering conditions - rs_twbIn = twbI[iwbI]; - float w = psyHumRat1(tdbI[idbI], rs_twbIn); - rs_asIn.as_Set(tdbI[idbI], w); - for (int idbO = 0; tdbO[idbO] > 0.f; idbO++) { // outdoor conditions - rs_asOut = rs_asIn; - rs_tdbOut = tdbO[idbO]; - rs_CoolingOutletAirState(); - float capTot, capSen, pwrIn; - rs_GetPerfClg(capTot, capSen, pwrIn); - fprintf(f, "%.f,%.f,%.f,%.f,%.2f,%.2f,%.2f\n", cfm, tdbI[idbI], - rs_twbIn, rs_tdbOut, capTot, capSen, pwrIn); - } - } - } - } - - fclose(f); - - *this = rsSave; // restore entry state - - return RCOK; -} // RSYS::rs_PerfMapAC +// Carrier +static float fAF[] = { .875f, 1.f, 1.125f, 0.f }; +static float tdbI[] = { 70.f, 75.f, 80.f, 0.f }; +static float twbI[] = { 57.f, 62.f, 67.f, 72.f }; +static float tdbO[] = { 75.f, 85.f, 95.f, 105.f, 115.f, 125.f, 0.f }; +#endif + + // write to file PM_.csv + const char* nameX = name; + if (IsBlank( nameX)) + nameX = "RSYS"; + FILE* f = fopen( strtprintf( "PM_%s.csv", nameX), "wt"); + if (!f) + return RCBAD; // can't open file + + RSYS rsSave( *this); // save for restore at exit + // (we alter mbrs here) + + fprintf(f, "%s\n", rs_desc.CStrIfNotBlank(nameX)); + + fprintf( f, "cfm,IDB (F),IWB (F),ODB (F),CapTot (kBtuh),CapSen (kBtuh),Pwr (kW)\n"); + + float capNomTonsX = rs_ClgCapNomTons(); // exact + float capNomTons = rs_ClgCapNomTons( .5f); // nearest .5 ton + float vfPerTonNom = 400.f * capNomTons / capNomTonsX; + if (Top.tp_airSH <= 0.f) + Top.tp_Psychro(); // set air constants + + for (int iAF=0;fAF[ iAF]>0.f; iAF++) + { rs_vfPerTon = vfPerTonNom * fAF[ iAF]; + float cfm = rs_vfPerTon * capNomTonsX; + rs_SetRunConstants(); + rs_SetupSizes(); + for (int idbI=0; tdbI[ idbI] > 0.f; idbI++ ) + { for (int iwbI=0; twbI[ iwbI] > 0.f; iwbI++) + { if (twbI[ iwbI] >= tdbI[ idbI]) + continue; + // entering conditions + rs_twbIn = twbI[ iwbI]; + float w = psyHumRat1( tdbI[ idbI], rs_twbIn); + rs_asIn.as_Set( tdbI[ idbI], w); + for (int idbO=0; tdbO[ idbO] > 0.f; idbO++) + { // outdoor conditions + rs_asOut = rs_asIn; + rs_tdbOut = tdbO[ idbO]; + rs_CoolingOutletAirState(); + float capTot, capSen, pwrIn; + rs_GetPerfClg( capTot, capSen, pwrIn); + fprintf( f, "%.f,%.f,%.f,%.f,%.2f,%.2f,%.2f\n", + cfm, tdbI[ idbI], rs_twbIn, rs_tdbOut, + capTot, capSen, pwrIn); + } + } + } + } + + fclose( f); + + *this = rsSave; // restore entry state + + return RCOK; +} // RSYS::rs_PerfMapAC //------------------------------------------------------------------------------- -RC RSYS::rs_ExportCorrelationValues() // write CSV file containing values from - // RSYS correlations +RC RSYS::rs_ExportCorrelationValues() // write CSV file containing values from RSYS correlations // testing aid { - static float fAF[] = {.550f, .700f, .875f, 1.f, 1.125f, 0.f}; - static float tdbI[] = {70.f, 72.f, 74.f, 76.f, 78.f, 80.f, 85.f, 0.f}; - static float twbI[] = {54.f, 56.f, 58.f, 60.f, 62.f, 64.f, 67.f, 71.f, 0.f}; - static float tdbO[] = {65.f, 75.f, 82.f, 85.f, 95.f, 105.f, 115.f, 0.f}; - - // write to file PM_.csv - const char *nameX = name; - if (IsBlank(nameX)) - nameX = "RSYS"; - FILE *f = fopen(strtprintf("CORVALS_%s.csv", nameX), "wt"); - if (!f) - return RCBAD; // can't open file - - RSYS rsSave(*this); // save for restore at exit - // (we alter mbrs here) - - fprintf(f, "%s\n", rs_desc.CStrIfNotBlank(nameX)); - - fprintf(f, "cfm/ton,ODB (F),EDB (F),EWB (F),RH,SHR,fCap,fEER,fInp\n"); - - float vfPerTonNom = 400.f; - - if (Top.tp_airSH <= 0.f) - Top.tp_Psychro(); // set air constants - - for (int iAF = 0; fAF[iAF] > 0.f; iAF++) { - rs_vfPerTon = vfPerTonNom * fAF[iAF]; - char sVf[20]; - sprintf(sVf, "%.f", rs_vfPerTon); - for (int idbO = 0; tdbO[idbO] > 0.f; idbO++) { // outdoor conditions - rs_tdbOut = tdbO[idbO]; - char sDbO[20]; - sprintf(sDbO, "%.f", rs_tdbOut); - - for (int idbI = 0; tdbI[idbI] > 0.f; idbI++) { // entering dry bulb - rs_tdbCoilIn = tdbI[idbI]; - char sDbI[20]; - sprintf(sDbI, "%.f", rs_tdbCoilIn); - for (int iwbI = 0; twbI[iwbI] > 0.f; - iwbI++) { // entering wet bulb conditions - rs_twbCoilIn = twbI[iwbI]; - if (rs_twbCoilIn > rs_tdbCoilIn) - continue; - float rh = psyRelHum(rs_tdbCoilIn, rs_twbCoilIn); - rs_CoolingSHR(); - rs_CoolingCapF1Spd(); - float fCondInp = rs_CoolingEff1Spd(); - fprintf(f, "%s,%s,%s,%.f,%.3f,%.3f,%.3f,%.3f,%.3f\n", sVf, sDbO, sDbI, - rs_twbCoilIn, rh, rs_SHR, rs_fCondCap, rs_fCondEER, fCondInp); - } - } - } - } - - fclose(f); - - *this = rsSave; // restore entry state - - return RCOK; -} // RSYS::rs_ExportCorrelationValues + static float fAF[] = { .550f, .700f, .875f, 1.f, 1.125f, 0.f }; + static float tdbI[] = { 70.f, 72.f, 74.f, 76.f, 78.f, 80.f, 85.f, 0.f }; + static float twbI[] = { 54.f, 56.f, 58.f, 60.f, 62.f, 64.f, 67.f, 71.f, 0.f }; + static float tdbO[] = { 65.f, 75.f, 82.f, 85.f, 95.f, 105.f, 115.f, 0.f }; + + // write to file PM_.csv + const char* nameX = name; + if (IsBlank(nameX)) + nameX = "RSYS"; + FILE* f = fopen(strtprintf("CORVALS_%s.csv", nameX), "wt"); + if (!f) + return RCBAD; // can't open file + + RSYS rsSave(*this); // save for restore at exit + // (we alter mbrs here) + + fprintf(f, "%s\n", rs_desc.CStrIfNotBlank(nameX)); + + fprintf(f, "cfm/ton,ODB (F),EDB (F),EWB (F),RH,SHR,fCap,fEER,fInp\n"); + + float vfPerTonNom = 400.f; + + if (Top.tp_airSH <= 0.f) + Top.tp_Psychro(); // set air constants + + for (int iAF = 0; fAF[iAF] > 0.f; iAF++) + { + rs_vfPerTon = vfPerTonNom * fAF[iAF]; + char sVf[20]; + sprintf(sVf, "%.f", rs_vfPerTon); + for (int idbO = 0; tdbO[idbO] > 0.f; idbO++) + { // outdoor conditions + rs_tdbOut = tdbO[idbO]; + char sDbO[20]; + sprintf(sDbO, "%.f", rs_tdbOut); + + for (int idbI = 0; tdbI[idbI] > 0.f; idbI++) + { // entering dry bulb + rs_tdbCoilIn = tdbI[idbI]; + char sDbI[20]; + sprintf(sDbI, "%.f", rs_tdbCoilIn); + for (int iwbI = 0; twbI[iwbI] > 0.f; iwbI++) + { // entering wet bulb conditions + rs_twbCoilIn = twbI[iwbI]; + if (rs_twbCoilIn > rs_tdbCoilIn) + continue; + float rh = psyRelHum(rs_tdbCoilIn, rs_twbCoilIn); + rs_CoolingSHR(); + rs_CoolingCapF1Spd(); + float fCondInp = rs_CoolingEff1Spd(); + fprintf(f, "%s,%s,%s,%.f,%.3f,%.3f,%.3f,%.3f,%.3f\n", + sVf, sDbO, sDbI, rs_twbCoilIn, rh, rs_SHR, rs_fCondCap, rs_fCondEER, fCondInp); + } + } + } + } + + fclose(f); + + *this = rsSave; // restore entry state + + return RCOK; +} // RSYS::rs_ExportCorrelationValues //----------------------------------------------------------------------------- -RC RSYS::rs_PerfDataASHP() { - // write to file PD_.csv - const char *nameX = name; - int iHSPF = int(0.5f + 10.f * rs_HSPF); - if (IsBlank(nameX)) - nameX = "RSYS"; - FILE *f = fopen(strtprintf("PD_%s%d.csv", nameX, iHSPF), "wt"); - if (!f) - return RCBAD; // can't open file - - RSYS rsSave(*this); // save for restore at exit - // (we alter mbrs here) - - fprintf(f, "%s,All values include rating fan power,,%s,CSE %s\n", - rs_desc.CStrIfNotBlank(nameX), Top.runDateTime.CStr(), - Top.tp_progVersion.CStr()); - - fprintf(f, - "HSPF=%0.2f, HSPF CSE=%0.3f,,HSPF MP=%0.3f,,HSPF ESL=%0.3f,,HSPF " - "E+=%0.3f\n", - rs_HSPF, rs_HSPFCheckASHP(0), rs_HSPFCheckASHP(1), - rs_HSPFCheckASHP(2), rs_HSPFCheckASHP(3)); - - fprintf(f, "tODB,cap CSE,inp CSE,COP CSE,cap MP,inp MP,COP MP,cap ESL,inp " - "ESL,COP ESL,cap E+,inp E+,COP E+\n"); - - for (int iR = 0; iR < 3; iR++) { - float tRat[] = {17, 35, 47}; - rs_PerfDataASHP1(f, tRat[iR]); - } - - fprintf(f, "\n"); - - for (int iDB = -10; iDB < 63; iDB++) - rs_PerfDataASHP1(f, float(iDB)); - - fprintf(f, "\ncap17/cap47 vs HSPF\n"); - - for (iHSPF = 65; iHSPF < 140; iHSPF++) { - rs_HSPF = float(iHSPF) / 10.f; - fprintf(f, "%0.3f,%0.3f\n", rs_HSPF, rs_CapRat1747()); - } - - // loop over range of HSPFs and cap47s, derive back-calc HSPF - fprintf(f, "\nBack-Calculated HSPF\n"); - const float cds[] = {0.f, .125f, .25f, -1.f}; - const float hspfs[] = {6.5f, 7.0f, 7.5f, 8.0f, 8.5f, 9.0f, 9.5f, 10.0f, - 10.5f, 11.0f, 11.5f, 12.f, 12.5f, 13.f, -1.}; - const float cap47s[] = {18000.f, 24000.f, 54999.f, 55000.f, -1.f}; - - ClrSet(RSYS_COP47); - ClrSet(RSYS_CAP35); - ClrSet(RSYS_COP35); - ClrSet(RSYS_CAP17); - ClrSet(RSYS_COP17); - - for (int iH = 0; hspfs[iH] > 0.f; iH++) { - rs_HSPF = hspfs[iH]; - for (int iC = 0; cap47s[iC] > 0.f; iC++) { - rs_cap47 = cap47s[iC]; - for (int iD = 0; cds[iD] >= 0.f; iD++) { - rs_CdH = cds[iD]; - rs_SetupASHP(); - if (iD == 0) - fprintf(f, "%0.3f,%0.f", rs_HSPF, rs_cap47); - float bcHSPF = rs_HSPFCheckASHP(0); - fprintf(f, ",%0.3f", bcHSPF); - } - fprintf(f, "\n"); - } - } - - fclose(f); - *this = rsSave; // restore entry state - - return RCOK; -} // RSYS::rs_PerfDataASHP +RC RSYS::rs_PerfDataASHP() +{ + // write to file PD_.csv + const char* nameX = name; + int iHSPF = int( 0.5f + 10.f*rs_HSPF); + if (IsBlank( nameX)) + nameX = "RSYS"; + FILE* f = fopen( strtprintf( "PD_%s%d.csv", nameX, iHSPF), "wt"); + if (!f) + return RCBAD; // can't open file + + RSYS rsSave( *this); // save for restore at exit + // (we alter mbrs here) + + fprintf( f, "%s,All values include rating fan power,,%s,CSE %s\n", + rs_desc.CStrIfNotBlank( nameX), + Top.runDateTime.CStr(), + Top.tp_progVersion.CStr()); + + fprintf( f, "HSPF=%0.2f, HSPF CSE=%0.3f,,HSPF MP=%0.3f,,HSPF ESL=%0.3f,,HSPF E+=%0.3f\n", + rs_HSPF, + rs_HSPFCheckASHP( 0), rs_HSPFCheckASHP( 1), rs_HSPFCheckASHP( 2), rs_HSPFCheckASHP( 3)); + + fprintf( f, "tODB,cap CSE,inp CSE,COP CSE,cap MP,inp MP,COP MP,cap ESL,inp ESL,COP ESL,cap E+,inp E+,COP E+\n"); + + for (int iR=0; iR<3; iR++) + { float tRat[] = { 17, 35, 47 }; + rs_PerfDataASHP1( f, tRat[ iR]); + } + + fprintf( f, "\n"); + + for (int iDB = -10; iDB<63; iDB++) + rs_PerfDataASHP1( f, float( iDB)); + + fprintf( f, "\ncap17/cap47 vs HSPF\n"); + + for (iHSPF=65; iHSPF<140; iHSPF++) + { rs_HSPF = float( iHSPF)/10.f; + fprintf( f, "%0.3f,%0.3f\n", rs_HSPF, rs_CapRat1747()); + } + + // loop over range of HSPFs and cap47s, derive back-calc HSPF + fprintf( f, "\nBack-Calculated HSPF\n"); + const float cds[] = { 0.f, .125f, .25f, -1.f }; + const float hspfs[] = { 6.5f, 7.0f, 7.5f, 8.0f, 8.5f, 9.0f, 9.5f, 10.0f, 10.5f, 11.0f, 11.5f, 12.f, 12.5f, 13.f, -1. }; + const float cap47s[] = { 18000.f, 24000.f, 54999.f, 55000.f, -1.f }; + + ClrSet( RSYS_COP47); + ClrSet( RSYS_CAP35); + ClrSet( RSYS_COP35); + ClrSet( RSYS_CAP17); + ClrSet( RSYS_COP17); + + for (int iH=0; hspfs[ iH] > 0.f; iH++) + { rs_HSPF = hspfs[ iH]; + for (int iC=0; cap47s[ iC] > 0.f; iC++) + { rs_cap47 = cap47s[ iC]; + for (int iD=0; cds[ iD] >= 0.f; iD++) + { rs_CdH = cds[ iD]; + rs_SetupASHP(); + if (iD==0) + fprintf( f, "%0.3f,%0.f", rs_HSPF, rs_cap47); + float bcHSPF = rs_HSPFCheckASHP( 0); + fprintf( f, ",%0.3f", bcHSPF); + } + fprintf( f, "\n"); + } + } + + fclose( f); + *this = rsSave; // restore entry state + + return RCOK; +} // RSYS::rs_PerfDataASHP //----------------------------------------------------------------------------- -void RSYS::rs_PerfDataASHP1( // test aid: ASHP performance from alt models - FILE *f, // where to write - float tdbOut) // outdoor temp, F +void RSYS::rs_PerfDataASHP1( // test aid: ASHP performance from alt models + FILE* f, // where to write + float tdbOut) // outdoor temp, F // all values include rating fan power { - // CSE "original" - float COP; - float cap = rs_PerfASHP(0 + 0x100, tdbOut, COP, 0.f); - float inp = cap / COP; - - // Micropas - float COPMP; - float capMP = rs_PerfASHP(1 + 0x100, tdbOut, COPMP, 0.f); - float inpMP = capMP / COPMP; - - // ESL - float COPESL; - float capESL = rs_PerfASHP(2 + 0x100, tdbOut, COPESL, rs_fanHRtdH); - float inpESL = capESL / COPESL; - - // E+ - float COPEP; - float capEP = rs_PerfASHP(3 + 0x100, tdbOut, COPEP, rs_fanHRtdH); - float inpEP = capEP / COPEP; - - fprintf( - f, - "%.f, %.f, %.f, %.3f, %.f, %.f, %.3f, %.f, %.f, %.3f, %.f, %.f, %.3f\n", - tdbOut, cap, inp, COP, capMP, inpMP, COPMP, capESL, inpESL, COPESL, capEP, - inpEP, COPEP); - -} // RSYS::rs_PerfDataASHP1 + // CSE "original" + float COP; + float cap = rs_PerfASHP( 0+0x100, tdbOut, COP, 0.f); + float inp = cap / COP; + + // Micropas + float COPMP; + float capMP = rs_PerfASHP( 1+0x100, tdbOut, COPMP, 0.f); + float inpMP = capMP / COPMP; + + // ESL + float COPESL; + float capESL = rs_PerfASHP( 2+0x100, tdbOut, COPESL, rs_fanHRtdH); + float inpESL = capESL / COPESL; + + // E+ + float COPEP; + float capEP = rs_PerfASHP( 3+0x100, tdbOut, COPEP, rs_fanHRtdH); + float inpEP = capEP / COPEP; + + fprintf( f, "%.f, %.f, %.f, %.3f, %.f, %.f, %.3f, %.f, %.f, %.3f, %.f, %.f, %.3f\n", + tdbOut, cap, inp, COP, + capMP, inpMP, COPMP, + capESL, inpESL, COPESL, + capEP, inpEP, COPEP); + +} // RSYS::rs_PerfDataASHP1 //----------------------------------------------------------------------------- -float RSYS::rs_PerfASHP( // ASHP performance (simplified call) - int ashpModel, // 0=CSE, 1=MP, 2=ESL, 3=Cutler - // + 0x100: do NOT model defrost heating - // else model defrost if available - float tdbOut, // outdoor dry bulb, F - float &COP, // returned: compressor-only full speed COP at tdbOut - float fanHAdj /*=0.f*/) // fan power adjustment, Btuh - // removed from capacity and input (before COP calc) +float RSYS::rs_PerfASHP( // ASHP performance (simplified call) + int ashpModel, // 0=CSE, 1=MP, 2=ESL, 3=Cutler + // + 0x100: do NOT model defrost heating + // else model defrost if available + float tdbOut, // outdoor dry bulb, F + float& COP, // returned: compressor-only full speed COP at tdbOut + float fanHAdj /*=0.f*/) // fan power adjustment, Btuh + // removed from capacity and input (before COP calc) // returns total heating capacity (compressor + capDefrostHt), Btuh { - float capHt, inpHt, capDfHt, capHtMin, inpHtMin, capDfHtMin; - COP = rs_PerfASHP2(ashpModel, tdbOut, fanHAdj, capHt, inpHt, capDfHt, - capHtMin, inpHtMin, capDfHtMin); + float capHt, inpHt, capDfHt, capHtMin, inpHtMin, capDfHtMin; + COP = rs_PerfASHP2(ashpModel, tdbOut, fanHAdj, + capHt, inpHt, capDfHt, capHtMin, inpHtMin, capDfHtMin); - return capHt; + return capHt; -} // RSYS::rs_PerfASHP +} // RSYS::rs_PerfASHP //------------------------------------------------------------------------------ -float RSYS::rs_PerfASHP2( // ASHP performance - int ashpModel, // 0=CSE, 1=MP, 2=ESL, 3=Cutler - // + 0x100: do NOT model defrost heating - // else model defrost if available - // Note: variable speed supported for CSE only - float tdbOut, // outdoor dry bulb, F - float fanHAdj, // fan power adjustment, Btuh - // removed from capacity and input (before COP calc) - float &capHt, // returned: compressor-only full speed heating capacity - float &inpHt, // returned: compressor-only full speed input power, Btuh - float &capDfHt, // returned: defrost full speed aux heat output, Btuh - // = strip/furnace output to make up compressor cooling - // (depends on rs_defrostModel) - float - &capHtMin, // returned: compressor-only min speed heating capacity, Btuh - // (= capHt if single speed) - float &inpHtMin, // returned: compressor-only min speed input power, Btuh - // (= inpHt if single speed) - float &capDfHtMin, // returned: defrost min speed aux heat output, Btuh - // (= capDfHt if single speed) - float COPAdjF /*=1.f*/) // COP adjustment factor - // multiplies final COP result +float RSYS::rs_PerfASHP2( // ASHP performance + int ashpModel, // 0=CSE, 1=MP, 2=ESL, 3=Cutler + // + 0x100: do NOT model defrost heating + // else model defrost if available + // Note: variable speed supported for CSE only + float tdbOut, // outdoor dry bulb, F + float fanHAdj, // fan power adjustment, Btuh + // removed from capacity and input (before COP calc) + float& capHt, // returned: compressor-only full speed heating capacity + float& inpHt, // returned: compressor-only full speed input power, Btuh + float& capDfHt, // returned: defrost full speed aux heat output, Btuh + // = strip/furnace output to make up compressor cooling + // (depends on rs_defrostModel) + float& capHtMin, // returned: compressor-only min speed heating capacity, Btuh + // (= capHt if single speed) + float& inpHtMin, // returned: compressor-only min speed input power, Btuh + // (= inpHt if single speed) + float& capDfHtMin, // returned: defrost min speed aux heat output, Btuh + // (= capDfHt if single speed) + float COPAdjF /*=1.f*/) // COP adjustment factor + // multiplies final COP result // returns compressor-only full-speed COP (as adjusted by COPAdjF) { - capDfHt = capDfHtMin = 0.f; - BOOL bDoDefrost = (ashpModel & 0x100) == 0 && - rs_defrostModel == C_RSYSDEFROSTMODELCH_REVCYCLEAUX; - ashpModel &= 0xff; - - if (ashpModel == 0) { - int iDefrost = rs_HasDefrost() && tdbOut > 17.f && - tdbOut < 45.f; // 1 if defrost, else 0 - if (rs_IsASHPVC()) { // retrieve min/max cap and input at tdbOut - // rs_pRgiHtg[ 0] includes 5, 17, 35, and 47 F points - // values include fan heat/power - /* rc = */ rs_GetPerfBtwxt(rs_pRgiHtg[0], tdbOut, capHt, inpHt, capHtMin, - inpHtMin); -#if defined(_DEBUG) - if (capHt < capHtMin) - printf("\ncapHt < capHtMin"); -#endif - rs_speedFMin = capHt <= 0.f ? 1.f : capHtMin / capHt; - if (iDefrost && bDoDefrost) { // C_RSYSDEFROSTMODELCH_REVCYCLEAUX - // include sufficient aux heat to make up for ASHP cooling - // = steady-state cap - integrated cap - // limit to available aux heat capacity - // Note: does NOT over-commit aux because compressor - // and aux do not run simultaneously. - float capHtSS, capHtSSMin, inpSink; - /* rc = */ rs_GetPerfBtwxt(rs_pRgiHtg[1], tdbOut, capHtSS, inpSink, - capHtSSMin, inpSink); - capDfHt = bracket(0.f, capHtSS - capHt, rs_capAuxH); - capDfHtMin = bracket(0.f, capHtSSMin - capHtMin, rs_capAuxH); - } - capHt -= fanHAdj; - inpHt -= fanHAdj; - capHtMin -= fanHAdj * rs_speedFMin; - inpHtMin -= fanHAdj * rs_speedFMin; - } else { - // CSE AHRI 210/240 model - // input power and heating capacity at current conditions (Btuh) - float tdbM17 = tdbOut - 17.f; - inpHt = rs_inp17 + rs_ASHPInpF[iDefrost] * tdbM17 - fanHAdj; - capHt = rs_cap17 + rs_ASHPCapF[iDefrost] * tdbM17 - fanHAdj; - if (iDefrost && bDoDefrost) { // C_RSYSDEFROSTMODELCH_REVCYCLEAUX - // include sufficient aux heat to make up for ASHP cooling - // limit to available aux heat capacity - // Note: does NOT over-commit aux because compressor - // and aux do not run simultaneously. - capDfHt = (rs_ASHPCapF[0] - rs_ASHPCapF[1]) * tdbM17; - if (capDfHt > rs_capAuxH) - capDfHt = rs_capAuxH; - } - } - } else { // DOE-2 curve fit methods - float tdbOut2 = tdbOut * tdbOut; - float tdbOut3 = tdbOut2 * tdbOut; - - float capNet; // capacity with fan - double fCap, fEIR; - if (ashpModel == 1) { // Micropas curves from Ken Nittler 5-2013) - // Believed to be from DOE-2.1E? - // DOE-2 curves should be applied to compressor-only - // but it appears the Micropas implementation applied to values with fan - - fCap = - 0.302 + 0.0121 * tdbOut + 0.0000759 * tdbOut2 - 0.000000384 * tdbOut3; - capNet = fCap * rs_cap47; // capacity with fan - capHt = capNet - fanHAdj; // capacity w/o fan - - float EIR47 = 1. / (rs_HSPF * 0.4f); // EIR at 47F w/fan power - fEIR = 1.9 - 0.025 * tdbOut + 0.000134 * tdbOut2 - 0.000000213 * tdbOut3; - inpHt = capNet * fEIR * EIR47 - fanHAdj; // input w/o fan - } else if (ashpModel == 2) { // ESL curves from Juan-Carlos Baltazar (ESL, - // Texas A&M), 5-2013 - // From DOE-2.1E? - - // capacity (curve SDL-C51) - fCap = 0.34148808 + 0.00894102 * tdbOut + 0.00010787 * tdbOut2; - capHt = fCap * rs_cap47; - - // EIR (curve SDL-C56) - fEIR = 2.03914613 - 0.03906753 * tdbOut + 0.00045617 * tdbOut2 - - 0.00000203 * tdbOut3; - - // fan / no-fan issue - - // ESL method used in their RESNET runs - // approximates HSPF w/o fans - // however, they discovered after the fact that RESNET procedure is based - // on - // rated HSPF (with fans) - float HSPFX = 1. / ((1. / rs_HSPF) - .01095); - float EIR47X = - .582 * (1. / (HSPFX / 3.413)); // compressor-only EIR @ 47 F - - // capNet? - inpHt = capHt * fEIR * EIR47X + fanHAdj; // input w/o fan - } else if (ashpModel == - 3) { // EnergyPlus model with Cutler/NREL coefficients - - // Data from tables 12 and 13 of NREL/TP-5500-56354 (Cutler et al.) - // January 2013 - // organization: epXXX[ iHS][ iCoeff] - // iHS: 0=cooling, 1 spd; 1=cooling, 2 spd lo; 2= cooling, 2 spd hi - // 3=heating, 1 spd; 4=heating, 2 spd lo; 5= heating, 2 spd hi - // iCoeff: 0=a, 1=b, 2=c, 3=d, 4=e, 5=f - // capacity coefficents - static const double epCap[6][6] = { - {3.68637657, -0.098352478, 0.000956357, 0.005838141, -0.0000127, - -0.000131702}, - {3.998418659, -0.108728222, 0.001056818, 0.007512314, -0.0000139, - -0.000164716}, - {3.466810106, -0.091476056, 0.000901205, 0.004163355, -0.00000919, - -0.000110829}, - {0.566333415, -0.000744164, -0.0000103, 0.009414634, 0.0000506, - -0.00000675}, - {0.335690634, 0.002405123, -0.0000464, 0.013498735, 0.0000499, - -0.00000725}, - {0.306358843, 0.005376987, -0.0000579, 0.011645092, 0.0000591, - -0.0000203}}; - // EIR coefficients - static const double epEIR[6][6] = { - {-3.437356399, 0.136656369, -0.001049231, -0.0079378, 0.000185435, - -0.0001441}, - {-4.282911381, 0.181023691, -0.001357391, -0.026310378, 0.000333282, - -0.000197405}, - {-3.557757517, 0.112737397, -0.000731381, 0.013184877, 0.000132645, - -0.000338716}, - {0.718398423, 0.003498178, 0.000142202, -0.005724331, 0.00014085, - -0.000215321}, - {0.36338171, 0.013523725, 0.000258872, -0.009450269, 0.000439519, - -0.000653723}, - {0.981100941, -0.005158493, 0.000243416, -0.005274352, 0.000230742, - -0.000336954}}; - - tdbOut2 = tdbOut * tdbOut; - float tdbEnt = 70.f; - float tdbEnt2 = tdbEnt * tdbEnt; - float tdbX = tdbOut * tdbEnt; - const int iHS = 3; // heating, 1 spd - - fCap = epCap[iHS][0] + epCap[iHS][1] * tdbEnt + epCap[iHS][2] * tdbEnt2 + - epCap[iHS][3] * tdbOut + epCap[iHS][4] * tdbOut2 + - epCap[iHS][5] * tdbX; - capHt = fCap * rs_cap47; - - fEIR = epEIR[iHS][0] + epEIR[iHS][1] * tdbEnt + epEIR[iHS][2] * tdbEnt2 + - epEIR[iHS][3] * tdbOut + epEIR[iHS][4] * tdbOut2 + - epEIR[iHS][5] * tdbX; - inpHt = capHt * fEIR / rs_COP47; - } -#if defined(_DEBUG) - else - ASSERT(1); // missing case + capDfHt = capDfHtMin = 0.f; + BOOL bDoDefrost = (ashpModel & 0x100) == 0 + && rs_defrostModel == C_RSYSDEFROSTMODELCH_REVCYCLEAUX; + ashpModel &= 0xff; + + if (ashpModel == 0) + { + int iDefrost = rs_HasDefrost() && tdbOut > 17.f && tdbOut < 45.f; // 1 if defrost, else 0 + if (rs_IsASHPVC()) + { // retrieve min/max cap and input at tdbOut + // rs_pRgiHtg[ 0] includes 5, 17, 35, and 47 F points + // values include fan heat/power + /* rc = */ rs_GetPerfBtwxt(rs_pRgiHtg[ 0], tdbOut, capHt, inpHt, capHtMin, inpHtMin); +#if defined( _DEBUG) + if (capHt < capHtMin) + printf("\ncapHt < capHtMin"); +#endif + rs_speedFMin = capHt <= 0.f ? 1.f : capHtMin / capHt; + if (iDefrost && bDoDefrost) + { // C_RSYSDEFROSTMODELCH_REVCYCLEAUX + // include sufficient aux heat to make up for ASHP cooling + // = steady-state cap - integrated cap + // limit to available aux heat capacity + // Note: does NOT over-commit aux because compressor + // and aux do not run simultaneously. + float capHtSS, capHtSSMin, inpSink; + /* rc = */ rs_GetPerfBtwxt(rs_pRgiHtg[1], tdbOut, capHtSS, inpSink, capHtSSMin, inpSink); + capDfHt = bracket(0.f, capHtSS - capHt, rs_capAuxH); + capDfHtMin = bracket(0.f, capHtSSMin - capHtMin, rs_capAuxH); + } + capHt -= fanHAdj; + inpHt -= fanHAdj; + capHtMin -= fanHAdj * rs_speedFMin; + inpHtMin -= fanHAdj * rs_speedFMin; + } + else + { + // CSE AHRI 210/240 model + // input power and heating capacity at current conditions (Btuh) + float tdbM17 = tdbOut - 17.f; + inpHt = rs_inp17 + rs_ASHPInpF[iDefrost] * tdbM17 - fanHAdj; + capHt = rs_cap17 + rs_ASHPCapF[iDefrost] * tdbM17 - fanHAdj; + if (iDefrost && bDoDefrost) + { // C_RSYSDEFROSTMODELCH_REVCYCLEAUX + // include sufficient aux heat to make up for ASHP cooling + // limit to available aux heat capacity + // Note: does NOT over-commit aux because compressor + // and aux do not run simultaneously. + capDfHt = (rs_ASHPCapF[0] - rs_ASHPCapF[1]) * tdbM17; + if (capDfHt > rs_capAuxH) + capDfHt = rs_capAuxH; + } + } + } + else + { // DOE-2 curve fit methods + float tdbOut2 = tdbOut * tdbOut; + float tdbOut3 = tdbOut2 * tdbOut; + + float capNet; // capacity with fan + double fCap, fEIR; + if (ashpModel == 1) + { // Micropas curves from Ken Nittler 5-2013) + // Believed to be from DOE-2.1E? + // DOE-2 curves should be applied to compressor-only + // but it appears the Micropas implementation applied to values with fan + + fCap = 0.302 + 0.0121*tdbOut + 0.0000759*tdbOut2 - 0.000000384*tdbOut3; + capNet = fCap * rs_cap47; // capacity with fan + capHt = capNet - fanHAdj; // capacity w/o fan + + float EIR47 = 1./(rs_HSPF*0.4f); // EIR at 47F w/fan power + fEIR = 1.9 - 0.025*tdbOut + 0.000134*tdbOut2 - 0.000000213*tdbOut3; + inpHt = capNet * fEIR * EIR47 - fanHAdj; // input w/o fan + } + else if (ashpModel == 2) + { // ESL curves from Juan-Carlos Baltazar (ESL, Texas A&M), 5-2013 + // From DOE-2.1E? + + // capacity (curve SDL-C51) + fCap = 0.34148808 + 0.00894102*tdbOut + 0.00010787 * tdbOut2; + capHt = fCap * rs_cap47; + + // EIR (curve SDL-C56) + fEIR = 2.03914613 - 0.03906753*tdbOut + 0.00045617*tdbOut2 - 0.00000203*tdbOut3; + + // fan / no-fan issue + + // ESL method used in their RESNET runs + // approximates HSPF w/o fans + // however, they discovered after the fact that RESNET procedure is based on + // rated HSPF (with fans) + float HSPFX = 1./((1./rs_HSPF) - .01095); + float EIR47X = .582 * (1./(HSPFX/3.413)); // compressor-only EIR @ 47 F + + // capNet? + inpHt = capHt * fEIR * EIR47X + fanHAdj; // input w/o fan + } + else if (ashpModel == 3) + { // EnergyPlus model with Cutler/NREL coefficients + +// Data from tables 12 and 13 of NREL/TP-5500-56354 (Cutler et al.) January 2013 +// organization: epXXX[ iHS][ iCoeff] +// iHS: 0=cooling, 1 spd; 1=cooling, 2 spd lo; 2= cooling, 2 spd hi +// 3=heating, 1 spd; 4=heating, 2 spd lo; 5= heating, 2 spd hi +// iCoeff: 0=a, 1=b, 2=c, 3=d, 4=e, 5=f +// capacity coefficents +static const double epCap[ 6][ 6] = { +{ 3.68637657, -0.098352478, 0.000956357, 0.005838141, -0.0000127, -0.000131702 }, +{ 3.998418659, -0.108728222, 0.001056818, 0.007512314, -0.0000139, -0.000164716 }, +{ 3.466810106, -0.091476056, 0.000901205, 0.004163355, -0.00000919, -0.000110829 }, +{ 0.566333415, -0.000744164, -0.0000103, 0.009414634, 0.0000506, -0.00000675 }, +{ 0.335690634, 0.002405123, -0.0000464, 0.013498735, 0.0000499, -0.00000725 }, +{ 0.306358843, 0.005376987, -0.0000579, 0.011645092, 0.0000591, -0.0000203 }}; +// EIR coefficients +static const double epEIR[ 6][ 6] = { +{ -3.437356399, 0.136656369, -0.001049231, -0.0079378, 0.000185435, -0.0001441 }, +{ -4.282911381, 0.181023691, -0.001357391, -0.026310378, 0.000333282, -0.000197405 }, +{ -3.557757517, 0.112737397, -0.000731381, 0.013184877, 0.000132645, -0.000338716 }, +{ 0.718398423, 0.003498178, 0.000142202, -0.005724331, 0.00014085, -0.000215321 }, +{ 0.36338171, 0.013523725, 0.000258872, -0.009450269, 0.000439519, -0.000653723 }, +{ 0.981100941, -0.005158493, 0.000243416, -0.005274352, 0.000230742, -0.000336954 }}; + + + tdbOut2 = tdbOut * tdbOut; + float tdbEnt = 70.f; + float tdbEnt2 = tdbEnt*tdbEnt; + float tdbX = tdbOut*tdbEnt; + const int iHS = 3; // heating, 1 spd + + fCap = epCap[ iHS][ 0] + + epCap[ iHS][ 1]*tdbEnt + + epCap[ iHS][ 2]*tdbEnt2 + + epCap[ iHS][ 3]*tdbOut + + epCap[ iHS][ 4]*tdbOut2 + + epCap[ iHS][ 5]*tdbX; + capHt = fCap * rs_cap47; + + fEIR = epEIR[ iHS][ 0] + + epEIR[ iHS][ 1]*tdbEnt + + epEIR[ iHS][ 2]*tdbEnt2 + + epEIR[ iHS][ 3]*tdbOut + + epEIR[ iHS][ 4]*tdbOut2 + + epEIR[ iHS][ 5]*tdbX; + inpHt = capHt * fEIR / rs_COP47; + } +#if defined( _DEBUG) + else + ASSERT( 1); // missing case #endif #if 0 0 capHt = fCap * rs_cap47; 0 inpHt = capNet * fEIR * EIR47 - fanHRtd; #endif - } + } - float COP; - if (capHt < 0.01f || inpHt < 0.01f) { - COP = 0.f; - capHt = 0.f; - } else - COP = COPAdjF * capHt / inpHt; // compressor-only efficiency + float COP; + if (capHt < 0.01f || inpHt < 0.01f) + { COP = 0.f; + capHt = 0.f; + } + else + COP = COPAdjF * capHt / inpHt; // compressor-only efficiency - capHt += capDfHt; // include defrost heat in capacity - capHtMin += capDfHtMin; + capHt += capDfHt; // include defrost heat in capacity + capHtMin += capDfHtMin; - if (!rs_IsASHPVC()) { // single speed - capHtMin = capHt; - inpHtMin = inpHt; - capDfHtMin = capDfHt; - } + if (!rs_IsASHPVC()) + { // single speed + capHtMin = capHt; + inpHtMin = inpHt; + capDfHtMin = capDfHt; + } - return COP; -} // RSYS::rs_PerfASHP2 + return COP; +} // RSYS::rs_PerfASHP2 //----------------------------------------------------------------------------- -RC RSYS::rs_SetupASHP() // set ASHP defaults and derived parameters +RC RSYS::rs_SetupASHP() // set ASHP defaults and derived parameters // caution: all required args assumed present and >0 // call during autosize or later // redundant calls OK { - RC rc = RCOK; + RC rc = RCOK; - // inter-default cap95 / cap47 - // at least one is present (see rs_CkF) - if (!IsSet(RSYS_CAP95)) - rs_cap95 = - ASHPCap95FromCap47(rs_cap47, IsSet(RSYS_CAPRAT9547), rs_capRat9547); - else if (!IsSet(RSYS_CAP47)) - rs_cap47 = - ASHPCap47FromCap95(rs_cap95, IsSet(RSYS_CAPRAT9547), rs_capRat9547); + // inter-default cap95 / cap47 + // at least one is present (see rs_CkF) + if (!IsSet( RSYS_CAP95)) + rs_cap95 = ASHPCap95FromCap47( rs_cap47, IsSet(RSYS_CAPRAT9547), rs_capRat9547); + else if (!IsSet( RSYS_CAP47)) + rs_cap47 = ASHPCap47FromCap95( rs_cap95, IsSet(RSYS_CAPRAT9547), rs_capRat9547); - // fan power included in heating ratings - // derive independently of cooling even though actually the same fan - // WHY: CA procedures allow separate heating / cooling sizing - // say 0 for air-to-water - rs_fanHRtdH = - rs_IsASHPHydronic() ? 0.f : rs_FanHRtdPerTon(rs_cap47 / 12000.f); + // fan power included in heating ratings + // derive independently of cooling even though actually the same fan + // WHY: CA procedures allow separate heating / cooling sizing + // say 0 for air-to-water + rs_fanHRtdH = rs_IsASHPHydronic() + ? 0.f + : rs_FanHRtdPerTon( rs_cap47 / 12000.f); - if (!IsSet(RSYS_CAP17) || rs_IsPkgRoom()) - rs_cap17 = max(rs_CapRat1747() * rs_cap47, 1.f); + if (!IsSet( RSYS_CAP17) || rs_IsPkgRoom()) + rs_cap17 = max( rs_CapRat1747()*rs_cap47, 1.f); - if (!IsSet(RSYS_CAP05)) - rs_cap05 = max(rs_CapRat0547() * rs_cap47, 1.f); + if (!IsSet( RSYS_CAP05)) + rs_cap05 = max(rs_CapRat0547() * rs_cap47, 1.f); - if (!IsSet(RSYS_CAP35)) - rs_cap35 = rs_Cap35Default(rs_cap47, rs_cap17); + if (!IsSet(RSYS_CAP35)) + rs_cap35 = rs_Cap35Default(rs_cap47, rs_cap17); #if ASHP_COPREG == 1 - // "traditional" model - if (!IsSet(RSYS_COP47)) - rs_COP47 = - 0.3038073f * rs_HSPF - 1.984475f * rs_cap17 / rs_cap47 + 2.360116f; - if (!IsSet(RSYS_COP17)) - rs_COP17 = - 0.2359355f * rs_HSPF + 1.205568f * rs_cap17 / rs_cap47 - 0.1660746; + // "traditional" model + if (!IsSet( RSYS_COP47)) + rs_COP47 = 0.3038073f * rs_HSPF - 1.984475f*rs_cap17/rs_cap47 + 2.360116f; + if (!IsSet( RSYS_COP17)) + rs_COP17 = 0.2359355f * rs_HSPF + 1.205568f*rs_cap17/rs_cap47 - 0.1660746; #elif ASHP_COPREG == 2 - // Revised 5-31-2013 - if (!IsSet(RSYS_COP47)) - rs_COP47 = 0.3225f * rs_HSPF + 0.9099f; - if (!IsSet(RSYS_COP17)) - rs_COP17 = 0.2186f * rs_HSPF + 0.6734f; + // Revised 5-31-2013 + if (!IsSet( RSYS_COP47)) + rs_COP47 = 0.3225f * rs_HSPF + 0.9099f; + if (!IsSet( RSYS_COP17)) + rs_COP17 = 0.2186f * rs_HSPF + 0.6734f; #elif ASHP_COPREG == 3 - // COP/HSPF fit 6-4-2013 - if (!IsSet(RSYS_COP47)) - rs_COP47 = -0.0129 * rs_HSPF * rs_HSPF + 0.5397 * rs_HSPF; - if (!IsSet(RSYS_COP17)) - rs_COP17 = -.00970 * rs_HSPF * rs_HSPF + 0.3805 * rs_HSPF; + // COP/HSPF fit 6-4-2013 + if (!IsSet( RSYS_COP47)) + rs_COP47 = -0.0129 * rs_HSPF * rs_HSPF + 0.5397 * rs_HSPF; + if (!IsSet( RSYS_COP17)) + rs_COP17 = -.00970 * rs_HSPF * rs_HSPF + 0.3805 * rs_HSPF; #elif ASHP_COPREG == 4 - // kW per ton fit 6-4-2013 - if (!IsSet(RSYS_COP47)) { - float kwPerTon = -0.089 * rs_HSPF + 1.7236; - rs_COP47 = 12000.f / (kwPerTon * 3413.f); - } - if (!IsSet(RSYS_COP17)) { - float kwPerTon = -0.0722 * rs_HSPF + 1.4896; - rs_COP17 = 12000.f * rs_cap17 / (rs_cap47 * kwPerTon * 3413.f); - } + // kW per ton fit 6-4-2013 + if (!IsSet( RSYS_COP47)) + { float kwPerTon = -0.089 * rs_HSPF + 1.7236; + rs_COP47 = 12000.f / (kwPerTon * 3413.f); + } + if (!IsSet( RSYS_COP17)) + { float kwPerTon = -0.0722 * rs_HSPF + 1.4896; + rs_COP17 = 12000.f * rs_cap17 / (rs_cap47 * kwPerTon * 3413.f); + } #elif ASHP_COPREG == 5 - // force COP17 = 1.8 at HSPF = 6.8 6-4-2013 - // same as ASHP_COPREG == 2 except for COP17 when HSPF < 8 - if (!IsSet(RSYS_COP47)) - rs_COP47 = 0.3225f * rs_HSPF + 0.9099f; - if (!IsSet(RSYS_COP17)) - rs_COP17 = - rs_HSPF < 8 ? 0.5183f * rs_HSPF - 1.7244f : 0.2186f * rs_HSPF + 0.6734f; + // force COP17 = 1.8 at HSPF = 6.8 6-4-2013 + // same as ASHP_COPREG == 2 except for COP17 when HSPF < 8 + if (!IsSet( RSYS_COP47)) + rs_COP47 = 0.3225f * rs_HSPF + 0.9099f; + if (!IsSet( RSYS_COP17)) + rs_COP17 = rs_HSPF < 8 + ? 0.5183f * rs_HSPF - 1.7244f + : 0.2186f * rs_HSPF + 0.6734f; #elif ASHP_COPREG == 6 - if (rs_IsPkgRoom()) { - rs_COP17 = 0.6870f * rs_COP47; - // rs_cap17 set above - } else if (!rs_IsASHPHydronic()) { // COP47: default as per ASHP_COPREG == 2 - // COP17 / COP35: adjust so HSPF is matched - if (!IsSet(RSYS_COP47)) - rs_COP47 = 0.3225f * rs_HSPF + 0.9099f; - if (!IsSet(RSYS_COP17)) { - rs_COP17 = 0.2186f * rs_HSPF + 0.6734f; - int iTry; - RC rc1; - const int nTry = 40; - for (iTry = 0; iTry < nTry; iTry++) { - rc1 = rs_HSPFMatchASHP(); // adjust COP17 to be - // consistent with rs_HSPF - if (rc1 || rs_COP17 < rs_COP47 || IsSet(RSYS_COP47)) - break; // accept rs_COP17 if < rs_COP47 or rs_COP47 is fixed - rs_COP47 += 0.1f; // try again with higher rs_COP47 - } - if ((rc1 || iTry == nTry) && !rs_isAuszH) - rc |= err( - ERR, - "RSYS '%s': No reasonable value found for rsCOP17 and/or rsCOP47." - "\n Check rsHSPF and other heating inputs.", - Name()); - } - } + if (rs_IsPkgRoom()) + { rs_COP17 = 0.6870f * rs_COP47; + // rs_cap17 set above + } + else if (!rs_IsASHPHydronic()) + { // COP47: default as per ASHP_COPREG == 2 + // COP17 / COP35: adjust so HSPF is matched + if (!IsSet( RSYS_COP47)) + rs_COP47 = 0.3225f * rs_HSPF + 0.9099f; + if (!IsSet( RSYS_COP17)) + { rs_COP17 = 0.2186f * rs_HSPF + 0.6734f; + int iTry; + RC rc1; + const int nTry = 40; + for (iTry=0; iTry0 // call during autosize or later // redundant calls OK { - RC rc = RCOK; + RC rc = RCOK; - // inter-default cap95 (aka capC) <-> capH - // at least one is present (see rs_CkF) - if (!IsSet(RSYS_CAP95)) - rs_cap95 = - WSHPPerf.whp_CapCFromCapH(rs_capH, IsSet(RSYS_CAPRATCH), rs_capRatCH); - else if (!IsSet(RSYS_CAPH)) - rs_capH = - WSHPPerf.whp_CapHFromCapC(rs_cap95, IsSet(RSYS_CAPRATCH), rs_capRatCH); + // inter-default cap95 (aka capC) <-> capH + // at least one is present (see rs_CkF) + if (!IsSet(RSYS_CAP95)) + rs_cap95 = WSHPPerf.whp_CapCFromCapH(rs_capH, IsSet(RSYS_CAPRATCH), rs_capRatCH); + else if (!IsSet(RSYS_CAPH)) + rs_capH = WSHPPerf.whp_CapHFromCapC(rs_cap95, IsSet(RSYS_CAPRATCH), rs_capRatCH); - rs_fanHRtdH = rs_FanHRtdPerTon(rs_capH / 12000.f); + rs_fanHRtdH = rs_FanHRtdPerTon(rs_capH / 12000.f); - return rc; -} // RSYS::rs_SetupWSHP + return rc; +} // RSYS::rs_SetupWSHP //----------------------------------------------------------------------------- -RC RSYS::rs_SetupCHDHW() // check/set up combined heat / DWH +RC RSYS::rs_SetupCHDHW() // check/set up combined heat / DWH // call from rs_TopRSys2() // *after* rs_SetWorkingPtrs() // *after* topDHW() -- DHWSYS, DHWHEATER, etc must exist // returns RCOK iff CHDHW config is valid { - RC rc = RCOK; + RC rc = RCOK; - DHWSYS *pWS = rs_GetCHDHWSYS(); + DHWSYS* pWS = rs_GetCHDHWSYS(); - if (!pWS) - rc |= oer("Missing rsCHDHWSYS"); // impossible? due to prior checks - else - rc |= pWS->ws_CheckCHDHWConfig(this); + if (!pWS) + rc |= oer("Missing rsCHDHWSYS"); // impossible? due to prior checks + else + rc |= pWS->ws_CheckCHDHWConfig( this); - if (!rc) { - rs_pCHDHW = new CHDHW(this); - float blowerEfficacy = float(rs_pCHDHW->hvt_GetRatedBlowerEfficacy()); - if (!IsSet(RSYS_FANPWRH)) - rs_fanPwrH = blowerEfficacy; - rc |= rs_pCHDHW->hvt_Init(rs_fanPwrH); + if (!rc) + { + rs_pCHDHW = new CHDHW( this); + float blowerEfficacy = float(rs_pCHDHW->hvt_GetRatedBlowerEfficacy()); + if (!IsSet(RSYS_FANPWRH)) + rs_fanPwrH = blowerEfficacy; + rc |= rs_pCHDHW->hvt_Init(rs_fanPwrH); - rs_tdDesH = rs_pCHDHW->hvt_GetTRise(); - rs_capH = rs_pCHDHW->hvt_GetRatedCap(); + rs_tdDesH = rs_pCHDHW->hvt_GetTRise(); + rs_capH = rs_pCHDHW->hvt_GetRatedCap(); - // rated fan heat (unused?) - rs_fanHRtdH = - blowerEfficacy * rs_pCHDHW->hvt_GetRatedBlowerAVF() * BtuperWh; - } + // rated fan heat (unused?) + rs_fanHRtdH = blowerEfficacy * rs_pCHDHW->hvt_GetRatedBlowerAVF() * BtuperWh; + } - return rc; -} // RSYS::rs_SetupCHDHW + return rc; +} // RSYS::rs_SetupCHDHW //---------------------------------------------------------------------------- -void RSYS::rs_CurCapHtCHDHW() // current CHDHW heating cap etc +void RSYS::rs_CurCapHtCHDHW() // current CHDHW heating cap etc // sets rs_tCoilEW, rs_capHtMin, rs_capHt, and rs_speedFMin { - DHWSYS *pWS = rs_GetCHDHWSYS(); - rs_tCoilEW = pWS->ws_GetCHDHWTSupply(); - rs_pCHDHW->hvt_CapHtgMinMax(rs_tCoilEW, rs_capHtMin, rs_capHt); - rs_speedFMin = rs_capHtMin / rs_capHt; -} // RSYS::rs_CurCapHtCHDHW + DHWSYS * pWS = rs_GetCHDHWSYS(); + rs_tCoilEW = pWS->ws_GetCHDHWTSupply(); + rs_pCHDHW->hvt_CapHtgMinMax(rs_tCoilEW, rs_capHtMin, rs_capHt); + rs_speedFMin = rs_capHtMin / rs_capHt; +} // RSYS::rs_CurCapHtCHDHW //----------------------------------------------------------------------------- // helper performance point re btwxt setup // = temperature + array of perf values -struct VSPERFP { - enum { ppCAPHS, ppINPHS, ppCAPLS, ppINPLS, ppCOUNT }; - VSPERFP(float temp, float capHS, float COPHS, float capLS, float COPLS) - : pp_temp(temp) { - pp_data[ppCAPHS] = capHS; - pp_data[ppINPHS] = abs(capHS / max(COPHS, .1f)); - pp_data[ppCAPLS] = capLS; - pp_data[ppINPLS] = abs(capLS / max(COPLS, .1f)); - } - - double pp_temp; // temp for this point, F - std::array pp_data{0.}; // data - double operator[](int i) const { return pp_data[i]; } - static int NData() { return ppCOUNT; } -}; // struct VSPERFP +struct VSPERFP +{ + enum { ppCAPHS, ppINPHS, ppCAPLS, ppINPLS, ppCOUNT }; + VSPERFP(float temp, float capHS, float COPHS, float capLS, float COPLS) + : pp_temp(temp) + { + pp_data[ppCAPHS] = capHS; + pp_data[ppINPHS] = abs(capHS / max(COPHS, .1f)); + pp_data[ppCAPLS] = capLS; + pp_data[ppINPLS] = abs(capLS / max(COPLS, .1f)); + } + + double pp_temp; // temp for this point, F + std::array< double, ppCOUNT> pp_data{ 0. }; // data + double operator [](int i) const { return pp_data[i]; } + static int NData() { return ppCOUNT; } +}; // struct VSPERFP //----------------------------------------------------------------------------- -RC RSYS::rs_SetRunConstantsASHP() // finalize constant data for simulation +RC RSYS::rs_SetRunConstantsASHP() // finalize constant data for simulation // uses: rs_cap/COP 47/35/17/05 // sets: rs_inp17/35/47, rs_COP35 (if needed), slopes, performance map(s), // returns RCOK iff success { - RC rc = RCOK; - - // min speed defaults - // WHY: multiple calls possible with different rs_COP17 (at least) - if (!IsSet(RSYS_COPMIN47)) - rs_COPMin47 = rs_COP47; - if (!IsSet(RSYS_COPMIN17)) - rs_COPMin17 = rs_COP17; - if (!IsSet(RSYS_COPMIN05)) - rs_COPMin05 = rs_COP05; - - rs_inp47 = rs_cap47 / max(rs_COP47, .1f); // full speed input power, Btuh - rs_inp17 = rs_cap17 / max(rs_COP17, .1f); + RC rc = RCOK; - if (!IsSet(RSYS_COP35)) { - rs_inp35 = rs_Inp35Default(rs_inp47, rs_inp17); - rs_COP35 = rs_cap35 / max(rs_inp35, .1f); - } else - rs_inp35 = rs_cap35 / max(rs_COP35, .1f); + // min speed defaults + // WHY: multiple calls possible with different rs_COP17 (at least) + if (!IsSet(RSYS_COPMIN47)) + rs_COPMin47 = rs_COP47; + if (!IsSet(RSYS_COPMIN17)) + rs_COPMin17 = rs_COP17; + if (!IsSet(RSYS_COPMIN05)) + rs_COPMin05 = rs_COP05; + + rs_inp47 = rs_cap47 / max( rs_COP47, .1f); // full speed input power, Btuh + rs_inp17 = rs_cap17 / max( rs_COP17, .1f); + + if (!IsSet( RSYS_COP35)) + { rs_inp35 = rs_Inp35Default( rs_inp47, rs_inp17); + rs_COP35 = rs_cap35 / max( rs_inp35, .1f); + } + else + rs_inp35 = rs_cap35 / max( rs_COP35, .1f); - rs_ASHPCapF[0] = (rs_cap47 - rs_cap17) / (47.f - 17.f); - rs_ASHPInpF[0] = (rs_inp47 - rs_inp17) / (47.f - 17.f); + rs_ASHPCapF[ 0] = (rs_cap47-rs_cap17) / (47.f-17.f); + rs_ASHPInpF[ 0] = (rs_inp47-rs_inp17) / (47.f-17.f); - rs_ASHPCapF[1] = (rs_cap35 - rs_cap17) / (35.f - 17.f); - rs_ASHPInpF[1] = (rs_inp35 - rs_inp17) / (35.f - 17.f); + rs_ASHPCapF[ 1] = (rs_cap35-rs_cap17) / (35.f-17.f); + rs_ASHPInpF[ 1] = (rs_inp35-rs_inp17) / (35.f-17.f); - // Btwxt heating performance map setup - // currently (2-22) used for rs_IsVC() only - float capMin35 = rs_Cap35Default(rs_CapMin47(), rs_CapMin17()); + // Btwxt heating performance map setup + // currently (2-22) used for rs_IsVC() only + float capMin35 = rs_Cap35Default(rs_CapMin47(), rs_CapMin17()); - if (!IsSet(RSYS_COPMIN35)) { - float inpMin47 = rs_CapMin47() / max(rs_COPMin47, .1f); - float inpMin17 = rs_CapMin17() / max(rs_COPMin17, .1f); - float inpMin35 = rs_Inp35Default(inpMin47, inpMin17); - rs_COPMin35 = capMin35 / max(inpMin35, .1f); - } + if (!IsSet(RSYS_COPMIN35)) + { + float inpMin47 = rs_CapMin47() / max(rs_COPMin47, .1f); + float inpMin17 = rs_CapMin17() / max(rs_COPMin17, .1f); + float inpMin35 = rs_Inp35Default(inpMin47, inpMin17); + rs_COPMin35 = capMin35 / max(inpMin35, .1f); + } #if 0 float tx = FindLimitingPoint(5.f, rs_cap05, rs_COP05, 17.f, rs_cap17, rs_COP17); #endif - // integrated performance (including defrost degradation) - std::vector ppV; - ppV.emplace_back(5.f, rs_cap05, rs_COP05, rs_CapMin05(), rs_COPMin05); - ppV.emplace_back(17.f, rs_cap17, rs_COP17, rs_CapMin17(), rs_COPMin17); - ppV.emplace_back(35.f, rs_cap35, rs_COP35, capMin35, rs_COPMin35); - ppV.emplace_back(47.f, rs_cap47, rs_COP47, rs_CapMin47(), rs_COPMin47); - rc |= rs_SetupBtwxt("Heating w/defrost", rs_pRgiHtg[0], ppV); - - // steady-state performance (no defrost degradation) - // linear w/o 35 F point - // used iff temp in 17 - 45 F range - ppV.clear(); - ppV.emplace_back(17.f, rs_cap17, rs_COP17, rs_CapMin17(), rs_COPMin17); - ppV.emplace_back(47.f, rs_cap47, rs_COP47, rs_CapMin47(), rs_COPMin47); - rc |= rs_SetupBtwxt("Heating w/o defrost", rs_pRgiHtg[1], ppV); - - return rc; -} // RSYS::rs_SetRunConstantsASHP + // integrated performance (including defrost degradation) + std::vector< VSPERFP> ppV; + ppV.emplace_back(5.f, rs_cap05, rs_COP05, rs_CapMin05(), rs_COPMin05); + ppV.emplace_back(17.f, rs_cap17, rs_COP17, rs_CapMin17(), rs_COPMin17); + ppV.emplace_back(35.f, rs_cap35, rs_COP35, capMin35, rs_COPMin35); + ppV.emplace_back(47.f, rs_cap47, rs_COP47, rs_CapMin47(), rs_COPMin47); + rc |= rs_SetupBtwxt("Heating w/defrost", rs_pRgiHtg[0], ppV); + + // steady-state performance (no defrost degradation) + // linear w/o 35 F point + // used iff temp in 17 - 45 F range + ppV.clear(); + ppV.emplace_back(17.f, rs_cap17, rs_COP17, rs_CapMin17(), rs_COPMin17); + ppV.emplace_back(47.f, rs_cap47, rs_COP47, rs_CapMin47(), rs_COPMin47); + rc |= rs_SetupBtwxt("Heating w/o defrost", rs_pRgiHtg[1], ppV); + + return rc; +} // RSYS::rs_SetRunConstantsASHP //----------------------------------------------------------------------------- -RC RSYS::rs_SetupBtwxtClg() { - RC rc = RCOK; +RC RSYS::rs_SetupBtwxtClg() +{ + RC rc = RCOK; - // vector of performance points - // note cooling capacities <0 - std::vector ppV; - ppV.emplace_back(82.f, -rs_cap82, rs_COP82, -rs_CapMin82(), rs_COPMin82); - ppV.emplace_back(95.f, -rs_cap95, rs_COP95, -rs_CapMin95(), rs_COPMin95); - ppV.emplace_back(115.f, -rs_cap115, rs_COP115, -rs_CapMin115(), rs_COPMin115); + // vector of performance points + // note cooling capacities <0 + std::vector< VSPERFP> ppV; + ppV.emplace_back(82.f, -rs_cap82, rs_COP82, -rs_CapMin82(), rs_COPMin82); + ppV.emplace_back(95.f, -rs_cap95, rs_COP95, -rs_CapMin95(), rs_COPMin95); + ppV.emplace_back(115.f, -rs_cap115, rs_COP115, -rs_CapMin115(), rs_COPMin115); - // Populate Btwxt interpolator grid data from performance points - rc |= rs_SetupBtwxt("Cooling", rs_pRgiClg, ppV); + // Populate Btwxt interpolator grid data from performance points + rc |= rs_SetupBtwxt("Cooling", rs_pRgiClg, ppV); - return rc; -} // RSYS::rs_SetupBtwxtClg + return rc; +} // RSYS::rs_SetupBtwxtClg //----------------------------------------------------------------------------- -float RSYS::rs_CapHtCurSpeedF() const // heating cap at current speed +float RSYS::rs_CapHtCurSpeedF() const // heating cap at current speed // valid for rs_speedFMin <= rs_speedF <= 1 // returns non-cycling heating capacity (including fan and defrost aux), Btuh { - // cap = rs_capHtMin - // + (rs_capHt - rs_capHtMin) * (rs_speedF - rs_speedFMin) / (1.f - - // rs_speedFMin); - // and rs_speedFMin = rs_capHtMin / rs_capHt - // thus cap reduces to ... - return rs_capHt * rs_speedF; -} // RSYS::rs_CapHtCurSpeedF +// cap = rs_capHtMin +// + (rs_capHt - rs_capHtMin) * (rs_speedF - rs_speedFMin) / (1.f - rs_speedFMin); +// and rs_speedFMin = rs_capHtMin / rs_capHt +// thus cap reduces to ... + return rs_capHt * rs_speedF; +} // RSYS::rs_CapHtCurSpeedF //----------------------------------------------------------------------------- -float RSYS::rs_CapDfHtCurSpeedF() const { - float capDfHt = rs_speedF == 1.f - ? rs_capDfHt - : rs_capDfHtMin + (rs_capDfHt - rs_capDfHtMin) * - (rs_speedF - rs_speedFMin) / - (1.f - rs_speedFMin); - return capDfHt; -} // RSYS::rs_capDfHtCurSpeedF() +float RSYS::rs_CapDfHtCurSpeedF() const +{ + float capDfHt = rs_speedF == 1.f + ? rs_capDfHt + : rs_capDfHtMin + + (rs_capDfHt - rs_capDfHtMin) * (rs_speedF - rs_speedFMin) / (1.f - rs_speedFMin); + return capDfHt; +} // RSYS::rs_capDfHtCurSpeedF() //----------------------------------------------------------------------------- -float RSYS::rs_InpHtCurSpeedF() const { - float inpHt = rs_speedF == 1.f - ? rs_inpHt - : rs_inpHtMin + (rs_inpHt - rs_inpHtMin) * - (rs_speedF - rs_speedFMin) / - (1.f - rs_speedFMin); - return inpHt; -} // RSYS::rs_InpHtCurSpeedF() +float RSYS::rs_InpHtCurSpeedF() const +{ + float inpHt = rs_speedF == 1.f + ? rs_inpHt + : rs_inpHtMin + + (rs_inpHt - rs_inpHtMin) * (rs_speedF - rs_speedFMin) / (1.f - rs_speedFMin); + return inpHt; +} // RSYS::rs_InpHtCurSpeedF() //----------------------------------------------------------------------------- -RC RSYS::rs_SetupBtwxt( // init/populate btwxt for heating runtime interpolation - const char *tag, // identifying text for this interpolator (for messages) - Btwxt::RegularGridInterpolator * - &pRgi, // returned: heap ptr to Btwxt interpolator object - // note: any prior contents deleted - const std::vector ppV) // performance point vector +RC RSYS::rs_SetupBtwxt( // init/populate btwxt for heating runtime interpolation + const char* tag, // identifying text for this interpolator (for messages) + Btwxt::RegularGridInterpolator*& pRgi, // returned: heap ptr to Btwxt interpolator object + // note: any prior contents deleted + const std::vector< VSPERFP> ppV) // performance point vector { - RC rc = RCOK; + RC rc = RCOK; - delete pRgi; // delete prior if any + delete pRgi; // delete prior if any - int nD = VSPERFP::NData(); - int nPts = static_cast(ppV.size()); - std::vector gridODB; - std::vector> values; - values.resize(nD); + int nD = VSPERFP::NData(); + int nPts = static_cast(ppV.size()); + std::vector< double> gridODB; + std::vector< std::vector> values; + values.resize(nD); - for (int iPt = 0; iPt < nPts; iPt++) { - const VSPERFP &pp = ppV[iPt]; - gridODB.push_back(pp.pp_temp); - // values[iPt].resize(nD); - for (int iD = 0; iD < nD; iD++) { - values[iD].push_back(pp[iD]); - } - } + for (int iPt = 0; iPt(this); + auto MX = std::make_shared< CourierMsgHandlerRec>(this); - // single grid variable = dry-bulb temp (allow linear extrapolation) - Btwxt::GridAxis dbtRange(gridODB, Btwxt::InterpolationMethod::linear, - Btwxt::ExtrapolationMethod::linear, - {-DBL_MAX, DBL_MAX}, "Dry-bulb temp", MX); + // single grid variable = dry-bulb temp (allow linear extrapolation) + Btwxt::GridAxis dbtRange(gridODB, + Btwxt::InterpolationMethod::linear, Btwxt::ExtrapolationMethod::linear, + { -DBL_MAX, DBL_MAX }, "Dry-bulb temp", MX); - std::vector dbt{dbtRange}; + std::vector dbt{ dbtRange}; - pRgi = new Btwxt::RegularGridInterpolator(dbt, values, tag, MX); + pRgi = new Btwxt::RegularGridInterpolator(dbt, values, tag, MX); #if 0 // test code @@ -4991,23 +5021,23 @@ RC RSYS::rs_SetupBtwxt( // init/populate btwxt for heating runtime interpolation result = (*rs_pRgiHtg)(targ); #endif - return rc; + return rc; -} // RSYS::rs_SetupBtwxt +} // RSYS::rs_SetupBtwxt //----------------------------------------------------------------------------- -RC RSYS::rs_GetPerfBtwxt( // retrieve performance info from btwxt map - Btwxt::RegularGridInterpolator *pRgi, // interpolation data - float tdbOut, // outdoor dry-bulb temp, F - float &cap, // returned: full speed capacity - float &inp, // returned: full speed input power, Btuh - float &capMin, // returned: min speed capacity, Btuh - float &inpMin) // returned: min speed input power, Btuh +RC RSYS::rs_GetPerfBtwxt( // retrieve performance info from btwxt map + Btwxt::RegularGridInterpolator* pRgi, // interpolation data + float tdbOut, // outdoor dry-bulb temp, F + float& cap, // returned: full speed capacity + float& inp, // returned: full speed input power, Btuh + float& capMin, // returned: min speed capacity, Btuh + float& inpMin) // returned: min speed input power, Btuh // Note: data generally net (include fan heat/power) per setup input // returns RCOK iff success // else ? { - RC rc = RCOK; -#if 0 && defined(_DEBUG) + RC rc = RCOK; +#if 0 && defined( _DEBUG) if (tdbOut > 33.f && tdbOut < 34.f) printf("\nCold"); #endif @@ -5016,422 +5046,424 @@ RC RSYS::rs_GetPerfBtwxt( // retrieve performance info from btwxt map std::vector< double> targ{ tdbOut, 999. }; auto result = (*pRgi)(targ); #else - std::vector targ{tdbOut}; - auto result = (*pRgi)(targ); -#endif - cap = result[VSPERFP::ppCAPHS]; - inp = result[VSPERFP::ppINPHS]; - capMin = result[VSPERFP::ppCAPLS]; - inpMin = result[VSPERFP::ppINPLS]; - return rc; -} // RSYS::rs_GetPerfBtwxt + std::vector< double> targ{ tdbOut }; + auto result = (*pRgi)(targ); +#endif + cap = result[VSPERFP::ppCAPHS]; + inp = result[VSPERFP::ppINPHS]; + capMin = result[VSPERFP::ppCAPLS]; + inpMin = result[VSPERFP::ppINPLS]; + return rc; +} // RSYS::rs_GetPerfBtwxt //----------------------------------------------------------------------------- -float RSYS::rs_CapEffASHP( // performance at current conditions - float tdbOut /*=-999.f*/, // outdoor dry-bulb temp, F - // default = rs_tdbOut (weather file value or - // expression) - int ashpModel /*=0*/, // alternative model - // 0=CSE, 1=MP, 2=ESL, 3=E+ - // +0x100: do NOT model defrost heating - // +0x200: ignore rs_fEffH (efficiency adjustment) - float fanHRtd /*=-1.f*/, // fan power included in rating, Btuh - // default = rs_fanHRtdH - float fanHOpr /*=-1.f*/, // operating fan power, Btuh - // default = rs_fanHeatH - float COPAdjF /*=-1.f*/) // COP adjustment factor - // default = rs_fEffH +float RSYS::rs_CapEffASHP( // performance at current conditions + float tdbOut/*=-999.f*/, // outdoor dry-bulb temp, F + // default = rs_tdbOut (weather file value or expression) + int ashpModel /*=0*/, // alternative model + // 0=CSE, 1=MP, 2=ESL, 3=E+ + // +0x100: do NOT model defrost heating + // +0x200: ignore rs_fEffH (efficiency adjustment) + float fanHRtd /*=-1.f*/, // fan power included in rating, Btuh + // default = rs_fanHRtdH + float fanHOpr /*=-1.f*/, // operating fan power, Btuh + // default = rs_fanHeatH + float COPAdjF /*=-1.f*/) // COP adjustment factor + // default = rs_fEffH // model does not depend on indoor conditions // sets rs_effHt, rs_capHt, and rs_capDfHt -// returns rs_capHt, Btuh = total heating capacity including fan and defrost if -// any -{ - if (tdbOut < -998.f) - tdbOut = rs_tdbOut; - if (fanHRtd < 0.f) - fanHRtd = rs_fanHRtdH; - if (fanHOpr < 0.f) - fanHOpr = rs_fanHeatH; - if (COPAdjF < 0.f) - COPAdjF = rs_fEffH; - - if (tdbOut < rs_ASHPLockOutT) { - rs_effHt = 0.f; // compressor is unavailable - rs_capHt = rs_capHtMin = fanHOpr; // compressor does nothing - rs_inpHt = rs_inpHtMin = 0.f; - } else { - rs_effHt = - rs_PerfASHP2(ashpModel, tdbOut, fanHRtd, rs_capHt, rs_inpHt, rs_capDfHt, - rs_capHtMin, rs_inpHtMin, rs_capDfHtMin, COPAdjF); - rs_capHt += fanHOpr; - rs_capHtMin += fanHOpr * rs_speedFMin; - } - return rs_CapHtCurSpeedF(); -} // RSYS::rs_CapEffASHP +// returns rs_capHt, Btuh = total heating capacity including fan and defrost if any +{ + if (tdbOut < -998.f) + tdbOut = rs_tdbOut; + if (fanHRtd < 0.f) + fanHRtd = rs_fanHRtdH; + if (fanHOpr < 0.f) + fanHOpr = rs_fanHeatH; + if (COPAdjF < 0.f) + COPAdjF = rs_fEffH; + + if (tdbOut < rs_ASHPLockOutT) + { rs_effHt = 0.f; // compressor is unavailable + rs_capHt = rs_capHtMin = fanHOpr; // compressor does nothing + rs_inpHt = rs_inpHtMin = 0.f; + } + else + { rs_effHt = rs_PerfASHP2(ashpModel, tdbOut, fanHRtd, rs_capHt, rs_inpHt, rs_capDfHt, + rs_capHtMin, rs_inpHtMin, rs_capDfHtMin, COPAdjF); + rs_capHt += fanHOpr; + rs_capHtMin += fanHOpr * rs_speedFMin; + } + return rs_CapHtCurSpeedF(); +} // RSYS::rs_CapEffASHP //----------------------------------------------------------------------------- -float RSYS::rs_CapEffASHP2() // performance at current conditions (no defaults) +float RSYS::rs_CapEffASHP2() // performance at current conditions (no defaults) // main simulation variant of rs_CapEffASHP() (see above) { - if (rs_tdbOut < rs_ASHPLockOutT) { - rs_effHt = 0.f; // compressor is unavailable - rs_capHt = rs_capHtMin = rs_fanHeatH; // compressor does nothing - rs_inpHt = rs_inpHtMin = 0.f; - } else if (rs_capHt == 0.f) { - rs_effHt = - rs_PerfASHP2(0, rs_tdbOut, rs_fanHRtdH, rs_capHt, rs_inpHt, rs_capDfHt, - rs_capHtMin, rs_inpHtMin, rs_capDfHtMin, rs_fEffH); - // add operating fan heat/power - rs_capHt += rs_fanHeatH; - rs_capHtMin += rs_fanHeatH * rs_speedFMin; - } - return rs_CapHtCurSpeedF(); -} // RSYS::rs_CapEffASHP2 + if (rs_tdbOut < rs_ASHPLockOutT) + { rs_effHt = 0.f; // compressor is unavailable + rs_capHt = rs_capHtMin = rs_fanHeatH; // compressor does nothing + rs_inpHt = rs_inpHtMin = 0.f; + } + else if (rs_capHt == 0.f) + { rs_effHt = rs_PerfASHP2( 0, rs_tdbOut, rs_fanHRtdH, rs_capHt, rs_inpHt, rs_capDfHt, + rs_capHtMin, rs_inpHtMin, rs_capDfHtMin, rs_fEffH); + // add operating fan heat/power + rs_capHt += rs_fanHeatH; + rs_capHtMin += rs_fanHeatH * rs_speedFMin; + } + return rs_CapHtCurSpeedF(); +} // RSYS::rs_CapEffASHP2 //----------------------------------------------------------------------------- -/*static*/ double RSYS::rs_CallCalcHSPF(void *pO, double &COP17) +/*static*/ double RSYS::rs_CallCalcHSPF( void* pO, double& COP17) // callback function for secant() // calcs HSPF using current COP17 // re search for COP17 consistent with specified HSPF // returns HSPF { - RSYS *pRS = (RSYS *)pO; - if (COP17 < 1.01) - COP17 = 1.01; // prevent impossible - pRS->rs_COP17 = COP17; - pRS->rs_SetRunConstantsASHP(); - int ashpModel = 0; - double hspf = pRS->rs_HSPFCheckASHP(ashpModel); - return hspf; -} // RSYS::rs_CallCalcHSPF + RSYS* pRS = (RSYS *)pO; + if (COP17 < 1.01) + COP17 = 1.01; // prevent impossible + pRS->rs_COP17 = COP17; + pRS->rs_SetRunConstantsASHP(); + int ashpModel = 0; + double hspf = pRS->rs_HSPFCheckASHP( ashpModel); + return hspf; +} // RSYS::rs_CallCalcHSPF //---------------------------------------------------------------------------- -RC RSYS::rs_HSPFMatchASHP() // adjust COP17 to yield user's HSPF +RC RSYS::rs_HSPFMatchASHP() // adjust COP17 to yield user's HSPF // uses rs_HSPF, rs_cap47, rs_COP47, rs_cap35, rs_COP35, rs_cap17 (at least) // adjusts rs_COP17 so calculated HSPF = rs_HSPF // returns RCOK iff COP17 found { - int rc = RCOK; - rs_SetRunConstantsASHP(); // ensure consistent starting point - int ashpModel = 0; - double hspf = rs_HSPFCheckASHP(ashpModel); - double COP17 = rs_COP17; - float dCOP17 = hspf > rs_HSPF ? -.1f : +.1f; - int ret = secant(rs_CallCalcHSPF, this, rs_HSPF, .001, COP17, hspf, // x1, f1 - COP17 + dCOP17); // x2 (f2) - if (ret != 0) - rc = RCBAD; // search failed (caller must handle) + int rc = RCOK; + rs_SetRunConstantsASHP(); // ensure consistent starting point + int ashpModel = 0; + double hspf = rs_HSPFCheckASHP( ashpModel); + double COP17 = rs_COP17; + float dCOP17 = hspf > rs_HSPF ? -.1f : +.1f; + int ret = secant( rs_CallCalcHSPF, this, rs_HSPF, .001, + COP17, hspf, // x1, f1 + COP17 + dCOP17); // x2 (f2) + if (ret != 0) + rc = RCBAD; // search failed (caller must handle) - return rc; + return rc; -} // RSYS::HSPFMatchASHP +} // RSYS::HSPFMatchASHP //---------------------------------------------------------------------------- -float RSYS::rs_HSPFCheckASHP( // calculate region 4 HSPF - int options /*=0*/) // low bits = ashpModel - // 0x100 = DbPrintf info for all bins +float RSYS::rs_HSPFCheckASHP( // calculate region 4 HSPF + int options/*=0*/) // low bits = ashpModel + // 0x100 = DbPrintf info for all bins // testing aid, "back calculates" HSPF from ASHP ratings // method = AHRI 210/240 single speed // Prerequisite: rs_CalcInputsAndSlopesASHP() or equivalent // (sets values used by rs_CapEffASHP()) // returns HSPF calculated from RSYS properties { - // bin hours for region 4 - static int nHrReg4[] = - // 62 57 52 ... - {297, 250, 232, 209, 225, 245, 283, 196, 124, 81, 58, 29, 14, 5, 2, 0}; - static double fHrReg4[] = - // 62 57 52 ... - {0.132, 0.111, 0.103, 0.093, 0.100, 0.109, 0.126, 0.087, - 0.055, 0.036, 0.026, 0.013, 0.006, 0.002, 0.001, 0.}; - - if (!rs_IsASHP()) - return -1.f; // not ASHP! - - int ashpModel = options & 0x1F; - int bDoPrint = (options & 0x100) != 0; - - [[maybe_unused]] int nHrTot = 0; // total bin hours (s/b 2250 for reg 4) - const float tDbIn = 65.f; // indoor design temp - const float tDbDes = 5.f; // outdoor design temp - const float DHR // design heating requirement per AHRI - = rs_DHR(rs_cap47 * (tDbIn - tDbDes) / 60.f); - double outTot = 0.; // total heat delivered (including fan heat), Btu - double inpTot = 0.; // total input (including fan), Btu - const float C = 0.77f; // AHRI "correction factor" - const float cd = rs_CdH; // cyclic degradation coefficient - const float tOff = 0.f; // low temp cut-out "off" temp - const float tOn = 5.f; // low temp cut-out "on" temp - float lockOutTSave = rs_ASHPLockOutT; - rs_ASHPLockOutT = -999.f; // disable our lockout model - // handled via delta in loop below - - rs_CapEffASHP(47.f, - ashpModel + 0x100); // debug aid (check values with fan power) - rs_CapEffASHP(17.f, ashpModel + 0x100); - - if (bDoPrint) - DbPrintf("\nHSPF calc model=%d\n", ashpModel); - - for (int iBin = 0; nHrReg4[iBin] > 0; iBin++) { - nHrTot += nHrReg4[iBin]; - float tDbBin = float(62 - iBin * 5); - - rs_CapEffASHP(tDbBin, ashpModel + 0x100, 0.f, 0.f, - 1.f); // derive capacity and efficiency for bin temp - // no fan heat adjustments - // no COP adjust - // load - float f = (tDbBin < tDbIn && tDbDes < tDbIn) - ? (tDbIn - tDbBin) / (tDbIn - tDbDes) - : 0.f; - double BL = f * C * DHR; // building heating load (Eqn 4.2-2) - // always >= 0 - - // other factors - double X = rs_capHt > 0.f ? min(BL / rs_capHt, 1.) - : double(BL > 0.); // 1. iff load else 0. - double PLF = 1. - cd * (1. - X); - - double Eh = rs_effHt > 0. ? rs_capHt / rs_effHt // input, Btuh - : 0.; - [[maybe_unused]] double EhW = Eh / 3.413; // input, W (check value) - - // low temp cut-out - float delta = tDbBin <= tOff || rs_effHt < 1. ? 0.f - : tDbBin > tOn ? 1.f - : 0.5f; - - double eh = X * Eh * delta / PLF; - double ehBin = eh * fHrReg4[iBin]; - - double RH = BL - X * delta * rs_capHt; // backup - double RHBin = RH * fHrReg4[iBin]; - - double inpBin = ehBin + RHBin; - [[maybe_unused]] double inpBinWh = inpBin / 3.413; - - double outBin = BL * fHrReg4[iBin]; - inpTot += inpBin; - outTot += outBin; - if (bDoPrint) - DbPrintf("%4.f %6.f %6.f %6.3f %6.f %6.f %6.f\n", tDbBin, outBin, - rs_capHt, rs_effHt, ehBin, RHBin, inpBin); - } - - double inpTotWh = inpTot / 3.413; - float HSPF = outTot / inpTotWh; - - if (bDoPrint) - DbPrintf("\noutTot=%0.f inpTot=%0.f inpTotWh=%0.f HSPF=%0.3f\n", outTot, - inpTot, inpTotWh, HSPF); - - rs_ASHPLockOutT = lockOutTSave; // restore lockout input - - return HSPF; - -} // RSYS::rs_HSPFCheckASHP +// bin hours for region 4 +static int nHrReg4[] = +// 62 57 52 ... +{ 297,250,232,209,225,245,283,196,124, 81, 58, 29, 14, 5, 2, 0 }; +static double fHrReg4[] = +// 62 57 52 ... +{ 0.132,0.111,0.103,0.093,0.100,0.109,0.126,0.087,0.055,0.036,0.026,0.013,0.006,0.002,0.001, 0. }; + + if (!rs_IsASHP()) + return -1.f; // not ASHP! + + int ashpModel = options & 0x1F; + int bDoPrint = (options & 0x100) != 0; + + [[ maybe_unused]] int nHrTot = 0; // total bin hours (s/b 2250 for reg 4) + const float tDbIn = 65.f; // indoor design temp + const float tDbDes = 5.f; // outdoor design temp + const float DHR // design heating requirement per AHRI + = rs_DHR( rs_cap47 * (tDbIn - tDbDes) / 60.f); + double outTot = 0.; // total heat delivered (including fan heat), Btu + double inpTot = 0.; // total input (including fan), Btu + const float C = 0.77f; // AHRI "correction factor" + const float cd = rs_CdH; // cyclic degradation coefficient + const float tOff = 0.f; // low temp cut-out "off" temp + const float tOn = 5.f; // low temp cut-out "on" temp + float lockOutTSave = rs_ASHPLockOutT; + rs_ASHPLockOutT = -999.f; // disable our lockout model + // handled via delta in loop below + + rs_CapEffASHP( 47.f, ashpModel+0x100); // debug aid (check values with fan power) + rs_CapEffASHP( 17.f, ashpModel+0x100); + + if (bDoPrint) + DbPrintf( "\nHSPF calc model=%d\n", ashpModel); + + for (int iBin=0; nHrReg4[ iBin] > 0; iBin++) + { nHrTot += nHrReg4[ iBin]; + float tDbBin = float( 62 - iBin*5); + + rs_CapEffASHP( tDbBin, ashpModel+0x100, 0.f, 0.f, 1.f); // derive capacity and efficiency for bin temp + // no fan heat adjustments + // no COP adjust + // load + float f = (tDbBin < tDbIn && tDbDes < tDbIn) + ? (tDbIn - tDbBin) / (tDbIn - tDbDes) + : 0.f; + double BL = f * C * DHR; // building heating load (Eqn 4.2-2) + // always >= 0 + + // other factors + double X = rs_capHt > 0.f + ? min( BL/rs_capHt, 1.) + : double( BL > 0.); // 1. iff load else 0. + double PLF = 1. - cd * ( 1. - X); + + double Eh = rs_effHt > 0. + ? rs_capHt / rs_effHt // input, Btuh + : 0.; + [[maybe_unused]] double EhW = Eh / 3.413; // input, W (check value) + + // low temp cut-out + float delta = tDbBin <= tOff || rs_effHt < 1. ? 0.f + : tDbBin > tOn ? 1.f + : 0.5f; + + double eh = X * Eh * delta / PLF; + double ehBin = eh * fHrReg4[ iBin]; + + double RH = BL - X * delta * rs_capHt; // backup + double RHBin = RH * fHrReg4[ iBin]; + + double inpBin = ehBin + RHBin; + [[maybe_unused]] double inpBinWh = inpBin / 3.413; + + double outBin = BL * fHrReg4[ iBin]; + inpTot += inpBin; + outTot += outBin; + if (bDoPrint) + DbPrintf( "%4.f %6.f %6.f %6.3f %6.f %6.f %6.f\n", + tDbBin, outBin, rs_capHt, rs_effHt, ehBin, RHBin, inpBin); + } + + double inpTotWh = inpTot/3.413; + float HSPF = outTot / inpTotWh; + + if (bDoPrint) + DbPrintf( "\noutTot=%0.f inpTot=%0.f inpTotWh=%0.f HSPF=%0.3f\n", + outTot, inpTot, inpTotWh, HSPF); + + rs_ASHPLockOutT = lockOutTSave; // restore lockout input + + return HSPF; + +} // RSYS::rs_HSPFCheckASHP //------------------------------------------------------------------------------- -/*static*/ float RSYS::rs_DHR( // AHRI nominal DHR - float capHt) // heating capacity, Btuh +/*static*/ float RSYS::rs_DHR( // AHRI nominal DHR + float capHt) // heating capacity, Btuh // rounds capacity per AHRI procedures, see AHRI 210/240 2008 section 4.2 ish // used *only* for checking ratings etc (not in simulation) // returns rounded DHR, Btuh { -#if 1 // creative extension to handle small capacities, 10-16-2014 - float roCap = capHt <= 5000.f ? 1000.f - : capHt <= 40000.f ? 5000.f +#if 1 // creative extension to handle small capacities, 10-16-2014 + float roCap = capHt <= 5000.f ? 1000.f + : capHt <= 40000.f ? 5000.f : capHt <= 110000.f ? 10000.f - : 20000.f; - float DHR = max(floor(0.5f + capHt / roCap) * roCap, 1000.f); + : 20000.f; + float DHR = max( floor( 0.5f + capHt / roCap) * roCap, 1000.f); #else - x float roCap = capHt <= 40000.f ? 5000.f x - : capHt <= 110000.f ? 10000.f x - : 20000.f; - x float DHR = max(floor(0.5f + capHt / roCap) * roCap, 5000.f); +x float roCap = capHt <= 40000.f ? 5000.f +x : capHt <= 110000.f ? 10000.f +x : 20000.f; +x float DHR = max( floor( 0.5f + capHt / roCap) * roCap, 5000.f); #endif - return DHR; -} // float RSYS_rsDHR + return DHR; +} // float RSYS_rsDHR //------------------------------------------------------------------------------- -/*static*/ double RSYS::rs_CallCalcCapHt(void *pO, double &cap47) +/*static*/ double RSYS::rs_CallCalcCapHt( void* pO, double& cap47) // callback function for secant() // resizes per cap47 // calcs capacity (including any defrost) at rs_tdbOut // returns capHt, Btuh { - RSYS *pRS = (RSYS *)pO; - double capHt = pRS->rs_CapHtForCap47(cap47); - return capHt; -} // RSYS::rs_CallCalcHSPF + RSYS* pRS = (RSYS *)pO; + double capHt = pRS->rs_CapHtForCap47( cap47); + return capHt; +} // RSYS::rs_CallCalcHSPF //------------------------------------------------------------------------------- -double RSYS::rs_CapHtForCap47( // inner function re ASHP sizing - float cap47) // proposed cap47, Btuh +double RSYS::rs_CapHtForCap47( // inner function re ASHP sizing + float cap47) // proposed cap47, Btuh // note: resizes ASHP, alters many RSYS values // returns total heating output at rs_tdbOut, Btuh { - rs_cap47 = cap47; - rs_SetupCapH(-1.f, 1); // set derived values from rs_cap47 - // calls rs_SetupASHP() -> sets cap17 and cap35 - // sets rs_amfH, rs_fanHeatH - int ashpModel = 0; - float capHt, inpHt, capDfHt, capHtMin, inpHtMin, capDfHtMin; - rs_PerfASHP2(ashpModel, rs_tdbOut, rs_fanHRtdH, capHt, inpHt, capDfHt, - capHtMin, inpHtMin, capDfHtMin); - return capHt + rs_fanHeatH; -} // RSYS::rs_CapHtForCap47 + rs_cap47 = cap47; + rs_SetupCapH( -1.f, 1); // set derived values from rs_cap47 + // calls rs_SetupASHP() -> sets cap17 and cap35 + // sets rs_amfH, rs_fanHeatH + int ashpModel = 0; + float capHt, inpHt, capDfHt, capHtMin, inpHtMin, capDfHtMin; + rs_PerfASHP2(ashpModel, rs_tdbOut, rs_fanHRtdH, + capHt, inpHt, capDfHt, capHtMin, inpHtMin, capDfHtMin); + return capHt + rs_fanHeatH; +} // RSYS::rs_CapHtForCap47 //------------------------------------------------------------------------------- -RC RSYS::rs_SizeHtASHP( // size ASHP - float dsnLoad, // required output, Btuh - float tdbOut) // outdoor dry-bulb temp, F +RC RSYS::rs_SizeHtASHP( // size ASHP + float dsnLoad, // required output, Btuh + float tdbOut) // outdoor dry-bulb temp, F // sets rs_cap47 (and dependent members) such that // heating capacity at tdbOut matches design load -{ - rs_tdbOut = tdbOut; - RC rc = RCOK; - - // calculate estimated cap47 ignoring defrost and fan power - // use standard straight line fits solved for cap47 - float r17 = min(rs_CapRat1747(), 1.f); // cap17/cap47 - float tdbM17 = tdbOut - 17.f; - float tF; - float d; - if (rs_HasDefrost() && tdbOut > 17.f && - tdbOut < 45.f) { // defrost regime: cap based on 17 - 35 slope - // pkg ASHP does not defrost - tF = tdbM17 / (35.f - 17.f); - float r35 = 0.9f * (r17 + 0.6f * (1.f - r17)); // cap35/cap47 - d = (r17 + tF * (r35 - r17)); - } else { // non-defrost: cap based on 17 - 47 slope - tF = tdbM17 / (47.f - 17.f); - d = (r17 + tF * (1.f - r17)); - } - float cap47Est = dsnLoad / d; - - // finalize cap47 result using secant() - // fan power and defrost included - double cap47 = cap47Est; - double f1 = DBL_MIN; - int ret = secant(rs_CallCalcCapHt, this, dsnLoad, 20.f, cap47, f1, // x1, f1 - cap47 + 100.); // x2 (f2) - if (ret != 0) { - rc = err(ERR, "RSYS '%s': Cap47 for design load fail (return code=%d)", - Name(), ret); - cap47 = cap47Est; - } - rs_cap47 = cap47; // redundant? - - rs_DefaultCapNomsIf(); // update nominal capacities (no calc effect) - - return rc; - -} // RSYS::rs_SizeHtASHP +{ + rs_tdbOut = tdbOut; + RC rc = RCOK; + + // calculate estimated cap47 ignoring defrost and fan power + // use standard straight line fits solved for cap47 + float r17 = min( rs_CapRat1747(), 1.f); // cap17/cap47 + float tdbM17 = tdbOut - 17.f; + float tF; + float d; + if (rs_HasDefrost() && tdbOut > 17.f && tdbOut < 45.f) + { // defrost regime: cap based on 17 - 35 slope + // pkg ASHP does not defrost + tF = tdbM17 / (35.f - 17.f); + float r35 = 0.9f * (r17 + 0.6f*(1.f - r17)); // cap35/cap47 + d = (r17 + tF * (r35 - r17)); + } + else + { // non-defrost: cap based on 17 - 47 slope + tF = tdbM17 / (47.f - 17.f); + d = (r17 + tF * (1.f - r17)); + } + float cap47Est = dsnLoad / d; + + // finalize cap47 result using secant() + // fan power and defrost included + double cap47 = cap47Est; + double f1 = DBL_MIN; + int ret = secant( rs_CallCalcCapHt, this, dsnLoad, 20.f, + cap47, f1, // x1, f1 + cap47+100.); // x2 (f2) + if (ret != 0) + { rc = err( ERR, "RSYS '%s': Cap47 for design load fail (return code=%d)", Name(), ret); + cap47 = cap47Est; + } + rs_cap47 = cap47; // redundant? + + rs_DefaultCapNomsIf(); // update nominal capacities (no calc effect) + + return rc; + +} // RSYS::rs_SizeHtASHP //------------------------------------------------------------------------------- -void RSYS::rs_DefaultCapNomsIf() // nominal capacities +void RSYS::rs_DefaultCapNomsIf() // nominal capacities // update nominal heating and cooling capacities // sets rs_capNomH and rs_capNomC if not fixed by user input // rs_capNomH and rs_capNomC are reporting only { - // uses non-NaN values if available, else arbitrary default - // autosized rs_capXXX are initially NaN - // does NOT set FsVAL field status, tricky to know when values are final. +// uses non-NaN values if available, else arbitrary default +// autosized rs_capXXX are initially NaN +// does NOT set FsVAL field status, tricky to know when values are final. - if (!IsVal(RSYS_CAPNOMH)) - rs_capNomH = - rs_IsASHP() ? ifNotNaN(rs_cap47, 18000.f) : ifNotNaN(rs_capH, 18000.f); + if (!IsVal( RSYS_CAPNOMH)) + rs_capNomH = rs_IsASHP() + ? ifNotNaN( rs_cap47, 18000.f) + : ifNotNaN( rs_capH, 18000.f); - if (!IsVal(RSYS_CAPNOMC)) - rs_capNomC = ifNotNaN(rs_cap95, 18000.f); + if (!IsVal( RSYS_CAPNOMC)) + rs_capNomC = ifNotNaN( rs_cap95, 18000.f); -} // RSYS::rs_DefaultCapNomsIf() +} // RSYS::rs_DefaultCapNomsIf() //------------------------------------------------------------------------------- -void RSYS::rs_SetModeAndSpeedF( // set mode / clear prior-step results - int rsModeNew, // new mode - float speedF /*=1.f*/) // new speed +void RSYS::rs_SetModeAndSpeedF( // set mode / clear prior-step results + int rsModeNew, // new mode + float speedF /*=1.f*/) // new speed // uses rs_mode to skip unneeded { -#if defined(_DEBUG) - if (speedF < 0.f || speedF > 1.f) - printf("\nrs_SetModeAndClear() speedF=%0.2f", speedF); +#if defined( _DEBUG) + if (speedF < 0.f || speedF > 1.f) + printf("\nrs_SetModeAndClear() speedF=%0.2f", speedF); #endif - rs_speedF = speedF; - rs_mode = rsModeNew; -} // RSYS::rs_SetModeAndSpeedF + rs_speedF = speedF; + rs_mode = rsModeNew; +} // RSYS::rs_SetModeAndSpeedF //----------------------------------------------------------------------------- void RSYS::rs_ClearSubhrResults( - int options /*= 0*/) // option bits - // all 0: full clear (e.g. subhr beg) - // 1: limited clear for speed retry -{ - rs_amf = 0.; - rs_amfReq[0] = rs_amfReq[1] = 0.; - rs_znLoad[0] = rs_znLoad[1] = 0.; - rs_asRet.as_Init(); - rs_asIn.as_Init(); - rs_twbIn = 0.; - rs_asOut.as_Init(); - rs_asSup.as_Init(); - rs_asOutAux.as_Init(); - rs_asSupAux.as_Init(); - - if (options & 1) - return; - - // all modes - rs_loadF = rs_PLR = rs_runF = rs_speedF = rs_runFAux = rs_PLF = - rs_capSenNetFS = 0.f; - rs_outSen = rs_outLat = rs_outFan = rs_outAux = rs_outDefrost = rs_outSenTot = - rs_inPrimary = rs_inFan = rs_inAux = rs_inDefrost = 0.; - - // heating - rs_effHt = rs_COPHtAdj = rs_tCoilEW = 0.f; - rs_capHt = rs_capHtMin = rs_capDfHt = rs_capDfHtMin = 0.f; - - // cooling - rs_tdbCoilIn = 0.f; - rs_twbCoilIn = 0.f; - rs_SHR = 0.f; - rs_fCondCap = 0.f; - rs_capTotCt = rs_capSenCt = rs_capLatCt = 0.f; - rs_fCondSEER = rs_fCondEER = 0.f; - rs_SEERnf = rs_EERnf = rs_EERt = rs_effCt = 0.f; - -} // RSYS::rs_ClearSubhrResults + int options /*= 0*/) // option bits + // all 0: full clear (e.g. subhr beg) + // 1: limited clear for speed retry +{ + rs_amf = 0.; + rs_amfReq[0] = rs_amfReq[1] = 0.; + rs_znLoad[0] = rs_znLoad[1] = 0.; + rs_asRet.as_Init(); + rs_asIn.as_Init(); + rs_twbIn = 0.; + rs_asOut.as_Init(); + rs_asSup.as_Init(); + rs_asOutAux.as_Init(); + rs_asSupAux.as_Init(); + + if (options & 1) + return; + + // all modes + rs_loadF = rs_PLR = rs_runF = rs_speedF = rs_runFAux = rs_PLF = rs_capSenNetFS = 0.f; + rs_outSen = rs_outLat = rs_outFan = rs_outAux = rs_outDefrost + = rs_outSenTot = rs_inPrimary = rs_inFan = rs_inAux = rs_inDefrost = 0.; + + // heating + rs_effHt = rs_COPHtAdj = rs_tCoilEW = 0.f; + rs_capHt = rs_capHtMin = rs_capDfHt = rs_capDfHtMin = 0.f; + + // cooling + rs_tdbCoilIn = 0.f; + rs_twbCoilIn = 0.f; + rs_SHR = 0.f; + rs_fCondCap = 0.f; + rs_capTotCt = rs_capSenCt = rs_capLatCt = 0.f; + rs_fCondSEER = rs_fCondEER = 0.f; + rs_SEERnf = rs_EERnf = rs_EERt = rs_effCt = 0.f; + +} // RSYS::rs_ClearSubhrResults //---------------------------------------------------------------------------- -int RSYS::rs_IsModeAvailable( // mode availability - int rsMode) const // desired mode (rsmHEAT, rsmCOOL, rsmOAV) +int RSYS::rs_IsModeAvailable( // mode availability + int rsMode) const // desired mode (rsmHEAT, rsmCOOL, rsmOAV) // returns 1 iff RSYS can operate in desired mode // 0 if not (mode fixed and different) // -1 if not (system off) { - int ret; - if (rs_mode != rsmOFF) - ret = rs_mode == rsMode; // RSYS mode already known - // OK iff desired mode is same - else { - switch (rs_modeCtrl) { - case C_RSYSMODECTRLCH_HEAT: - ret = rsMode == rsmHEAT && rs_CanHeat(); - break; - - case C_RSYSMODECTRLCH_COOL: - ret = rsMode == rsmCOOL && rs_CanCool(); - break; - - case C_RSYSMODECTRLCH_AUTO: - // control is auto and system is currently off - ret = (rsMode == rsmHEAT && rs_CanHeat()) || - (rsMode == rsmCOOL && rs_CanCool()) || - (rsMode == rsmOAV && rs_CanOAV()); - break; - - case C_RSYSMODECTRLCH_OFF: - default: - ret = -1; // off: no can do - } - } - - return ret; -} // RSYS::rs_IsModeAvailable + int ret; + if (rs_mode != rsmOFF) + ret = rs_mode == rsMode; // RSYS mode already known + // OK iff desired mode is same + else + { + switch (rs_modeCtrl) + { + case C_RSYSMODECTRLCH_HEAT: + ret = rsMode == rsmHEAT && rs_CanHeat(); + break; + + case C_RSYSMODECTRLCH_COOL: + ret = rsMode == rsmCOOL && rs_CanCool(); + break; + + case C_RSYSMODECTRLCH_AUTO: + // control is auto and system is currently off + ret = (rsMode == rsmHEAT && rs_CanHeat()) + || (rsMode == rsmCOOL && rs_CanCool()) + || (rsMode == rsmOAV && rs_CanOAV()); + break; + + case C_RSYSMODECTRLCH_OFF: + default: + ret = -1; // off: no can do + } + } + + return ret; +} // RSYS::rs_IsModeAvailable //---------------------------------------------------------------------------- -void RSYS::rs_EnteringAirState() // RSYS entering air state +void RSYS::rs_EnteringAirState() // RSYS entering air state // rs_mode s/b set by caller (and not rsmOFF) // rs_amf s/b set by caller and > 0 @@ -5447,263 +5479,272 @@ void RSYS::rs_EnteringAirState() // RSYS entering air state // rs_twbIn = corresponding wet bulb temp, F { - if (rs_mode == rsmOAV) - // outdoor air vent: set state from possibly modified outdoor conditions - // ignore possibility of impossible humidity ratio - rs_asRet.as_Set(rs_OAVTdbInlet, Top.wOSh); - - else { - ZNR *zp; - AIRFLOW afRet; // air flow at return duct inlet - // magically mixed from all zones - // served by this system - RLUPC(ZrB, zp, rs_IsZoneServed(zp)) { // determine return air state - // mix zone states weighted by prior flow - // use area as proxy if prior flow not known - double amfX = rs_mode == rs_modeLs ? zp->zn_rsAmfRetLs : zp->i.znArea; - // return grille temp = zone air temp from last step - afRet.af_AccumDry(amfX, zp->tzls, zp->wzls); -#if defined(_DEBUG) - if ((afRet.as_tdb < 30. || afRet.as_tdb > 110.f) && !rs_IsAutoSizing()) - orWarn("dubious return temp (%.1f F)", afRet.as_tdb); + if (rs_mode == rsmOAV) + // outdoor air vent: set state from possibly modified outdoor conditions + // ignore possibility of impossible humidity ratio + rs_asRet.as_Set( rs_OAVTdbInlet, Top.wOSh); + + else + { ZNR* zp; + AIRFLOW afRet; // air flow at return duct inlet + // magically mixed from all zones + // served by this system + RLUPC( ZrB, zp, rs_IsZoneServed( zp)) + { // determine return air state + // mix zone states weighted by prior flow + // use area as proxy if prior flow not known + double amfX = rs_mode == rs_modeLs + ? zp->zn_rsAmfRetLs + : zp->i.znArea; + // return grille temp = zone air temp from last step + afRet.af_AccumDry( amfX, zp->tzls, zp->wzls); +#if defined( _DEBUG) + if ((afRet.as_tdb < 30. || afRet.as_tdb > 110.f) && !rs_IsAutoSizing()) + orWarn("dubious return temp (%.1f F)", afRet.as_tdb); #endif - } - rs_asRet.as_Set(afRet); // state = flow w/o amf - } + } + rs_asRet.as_Set( afRet); // state = flow w/o amf + } - // adjust state for return duct - int iDS = rs_Dsi(1); // idx of ductseg - rs_asIn = iDS > 0 ? DsR[iDS].ds_CalcFL(rs_asRet, rs_amf) : rs_asRet; + // adjust state for return duct + int iDS = rs_Dsi( 1); // idx of ductseg + rs_asIn = iDS > 0 + ? DsR[ iDS].ds_CalcFL( rs_asRet, rs_amf) + : rs_asRet; -#if defined(_DEBUG) - if (rs_mode != rsmOAV && (rs_asIn.as_tdb < 30. || rs_asIn.as_tdb > 110.f)) { - if (!Top.isWarmup) - orWarn("Dubious asIn tdb (%0.2f)", rs_asIn.as_tdb); - // repeat return duct calc (debug aid) - // int iDS = rs_Dsi( 1); above - if (iDS > 0) - rs_asIn = DsR[iDS].ds_CalcFL(rs_asRet, rs_amf); - } -#endif - - if (IsSet(RSYS_RHINTEST)) { - rs_asIn.as_SetWFromRh( - rs_rhInTest); // testing aid: set w from rh (tdb unchanged) - rs_rhIn = rs_rhInTest; - } else - rs_rhIn = rs_asIn.as_RelHum(); // else calc rh - rs_twbIn = rs_asIn.as_Twb(); - -} // RSYS::rs_EnteringAirState + +#if defined( _DEBUG) + if (rs_mode != rsmOAV + && (rs_asIn.as_tdb < 30. || rs_asIn.as_tdb > 110.f)) + { if (!Top.isWarmup) + orWarn( "Dubious asIn tdb (%0.2f)", rs_asIn.as_tdb); + // repeat return duct calc (debug aid) + // int iDS = rs_Dsi( 1); above + if (iDS > 0) + rs_asIn = DsR[ iDS].ds_CalcFL( rs_asRet, rs_amf); + } +#endif + + if (IsSet( RSYS_RHINTEST)) + { rs_asIn.as_SetWFromRh( rs_rhInTest); // testing aid: set w from rh (tdb unchanged) + rs_rhIn = rs_rhInTest; + } + else + rs_rhIn = rs_asIn.as_RelHum(); // else calc rh + rs_twbIn = rs_asIn.as_Twb(); + +} // RSYS::rs_EnteringAirState //---------------------------------------------------------------------------- -int RSYS::rs_SupplyAirState( // current conditioning capabilities - int rsMode, // desired mode (do not call with rsMode == rsmOFF) - float speedF /*=1.f*/) // speed fraction - // sets rs_speedF to max( speedF, min +int RSYS::rs_SupplyAirState( // current conditioning capabilities + int rsMode, // desired mode (do not call with rsMode == rsmOFF) + float speedF /*=1.f*/) // speed fraction + // sets rs_speedF to max( speedF, min // NOTE: many side effects -- changes rs_mode, clears prior step, ... // returns 3: mode is available, rs_asSup set // 2: mode is being actively autosized, rs_asSup set -// 1: mode is inactive (another mode is being actively -// autosized) 0: mode not available -{ - // TODO: not returning right value! - - float speedFWas = rs_speedF; - - // mode availability - rs_amf = 0.; - if (rs_IsModeAvailable(rsMode) <= 0) - return 0; - - int rsAvail = rs_SetAmf(rsMode, speedF); - if (rsAvail < 2) - return rsAvail; - - if (rs_mode == rsmOFF || - speedF != speedFWas) // if was off or speed has changed - { // first zone requesting this mode during this step - rs_SetModeAndSpeedF(rsMode, speedF); - - // determine entering air state - // does return duct calcs, uses rs_amf - rs_EnteringAirState(); - - rs_asOut = rs_asIn; // init entering state - - int auszMode = rs_IsAutoSizing(); - if (rs_mode == rsmCOOL) { - ++rs_calcCount[1]; - rs_CoolingOutletAirState(auszMode); - } else if (rs_mode == rsmHEAT) { - ++rs_calcCount[0]; - // rs_asOut = rs_asIn; // above - rs_HeatingOutletAirState(auszMode); - } else { // OAV: add fan heat - // rs_asOut = rs_asIn; // above - rs_asOut.as_AddQSen2(rs_fanHeatOAV, rs_amf); - } - - // outlet air state rs_asOut now known for all cases - // calc supply air state at zone(s) - rs_asSup = rs_asOut; - rs_SupplyDSEAndDucts(rs_asSup); - } - // else supply air state known - - return rsAvail; -} // RSYS::rs_SupplyAirState +// 1: mode is inactive (another mode is being actively autosized) +// 0: mode not available +{ + // TODO: not returning right value! + + float speedFWas = rs_speedF; + + // mode availability + rs_amf = 0.; + if (rs_IsModeAvailable(rsMode) <= 0) + return 0; + + int rsAvail = rs_SetAmf(rsMode, speedF); + if (rsAvail < 2) + return rsAvail; + + if (rs_mode == rsmOFF || speedF != speedFWas) // if was off or speed has changed + { // first zone requesting this mode during this step + rs_SetModeAndSpeedF(rsMode, speedF); + + // determine entering air state + // does return duct calcs, uses rs_amf + rs_EnteringAirState(); + + rs_asOut = rs_asIn; // init entering state + + int auszMode = rs_IsAutoSizing(); + if (rs_mode == rsmCOOL) + { ++rs_calcCount[1]; + rs_CoolingOutletAirState(auszMode); + } + else if (rs_mode == rsmHEAT) + { ++rs_calcCount[0]; + // rs_asOut = rs_asIn; // above + rs_HeatingOutletAirState( auszMode); + } + else + { // OAV: add fan heat + // rs_asOut = rs_asIn; // above + rs_asOut.as_AddQSen2( rs_fanHeatOAV, rs_amf); + } + + // outlet air state rs_asOut now known for all cases + // calc supply air state at zone(s) + rs_asSup = rs_asOut; + rs_SupplyDSEAndDucts(rs_asSup); + + } + // else supply air state known + + return rsAvail; +} // RSYS::rs_SupplyAirState //---------------------------------------------------------------------------- -float RSYS::rs_GetAmfFullSpeed(int rsMode) const { - float amf; - switch (rsMode) { - case rsmHEAT: - amf = rs_amfH; - break; - case rsmCOOL: - amf = rs_amfC; - break; - case rsmOAV: - amf = rs_amfOAV; - break; - default: - amf = 0.; - } - return amf; -} // RSYS::rs_GetAmfFullSpeed +float RSYS::rs_GetAmfFullSpeed( int rsMode) const +{ + float amf; + switch (rsMode) + { + case rsmHEAT: amf = rs_amfH; break; + case rsmCOOL: amf = rs_amfC; break; + case rsmOAV: amf = rs_amfOAV; break; + default: amf = 0.; + } + return amf; +} // RSYS::rs_GetAmfFullSpeed //------------------------------------------------------------------------------ -int RSYS::rs_SetAmf( // set rs_amf - int rsMode, float speedF) -// returns 3: mode is available, rs_amf set per speedF -// 2: mode is being actively autosized, rs_amf = full speed -// 1: mode is inactive (another mode is being actively -// autosized) 0: no amf available, rs_amf = 0 -{ - // set mode-specific full speed air flow - // must be set even if mode not changed - // due to possible speedF modifications - rs_amf = rs_GetAmfFullSpeed(rsMode); - if (rs_amf < .0001) { - rs_amf = 0.; - return 0; - } - - int rsAvail = 3; - int auszMode = rs_IsAutoSizing(); - if (auszMode != rsmOFF) { // autosizing underway - if (auszMode != rsMode) - return 1; // another mode is being autosized - rsAvail = 2; // current mode now being autosized - } - - if (rsMode != rsmHEAT || !rs_IsCHDHW()) - rs_amf *= speedF; // assume air flow scales with capacity - else { - if (rs_capHt == 0.f) - rs_CurCapHtCHDHW(); - float avf; - float fanPwr; // unused - rs_pCHDHW->hvt_BlowerAVFandPower(speedF * rs_capHt, avf, fanPwr); - rs_amf = AVFtoAMF(avf); +int RSYS::rs_SetAmf( // set rs_amf + int rsMode, + float speedF) + // returns 3: mode is available, rs_amf set per speedF + // 2: mode is being actively autosized, rs_amf = full speed + // 1: mode is inactive (another mode is being actively autosized) + // 0: no amf available, rs_amf = 0 +{ + // set mode-specific full speed air flow + // must be set even if mode not changed + // due to possible speedF modifications + rs_amf = rs_GetAmfFullSpeed(rsMode); + if (rs_amf < .0001) + { rs_amf = 0.; + return 0; + } + + int rsAvail = 3; + int auszMode = rs_IsAutoSizing(); + if (auszMode != rsmOFF) + { // autosizing underway + if (auszMode != rsMode) + return 1; // another mode is being autosized + rsAvail = 2; // current mode now being autosized + } + + if (rsMode != rsmHEAT || !rs_IsCHDHW()) + rs_amf *= speedF; // assume air flow scales with capacity + else + { if (rs_capHt == 0.f) + rs_CurCapHtCHDHW(); + float avf; + float fanPwr; // unused + rs_pCHDHW->hvt_BlowerAVFandPower(speedF * rs_capHt, avf, fanPwr); + rs_amf = AVFtoAMF(avf); #if 0 if (rs_capHt < 34000.) printf("\nLow %.0f speedF=%0.3f avf=%0.f", rs_capHt, speedF, avf); #endif - } - return rsAvail; + } + return rsAvail; -} // RSYS::rs_SetAmf +} // RSYS::rs_SetAmf //---------------------------------------------------------------------------- -float RSYS::rs_SupplyDSEAndDucts( // apply supply duct and DSE losses - AIRSTATE &as, // air state - // call: state at RSYS outlet - // return: state at room register - float amf /*=-1*/) // dry air mass flow rate, lbm/hr - // default: rs_amf +float RSYS::rs_SupplyDSEAndDucts( // apply supply duct and DSE losses + AIRSTATE& as, // air state + // call: state at RSYS outlet + // return: state at room register + float amf /*=-1*/) // dry air mass flow rate, lbm/hr + // default: rs_amf // returns room register air mass flow rate, lbm/hr (after leakage if any) { - if (amf < 0.f) - amf = rs_amf; - - // DSE: discard (1-DSE) of heat/moisture added by system - int iHC = rs_DsHC(); // returns nz iff cooling - float DSE = iHC ? rs_DSEC : rs_DSEH; - if (DSE > 0.f) { - if (DSE < 1.f) - as.as_MixF(1.f - DSE, rs_asIn); - // else leave as unchanged (no duct losses) - } else { // supply duct (if any) to get supply state - int iDS = rs_Dsi(0, iHC); // retrieve supply duct idx - if (iDS > 0) // if supply duct present - { - as = DsR[iDS].ds_CalcFL(as, amf); - amf *= (1.f - DsR[iDS].ds_leakF); - } - } - return amf; -} // RSYS_SupplyDSEAndDucts + if (amf < 0.f) + amf = rs_amf; + + // DSE: discard (1-DSE) of heat/moisture added by system + int iHC = rs_DsHC(); // returns nz iff cooling + float DSE = iHC ? rs_DSEC : rs_DSEH; + if (DSE > 0.f) + { if (DSE < 1.f) + as.as_MixF( 1.f - DSE, rs_asIn); + // else leave as unchanged (no duct losses) + } + else + { // supply duct (if any) to get supply state + int iDS = rs_Dsi( 0, iHC); // retrieve supply duct idx + if (iDS > 0) // if supply duct present + { as = DsR[iDS].ds_CalcFL(as, amf); + amf *= (1.f - DsR[iDS].ds_leakF); + } + } + return amf; +} // RSYS_SupplyDSEAndDucts //---------------------------------------------------------------------------- -void RSYS::rs_SupplyDSEAndDuctsRev( // reverse supply duct and DSE losses - AIRSTATE &as) // air state - // call: state at room register - // return: state at RSYS outlet -{ - int iHC = rs_DsHC(); // returns nz iff cooling - float DSE = iHC ? rs_DSEC : rs_DSEH; - if (DSE > 0.f) { // distribution system efficiency - // 1-DSE is discarded - if (DSE < 1.f) - as.as_MixF(-(1.f - DSE) / DSE, rs_asIn); - // else leave as unchanged (no duct losses) - } else { // supply duct (if any) - int iDS = rs_Dsi(0); - if (iDS > 0) // if supply duct present - { - AIRSTATE asSup(as); - as = DsR[iDS].ds_CalcFLRev(asSup); - } - } -} // RSYS_SupplyDSEAndDuctsRev +void RSYS::rs_SupplyDSEAndDuctsRev( // reverse supply duct and DSE losses + AIRSTATE& as) // air state + // call: state at room register + // return: state at RSYS outlet +{ + int iHC = rs_DsHC(); // returns nz iff cooling + float DSE = iHC ? rs_DSEC : rs_DSEH; + if (DSE > 0.f) + { // distribution system efficiency + // 1-DSE is discarded + if (DSE < 1.f) + as.as_MixF( -(1.f - DSE)/DSE, rs_asIn); + // else leave as unchanged (no duct losses) + } + else + { // supply duct (if any) + int iDS = rs_Dsi( 0); + if (iDS > 0) // if supply duct present + { AIRSTATE asSup( as); + as = DsR[ iDS].ds_CalcFLRev( asSup); + } + } +} // RSYS_SupplyDSEAndDuctsRev //---------------------------------------------------------------------------- -double RSYS::rs_ZoneAirRequest( // air quantity needed by zone - double - znSupReq, // dry-air mass flow rate required at supply register, lbm/hr - int iAux /*=0*/) // 0 = primary mode (compressor) - // 1 = aux mode (aux alone or main+aux (ASHP heating only) +double RSYS::rs_ZoneAirRequest( // air quantity needed by zone + double znSupReq, // dry-air mass flow rate required at supply register, lbm/hr + int iAux /*=0*/) // 0 = primary mode (compressor) + // 1 = aux mode (aux alone or main+aux (ASHP heating only) // each zone needing conditioning requests air // returns RSYS (not supply register) amf needed to provide zone requirement { - // handle impossible requests - // supplyDT = tz - tSup - if (znSupReq > 1.e10) // znSupReq = DBL_MAX if supplyDT is tiny - znSupReq = rs_amf; - else if (znSupReq < 0.) - // reverse flow - // caused by "flipped" supply DT - // due to e.g. big duct losses - znSupReq = -#if 1 // attempt to fix small load sizing, 7-16-2021 - 2. * rs_amf; // request "a lot" + // handle impossible requests + // supplyDT = tz - tSup + if (znSupReq > 1.e10) // znSupReq = DBL_MAX if supplyDT is tiny + znSupReq = rs_amf; + else if (znSupReq < 0.) + // reverse flow + // caused by "flipped" supply DT + // due to e.g. big duct losses + znSupReq = +#if 1 // attempt to fix small load sizing, 7-16-2021 + 2. * rs_amf; // request "a lot" #else -#if 1 // unknown date - Top.tp_autoSizing +#if 1 // unknown date + Top.tp_autoSizing #else - Top.tp_pass1A + Top.tp_pass1A #endif - ? 1. // autosize warmup: ignore - : 2. * rs_amf; // simulation: request "a lot" + ? 1. // autosize warmup: ignore + : 2. * rs_amf; // simulation: request "a lot" #endif - // air required at system - double znAmfSys = znSupReq / rs_ducts[rs_DsHC()].ductLkXF[0]; - rs_amfReq[iAux] += znAmfSys; // all-zone total at system + // air required at system + double znAmfSys = znSupReq / rs_ducts[ rs_DsHC()].ductLkXF[ 0]; + rs_amfReq[ iAux] += znAmfSys; // all-zone total at system - // load at system, Btuh - rs_znLoad[iAux] += - (iAux ? rs_asOutAux : rs_asOut).as_QSenDiff2(rs_asIn, znAmfSys); + // load at system, Btuh + rs_znLoad[ iAux] += + (iAux ? rs_asOutAux : rs_asOut).as_QSenDiff2(rs_asIn, znAmfSys); -#if 0 && defined(_DEBUG) +#if 0 && defined( _DEBUG) if (iAux == 1) { const char* msg = fabs(rs_znLoad[0] - rs_znLoad[1]) > 1.f @@ -5718,457 +5759,466 @@ double RSYS::rs_ZoneAirRequest( // air quantity needed by zone rs_amfReq[ iAux], iAux); #endif #endif - return znAmfSys; -} // RSYS::rs_ZoneAirRequest + return znAmfSys; +} // RSYS::rs_ZoneAirRequest //----------------------------------------------------------------------------- -#if defined(_DEBUG) +#if defined( _DEBUG) #define RSYSITERCOUNT -#if defined(RSYSITERCOUNT) +#if defined( RSYSITERCOUNT) // dev aid statistic re minimizing aux heating iterations -int rsysPartAuxCount = 0; // # of part-load auxiliary substeps -int rsysIterCount = 0; // count secant method call-backs +int rsysPartAuxCount = 0; // # of part-load auxiliary substeps +int rsysIterCount = 0; // count secant method call-backs #endif #endif //----------------------------------------------------------------------------- +RC RSYS::rs_AllocateZoneAir() // finalize zone air flows +// +{ + if (rs_mode == rsmOAV) + return RCOK; // OAV: allocation done by rs_OAVAttempt -double formerLambda(void *pO, double &tSup) { - double amf = ((RSYS *)pO)->rs_AmfRequired(tSup); - return amf != 0. ? 1. / amf : 1.e10; -} +#if defined( _DEBUG) + if (rs_speedF < 0.f || rs_speedF > 1.f) + printf("\nrs_speedF = %0.f", rs_speedF); +#endif + RC rc = RCOK; -RC RSYS::rs_AllocateZoneAir() // finalize zone air flows -// -{ - if (rs_mode == rsmOAV) - return RCOK; // OAV: allocation done by rs_OAVAttempt + ASSERT(rs_fxCap[0] == 0.f && rs_fxCap[1] == 0.f); + if (rs_amfReq[ 0] > 0.) // if any air requested (by any zone) + rs_fxCap[ 0] = rs_amf / rs_amfReq[ 0]; // >1 = excess capacity + // else rs_fxCap = 0 + double fSize = min( rs_fxCap[ 0], 1.); // limit to available + + // bAux: aux possible and needed + // note: rs_capAuxH>0 possible for ASHP *only* (but can be =0 for ASHP) + bool bAux = rs_mode == rsmHEAT && rs_capAuxH > 0.f + && (rs_effHt == 0.f || (fSize > 0. && fSize < 1.)); + + if (bAux && rs_ctrlAuxH != C_AUXHEATCTRL_CYCLE) + { // check that aux will be helpful + // _CYCLE: any rs_capAuxH > 0 helps (> 0.f check is above) + // _LO, _ALT: aux supply temp must be greater than primary + if (rs_asSupAux.as_tdb <= rs_asSup.as_tdb) + { orWarn("Aux heat supply temperature (%0.1f F) <= primary supply temperature (%0.1f F)." + "\n Aux heat is not helpful. rscapAuxH should be increased.", + rs_asSupAux.as_tdb, rs_asSup.as_tdb); + // bAux = false; no: real controls would run aux even if not helpful + } + } -#if defined(_DEBUG) - if (rs_speedF < 0.f || rs_speedF > 1.f) - printf("\nrs_speedF = %0.f", rs_speedF); -#endif - RC rc = RCOK; - - ASSERT(rs_fxCap[0] == 0.f && rs_fxCap[1] == 0.f); - if (rs_amfReq[0] > 0.) // if any air requested (by any zone) - rs_fxCap[0] = rs_amf / rs_amfReq[0]; // >1 = excess capacity - // else rs_fxCap = 0 - double fSize = min(rs_fxCap[0], 1.); // limit to available - - // bAux: aux possible and needed - // note: rs_capAuxH>0 possible for ASHP *only* (but can be =0 for ASHP) - bool bAux = rs_mode == rsmHEAT && rs_capAuxH > 0.f && - (rs_effHt == 0.f || (fSize > 0. && fSize < 1.)); - - if (bAux && - rs_ctrlAuxH != C_AUXHEATCTRL_CYCLE) { // check that aux will be helpful - // _CYCLE: any rs_capAuxH > 0 helps (> 0.f check is above) - // _LO, _ALT: aux supply temp must be greater than primary - if (rs_asSupAux.as_tdb <= rs_asSup.as_tdb) { - orWarn("Aux heat supply temperature (%0.1f F) <= primary supply " - "temperature (%0.1f F)." - "\n Aux heat is not helpful. rscapAuxH should be increased.", - rs_asSupAux.as_tdb, rs_asSup.as_tdb); - // bAux = false; no: real controls would run aux even if not helpful - } - } - - ZNR *zp; - if (!bAux) { // aux not available or not needed - if (rs_IsVCMode(rs_mode) && fSize == 1. && - !rs_IsAutoSizing()) { // variable capacity with excess capacity - // find intermediate speed + ZNR* zp; + if (!bAux) + { // aux not available or not needed + if (rs_IsVCMode( rs_mode) && fSize == 1. && !rs_IsAutoSizing()) + { // variable capacity with excess capacity + // find intermediate speed #if 0 float tSupUseful; bool bUseful = rs_IsSupplyAirTempUseful(rs_mode, rs_asSup.as_tdb, tSupUseful); if (!bUseful) printf("\nNot useful"); #endif - rc |= rs_FindRequiredSpeedF(); - // now rs_speedFMin <= rs_speedF < 1 - fSize = min(rs_fxCap[0], 1.); - } - RLUPC(ZrB, zp, rs_IsZoneServed(zp)) - zp->zn_SetRSYSAmf(fSize, 0); - } else if (rs_amfReq[1] > rs_amf) { // full aux insufficient - // allocate shortfall across zones - rs_fxCap[1] = rs_amf / rs_amfReq[1]; // x/0 impossible - - // finalize supply conditions - // recalc duct loss - // WHY: duct temps must be current re surrounding zone xfer - rs_asSup = rs_asOut = rs_asOutAux; - rs_SupplyDSEAndDucts(rs_asSup); -#if defined(_DEBUG) - // result s/b same as initial calc! - if (rs_asSup != rs_asSupAux) - printf("rs_asSupAux mismatch!\n"); -#endif - rs_runFAux = 1.; // aux is at full cap - if (rs_ctrlAuxH == C_AUXHEATCTRL_LO) - rs_effHt = 0.f; // force compressor off - // rs_speedF = 0.; // dicey: fan is at full speed - RLUPC(ZrB, zp, rs_IsZoneServed(zp)) - zp->zn_SetRSYSAmf(rs_fxCap[1], 1); // tell zones how much they get - } else if (rs_effHt == 0.f || - rs_ctrlAuxH == C_AUXHEATCTRL_LO) { // compressor is unavailable - // meet load with aux alone -#if 0 && defined(_DEBUG) + rc |= rs_FindRequiredSpeedF(); + // now rs_speedFMin <= rs_speedF < 1 + fSize = min(rs_fxCap[0], 1.); + } + RLUPC(ZrB, zp, rs_IsZoneServed(zp)) + zp->zn_SetRSYSAmf(fSize, 0); + } + else if (rs_amfReq[ 1] > rs_amf) + { // full aux insufficient + // allocate shortfall across zones + rs_fxCap[ 1] = rs_amf / rs_amfReq[ 1]; // x/0 impossible + + // finalize supply conditions + // recalc duct loss + // WHY: duct temps must be current re surrounding zone xfer + rs_asSup = rs_asOut = rs_asOutAux; + rs_SupplyDSEAndDucts( rs_asSup); +#if defined( _DEBUG) + // result s/b same as initial calc! + if (rs_asSup != rs_asSupAux) + printf( "rs_asSupAux mismatch!\n"); +#endif + rs_runFAux = 1.; // aux is at full cap + if (rs_ctrlAuxH == C_AUXHEATCTRL_LO) + rs_effHt = 0.f; // force compressor off + // rs_speedF = 0.; // dicey: fan is at full speed + RLUPC( ZrB, zp, rs_IsZoneServed( zp)) + zp->zn_SetRSYSAmf( rs_fxCap[ 1], 1); // tell zones how much they get + } + else if (rs_effHt == 0.f || rs_ctrlAuxH == C_AUXHEATCTRL_LO) + { // compressor is unavailable + // meet load with aux alone +#if 0 && defined( _DEBUG) if (Top.jDay == 336) printf("\nHit %s", Top.dateStr.CStr()); #endif - rs_effHt = 0.f; // force compressor off (for _LO case) - rs_fxCap[1] = rs_amf / rs_amfReq[1]; // x/0 impossible - rs_runFAux = 1.f / rs_fxCap[1]; -#if defined(_DEBUG) - if (ifBracket(0.f, rs_runFAux, 1.f)) - printf("rs_AllocateZoneAir: rs_runFAux > 1\n"); -#endif - - // finalize supply conditions - // recalc duct loss - // WHY: duct temps must be current re surrounding zone xfer - rs_asSup = rs_asOut = rs_asOutAux; - rs_SupplyDSEAndDucts(rs_asSup); -#if defined(_DEBUG) - // result s/b same as initial calc! - if (rs_asSup != rs_asSupAux) - printf("rs_asSupAux mismatch!\n"); -#endif - RLUPC(ZrB, zp, rs_IsZoneServed(zp)) - zp->zn_SetRSYSAmfFromTSup(); - } else { // partial aux required - // C_AUXHEATCTRL_CYCLE: full compressor + cycling aux - // C_AUXHEATCTRL_ALT: compressor / aux alternate - // find supply temp that satisfies all zones at full volume - // Use secant method to find inverse of 1/amf = f( tSup) - // 1/amf is linear-ish with tSup, reduces iterations -#if defined(RSYSITERCOUNT) - rsysPartAuxCount++; -#endif -#if 0 && defined(_DEBUG) + rs_effHt = 0.f; // force compressor off (for _LO case) + rs_fxCap[ 1] = rs_amf / rs_amfReq[ 1]; // x/0 impossible + rs_runFAux = 1.f / rs_fxCap[ 1]; +#if defined( _DEBUG) + if (ifBracket( 0.f, rs_runFAux, 1.f)) + printf( "rs_AllocateZoneAir: rs_runFAux > 1\n"); +#endif + + // finalize supply conditions + // recalc duct loss + // WHY: duct temps must be current re surrounding zone xfer + rs_asSup = rs_asOut = rs_asOutAux; + rs_SupplyDSEAndDucts( rs_asSup); +#if defined( _DEBUG) + // result s/b same as initial calc! + if (rs_asSup != rs_asSupAux) + printf( "rs_asSupAux mismatch!\n"); +#endif + RLUPC( ZrB, zp, rs_IsZoneServed( zp)) + zp->zn_SetRSYSAmfFromTSup(); + } + else + { // partial aux required + // C_AUXHEATCTRL_CYCLE: full compressor + cycling aux + // C_AUXHEATCTRL_ALT: compressor / aux alternate + // find supply temp that satisfies all zones at full volume + // Use secant method to find inverse of 1/amf = f( tSup) + // 1/amf is linear-ish with tSup, reduces iterations +#if defined( RSYSITERCOUNT) + rsysPartAuxCount++; +#endif +#if 0 && defined( _DEBUG) if (Top.jDay == 336) printf("\nHit %s", Top.dateStr.CStr()); #endif - double tSup = max(rs_asSup.as_tdb, rs_tSupLs); // use last result as guess - double tSup_prev = tSup; // retain previous value in case of failure - double amfX = DBL_MIN; - double amfXTarg = 1. / rs_amf; // f = target function value - int ret = secant( - [](void *pO, double &tSup) { // returns 1/reqAMF, 1.e10 iff reqAMF = 0 -#if defined(RSYSITERCOUNT) - rsysIterCount++; -#endif - double amf = ((RSYS *)pO)->rs_AmfRequired(tSup); - return amf != 0. ? 1. / amf : 1.e10; - }, - this, amfXTarg, .0001 * amfXTarg, tSup, amfX, // x1, f1 - rs_asSupAux.as_tdb, DBL_MIN); // x2, f2 - if (ret != 0) { - oWarn("ASHP aux heat supply temp fail; restoring previous value"); - tSup = tSup_prev; - } -#if defined(_DEBUG) - // check tSup -- should be between noAux and fullAux temps - double tSupX = bracket(rs_asSup.as_tdb, tSup, rs_asSupAux.as_tdb); - if (tSupX != tSup) { - printf("rs_AllocateZoneAir: tSup out of range\n"); - tSup = tSupX; - } -#endif - AIRSTATE asOutNoAux(rs_asOut); - rs_asSup.as_tdb = tSup; // required supply air state - rs_asOut = rs_asSup; - rs_SupplyDSEAndDuctsRev(rs_asOut); - -#if defined(_DEBUG) - // reverse calc, should match supply - AIRSTATE asSupX(rs_asOut); - rs_SupplyDSEAndDucts(asSupX); - if (asSupX != rs_asSup) - printf("rs_AllocateZoneAir: inconsistent air state\n"); -#endif - double qAux; - if (rs_ctrlAuxH == C_AUXHEATCTRL_ALT) { // aux alternates with primary - rs_runFAux = (rs_asOut.as_tdb - asOutNoAux.as_tdb) / - (rs_asOutAux.as_tdb - asOutNoAux.as_tdb); -#if defined(_DEBUG) - if (rs_runFAux < 0.f) - printf("\nrs_runFAux < 0"); - if (rs_runFAux > 1.f) - printf("\nrs_runFAux > 1"); + double tSup = max(rs_asSup.as_tdb, rs_tSupLs); // use last result as guess + double amfX = DBL_MIN; + double amfXTarg = 1. / rs_amf; // f = target function value + int ret = secant( + [](void* pO, double& tSup) + { // returns 1/reqAMF, 1.e10 iff reqAMF = 0 +#if defined( RSYSITERCOUNT) + rsysIterCount++; +#endif + double amf = ((RSYS*)pO)->rs_AmfRequired(tSup); + return amf != 0. ? 1. / amf : 1.e10; + }, + this, amfXTarg, .0001*amfXTarg, + tSup, amfX, // x1, f1 + rs_asSupAux.as_tdb, DBL_MIN); // x2, f2 + if (ret != 0) { + oWarn("ASHP aux heat supply temp fail; restoring previous value"); + tSup = rs_asSup.as_tdb; + } +#if defined( _DEBUG) + // check tSup -- should be between noAux and fullAux temps + double tSupX = bracket(rs_asSup.as_tdb, tSup, rs_asSupAux.as_tdb); + if (tSupX != tSup) + { + printf("rs_AllocateZoneAir: tSup out of range\n"); + tSup = tSupX; + } #endif - float fFan = rs_capAuxH / (rs_capAuxH + rs_fanHeatH); - qAux = rs_runFAux * rs_capAuxH * fFan; - } else { // aux cycles: determine added heat rqd in addition to prim - qAux = rs_asOut.as_QSenDiff2(asOutNoAux, rs_amf); + AIRSTATE asOutNoAux(rs_asOut); + rs_asSup.as_tdb = tSup; // required supply air state + rs_asOut = rs_asSup; + rs_SupplyDSEAndDuctsRev(rs_asOut); + +#if defined( _DEBUG) + // reverse calc, should match supply + AIRSTATE asSupX(rs_asOut); + rs_SupplyDSEAndDucts(asSupX); + if (asSupX != rs_asSup) + printf("rs_AllocateZoneAir: inconsistent air state\n"); +#endif + double qAux; + if (rs_ctrlAuxH == C_AUXHEATCTRL_ALT) + { // aux alternates with primary + rs_runFAux = (rs_asOut.as_tdb - asOutNoAux.as_tdb) / (rs_asOutAux.as_tdb - asOutNoAux.as_tdb); +#if defined( _DEBUG) + if (rs_runFAux < 0.f) + printf("\nrs_runFAux < 0"); + if (rs_runFAux > 1.f) + printf("\nrs_runFAux > 1"); +#endif + float fFan = rs_capAuxH / (rs_capAuxH + rs_fanHeatH); + qAux = rs_runFAux * rs_capAuxH * fFan; + } + else + { // aux cycles: determine added heat rqd in addition to prim + qAux = rs_asOut.as_QSenDiff2(asOutNoAux, rs_amf); - if (qAux < 0.) { -#if defined(_DEBUG) - if (qAux < -.1) - printf("rs_AllocateZoneAir: qAux < 0 (%.2f)\n", qAux); + if (qAux < 0.) + { +#if defined( _DEBUG) + if (qAux < -.1) + printf("rs_AllocateZoneAir: qAux < 0 (%.2f)\n", qAux); #endif - qAux = 0.; - } - rs_runFAux = qAux / rs_capAuxH; - } -#if defined(_DEBUG) - if (ifBracket(0.f, rs_runFAux, 1.f)) - printf("rs_AllocateZoneAir: rs_runFAux > 1\n"); + qAux = 0.; + } + rs_runFAux = qAux / rs_capAuxH; + } +#if defined( _DEBUG) + if (ifBracket( 0.f, rs_runFAux, 1.f)) + printf( "rs_AllocateZoneAir: rs_runFAux > 1\n"); #endif - RLUPC(ZrB, zp, rs_IsZoneServed(zp)) - zp->zn_SetRSYSAmfFromTSup(); - } // end partial aux + RLUPC( ZrB, zp, rs_IsZoneServed( zp)) + zp->zn_SetRSYSAmfFromTSup(); + } // end partial aux - return rc; -} // RSYS::rs_AllocateZoneAir + return rc; +} // RSYS::rs_AllocateZoneAir //---------------------------------------------------------------------------- -#if 0 && defined(_DEBUG) +#if 0 && defined( _DEBUG) #define TRACEFINDSPEEDF #endif //---------------------------------------------------------------------------- -RC RSYS::rs_FindRequiredSpeedF() // find VC intermediate speed +RC RSYS::rs_FindRequiredSpeedF() // find VC intermediate speed // at call: RSYS in full speed state (excess capacity, rs_fxCap[ 0]==1) // returns RCBAD iff search fail (unexpected) // else RCOK and RSYS at intermediate speed state // if rs_fxCap[ 0] == 1, pegged at rs_speedFMin (must cycle below min) { -#if defined(_DEBUG) - if (rs_fxCap[0] < 1.f) - printf("\nrs_AllocateZoneAirVC: rs_fxCap[ 0] < 1"); +#if defined( _DEBUG) + if (rs_fxCap[0] < 1.f) + printf("\nrs_AllocateZoneAirVC: rs_fxCap[ 0] < 1"); #endif - float fxCapFullSpd = rs_fxCap[0]; // save full speed cap rat - RC rc = RCOK; + float fxCapFullSpd = rs_fxCap[0]; // save full speed cap rat + RC rc = RCOK; - assert(!rs_IsAutoSizing()); + assert(!rs_IsAutoSizing()); - // try minimum speed - rc |= rs_TotalAirRequestForSpeedF(rs_speedFMin); + // try minimum speed + rc |= rs_TotalAirRequestForSpeedF( rs_speedFMin); - // note: rs_speedF > rs_speedFMin is possible - // rs_TotalAirRequestForSpeedF() increases speed to avoid useless supply temp + // note: rs_speedF > rs_speedFMin is possible + // rs_TotalAirRequestForSpeedF() increases speed to avoid useless supply temp - if (rc || rs_fxCap[0] >= 1.f) - return rc; // error or rs_speedFMin is sufficient + if (rc || rs_fxCap[0] >= 1.f) + return rc; // error or rs_speedFMin is sufficient - // rs_speedFMin not sufficient, search for intermediate speed + // rs_speedFMin not sufficient, search for intermediate speed - float fxCapMinSpd = rs_fxCap[0]; + float fxCapMinSpd = rs_fxCap[0]; - float speedFEst = rs_speedF + (1.f - rs_speedF) * (1.f - fxCapMinSpd) / - (fxCapFullSpd - fxCapMinSpd); + float speedFEst = rs_speedF + (1.f - rs_speedF) * (1.f - fxCapMinSpd) / (fxCapFullSpd - fxCapMinSpd); - double speedF, f1; + double speedF, f1; #if 1 - speedF = min(1., speedFEst * 1.2); - f1 = DBL_MIN; + speedF = min(1., speedFEst * 1.2); + f1 = DBL_MIN; #elif 0 - if (speedFEst > 0.5f * rs_speedF + 0.5f) { - speedF = 1.; - f1 = fxCapFullSpd; - } else { - speedF = rs_speedF; - f1 = fxCapMinSpd; - } + if (speedFEst > 0.5f * rs_speedF + 0.5f) + { + speedF = 1.; + f1 = fxCapFullSpd; + } + else + { + speedF = rs_speedF; + f1 = fxCapMinSpd; + } #elif 0 - speedF = rs_speedFMin; - f1 = fxCapMinSpd; + speedF = rs_speedFMin; + f1 = fxCapMinSpd; #else - speedF = double(0.5f * rs_speedFMin + 0.5f); - f1 = DBL_MIN; -#endif - -#if defined(TRACEFINDSPEEDF) - printf("\nStart speedFMin=%0.4f fxCapMinSpd=%0.4f fxCapFullSpd=%0.4f " - "speedFEst=%0.4f", - rs_speedFMin, fxCapMinSpd, fxCapFullSpd, speedFEst); -#endif - - // search for speedF that produces fxCap = 1. - // this RSYS is left in the corresponding state via - // rc_TotalAirRequestForSpeed() calls (secant results not directly returned) - int ret = secant( - [](void *pO, double &speedF) { - return ((RSYS *)pO)->rs_FxCapForSpeedF(speedF); - }, - this, 1., .0005, // target=rs_fxCap[0]=1 within .0005 - speedF, f1, // x1, f1 - speedFEst, DBL_MIN); // x2, f2 (not known) - if (ret) // if secant failed (unexpected) + speedF = double( 0.5f * rs_speedFMin + 0.5f); + f1 = DBL_MIN; +#endif + +#if defined( TRACEFINDSPEEDF) + printf("\nStart speedFMin=%0.4f fxCapMinSpd=%0.4f fxCapFullSpd=%0.4f speedFEst=%0.4f", + rs_speedFMin, fxCapMinSpd, fxCapFullSpd, speedFEst); +#endif + + // search for speedF that produces fxCap = 1. + // this RSYS is left in the corresponding state via rc_TotalAirRequestForSpeed() calls + // (secant results not directly returned) + int ret = secant([](void* pO, double& speedF) { return ((RSYS*)pO)->rs_FxCapForSpeedF(speedF); }, + this, + 1., .0005, // target=rs_fxCap[0]=1 within .0005 + speedF, f1, // x1, f1 + speedFEst, DBL_MIN); // x2, f2 (not known) + if (ret) // if secant failed (unexpected) #if 0 rc |= rs_FindSpeedFForTSup(80.); #else - // leave RSYS is speedFEst state (= semi-decent result) - rc |= rs_TotalAirRequestForSpeedF(speedFEst); + // leave RSYS is speedFEst state (= semi-decent result) + rc |= rs_TotalAirRequestForSpeedF(speedFEst); #endif -#if defined(TRACEFINDSPEEDF) - if (ret) - printf("\nrs_FindRequiredSpeedF: secant ret=%d rsMode=%d", ret, rs_mode); - if (rs_speedF < rs_speedFMin || rs_speedF > 1.f) - printf("\nrs_FindRequiredSpeedF bad rs_speedF=%0.2f", rs_speedF); - printf("\nDone speedF=%0.4f fxCap=%0.4f", rs_speedF, rs_fxCap[0]); +#if defined( TRACEFINDSPEEDF) + if (ret) + printf("\nrs_FindRequiredSpeedF: secant ret=%d rsMode=%d", ret, rs_mode); + if (rs_speedF < rs_speedFMin || rs_speedF > 1.f) + printf("\nrs_FindRequiredSpeedF bad rs_speedF=%0.2f", rs_speedF); + printf("\nDone speedF=%0.4f fxCap=%0.4f", rs_speedF, rs_fxCap[0]); #endif - return rc; + return rc; -} // RSYS::rs_FindRequiredSpeedF() +} // RSYS::rs_FindRequiredSpeedF() //----------------------------------------------------------------------------- -bool RSYS::rs_IsSupplyAirTempUseful( // determine utility of supply air - int rsMode, // mode of interest - float tSup, // proposed supply temp - float &tSupUseful) const // returned: temp that would be useful +bool RSYS::rs_IsSupplyAirTempUseful( // determine utility of supply air + int rsMode, // mode of interest + float tSup, // proposed supply temp + float& tSupUseful) const // returned: temp that would be useful // returns true iff proposed supply air temp // sets rs_TSupLimit { - bool bRet = false; - float tspMaxH = -999.f; - float tspMinC = 999.f; - ZNR *zp; - RLUPC(ZrB, zp, rs_IsZoneServed(zp)) { - setToMax(tspMaxH, zp->zn_tzspH); - setToMin(tspMinC, zp->zn_tzspC); - } - if (rsMode == rsmHEAT) { - tSupUseful = tspMaxH + .1f; - bRet = tSup >= tSupUseful; - } else { - tSupUseful = tspMinC - .1f; - bRet = tSup <= tSupUseful; - } - return bRet; -} // RSYS::rs_IsSupplyAirTempUseful + bool bRet = false; + float tspMaxH = -999.f; + float tspMinC = 999.f; + ZNR* zp; + RLUPC(ZrB, zp, rs_IsZoneServed(zp)) + { + setToMax(tspMaxH, zp->zn_tzspH); + setToMin(tspMinC, zp->zn_tzspC); + } + if (rsMode == rsmHEAT) + { + tSupUseful = tspMaxH + .1f; + bRet = tSup >= tSupUseful; + } + else + { + tSupUseful = tspMinC - .1f; + bRet = tSup <= tSupUseful; + } + return bRet; +} // RSYS::rs_IsSupplyAirTempUseful //----------------------------------------------------------------------------- -RC RSYS::rs_TotalAirRequestForSpeedF( // all-zone air request at speedF - float speedF, // speed to attempt - int options /*=0*/) // option bits - // 1: report zn_AirRequest errors +RC RSYS::rs_TotalAirRequestForSpeedF( // all-zone air request at speedF + float speedF, // speed to attempt + int options /*=0*/) // option bits + // 1: report zn_AirRequest errors // returns RCOK iff valid result (rs_fxCap[ 0] set) // else RCBAD, rs_fxCap[ 0] = 0 { - RC rc = RCOK; + RC rc = RCOK; - if (speedF != rs_speedF) { - rs_ClearSubhrResults(1); // init for speed re-try - /* int ret = */ rs_SupplyAirState(rs_mode, speedF); + if (speedF != rs_speedF) + { + rs_ClearSubhrResults(1); // init for speed re-try + /* int ret = */ rs_SupplyAirState(rs_mode, speedF); - float tSupUseful; - if (!rs_IsSupplyAirTempUseful( - rs_mode, rs_asSup.as_tdb, - tSupUseful)) { // adjust rs_speedF to produce useful supply air temp - rs_FindSpeedFForTSup(tSupUseful); - // printf("\nSpeedF = %0.3f", rs_speedF); - } - ZNR *zp; - RLUPC(ZrB, zp, rs_IsZoneServed(zp)) - rc |= zp->zn_AirRequest(this, options); + float tSupUseful; + if (!rs_IsSupplyAirTempUseful(rs_mode, rs_asSup.as_tdb, tSupUseful)) + { // adjust rs_speedF to produce useful supply air temp + rs_FindSpeedFForTSup(tSupUseful); + // printf("\nSpeedF = %0.3f", rs_speedF); - if (rc || rs_amfReq[0] <= 0.) { -#if defined(_DEBUG) - printf("\nrs_TotalAirRequestForSpeed: rs_amfReq[ 0] <= 0."); + } + ZNR* zp; + RLUPC(ZrB, zp, rs_IsZoneServed(zp)) + rc |= zp->zn_AirRequest(this, options); + + if (rc || rs_amfReq[0] <= 0.) + { +#if defined( _DEBUG) + printf("\nrs_TotalAirRequestForSpeed: rs_amfReq[ 0] <= 0."); #endif - rs_fxCap[0] = 0.f; - rc = RCBAD; - } else - rs_fxCap[0] = rs_amf / rs_amfReq[0]; // >1 = excess capacity - } + rs_fxCap[0] = 0.f; + rc = RCBAD; + } + else + rs_fxCap[0] = rs_amf / rs_amfReq[0]; // >1 = excess capacity + } - return rc; -} // RSYS::rs_TotalAirRequestForSpeedF + return rc; +} // RSYS::rs_TotalAirRequestForSpeedF //----------------------------------------------------------------------------- -double RSYS::rs_FxCapForSpeedF( // call-back fcn for secant method - double &speedF) // trial speedF (may be returned modified) +double RSYS::rs_FxCapForSpeedF( // call-back fcn for secant method + double& speedF) // trial speedF (may be returned modified) // returns required rs_fxCap[ 0] (= rs_amf / amfReq), >1 = excess capacity { -#if defined(_DEBUG) - double speedFWas = speedF; - // float rsSpeedFWas = rs_speedF; +#if defined( _DEBUG) + double speedFWas = speedF; + // float rsSpeedFWas = rs_speedF; #endif - speedF = bracket(double(rs_speedFMin), speedF, 1.); -#if defined(_DEBUG) - if (speedF != speedFWas) - printf("\nrs_FxCapForSpeedF limit: speedFWas=%0.4f speedF=%0.4f", - speedFWas, speedF); -#endif - - // options: report bad supply temps iff near solution - // (when far from solution errors are common and not meaningful - int arOptions = abs(rs_fxCap[0] - 1.f) < .01f; - - /*RC rc =*/rs_TotalAirRequestForSpeedF(float(speedF), arOptions); - - // if rc != RCOK, rs_fxCap[0] is 0 - -#if defined(TRACEFINDSPEEDF) - static double speedFLastCall = 0.; - static float fxLastCall = 0.; - bool bDup = speedF != speedFLastCall && rs_fxCap[0] == fxLastCall; - printf("\n speedF=%0.4f f=%0.4f tSup=%0.3f rs_amf=%0.3f rs_amfReq=%0.3f " - "(speedFLastCall=%0.4f rsSpeedFWas=%0.4f)%s", - speedF, rs_fxCap[0], rs_asSup.as_tdb, rs_amf, rs_amfReq[0], - speedFLastCall, rsSpeedFWas, bDup ? " Dup" : ""); - if (bDup) { - rs_speedF = rsSpeedFWas; - rs_TotalAirRequestForSpeedF(float(speedF), arOptions); - } - - if (rc) - printf("\nrs_FxCapForSpeedF: rs_TotalAirRequestForSpeedF() fail"); - speedFLastCall = speedF; - fxLastCall = rs_fxCap[0]; + speedF = bracket(double(rs_speedFMin), speedF, 1.); +#if defined( _DEBUG) + if (speedF != speedFWas) + printf("\nrs_FxCapForSpeedF limit: speedFWas=%0.4f speedF=%0.4f", + speedFWas, speedF); +#endif + + // options: report bad supply temps iff near solution + // (when far from solution errors are common and not meaningful + int arOptions = abs(rs_fxCap[0] - 1.f) < .01f; + + /*RC rc =*/ rs_TotalAirRequestForSpeedF(float(speedF), arOptions); + + // if rc != RCOK, rs_fxCap[0] is 0 + +#if defined( TRACEFINDSPEEDF) + static double speedFLastCall = 0.; + static float fxLastCall = 0.; + bool bDup = speedF != speedFLastCall && rs_fxCap[0] == fxLastCall; + printf("\n speedF=%0.4f f=%0.4f tSup=%0.3f rs_amf=%0.3f rs_amfReq=%0.3f (speedFLastCall=%0.4f rsSpeedFWas=%0.4f)%s", + speedF, rs_fxCap[0], rs_asSup.as_tdb, rs_amf, rs_amfReq[ 0], speedFLastCall, rsSpeedFWas, bDup ? " Dup" : ""); + if (bDup) + { + rs_speedF = rsSpeedFWas; + rs_TotalAirRequestForSpeedF(float(speedF), arOptions); + } + + if (rc) + printf("\nrs_FxCapForSpeedF: rs_TotalAirRequestForSpeedF() fail"); + speedFLastCall = speedF; + fxLastCall = rs_fxCap[0]; #else - // ignore rc, return rs_fxCap[ 0] = 0 + // ignore rc, return rs_fxCap[ 0] = 0 #endif - return double(rs_fxCap[0]); -} // RSYS::rs_FxCapForSpeedF + return double(rs_fxCap[0]); +} // RSYS::rs_FxCapForSpeedF //----------------------------------------------------------------------------- -RC RSYS::rs_FindSpeedFForTSup( // find speedF that will deliver specified supply - // temp - double tSup) { - RC rc = RCOK; - - double speedFLo = rs_speedFMin; - double speedFHi = 1.; - double f1 = DBL_MIN; - - // search for speedF that produces fxCap = 1. - // this RSYS is left in the corresponding state via - // rc_TotalAirRequestForSpeed() calls (secant results not directly returned) - [[maybe_unused]] int ret = secant( - [](void *pO, double &speedF) { - return ((RSYS *)pO)->rs_TSupForSpeedF(speedF); - }, - this, tSup, .005, // target=tSup within .005 degF - speedFLo, f1, // x1, f1 - speedFHi, DBL_MIN); // x2, f2 - - return rc; - -} // RSYS::rs_FindSpeedFForTSup +RC RSYS::rs_FindSpeedFForTSup( // find speedF that will deliver specified supply temp + double tSup) +{ + RC rc = RCOK; + + double speedFLo = rs_speedFMin; + double speedFHi = 1.; + double f1 = DBL_MIN; + + // search for speedF that produces fxCap = 1. +// this RSYS is left in the corresponding state via rc_TotalAirRequestForSpeed() calls +// (secant results not directly returned) + [[maybe_unused]] int ret = secant([](void* pO, double& speedF) { return ((RSYS*)pO)->rs_TSupForSpeedF(speedF); }, + this, + tSup, .005, // target=tSup within .005 degF + speedFLo, f1, // x1, f1 + speedFHi, DBL_MIN); // x2, f2 + + return rc; + +} // RSYS::rs_FindSpeedFForTSup //----------------------------------------------------------------------------- -double RSYS::rs_TSupForSpeedF(double &speedF) { -#if defined(_DEBUG) - double speedFWas = speedF; - // float rsSpeedFWas = rs_speedF; +double RSYS::rs_TSupForSpeedF( + double& speedF) +{ +#if defined( _DEBUG) + double speedFWas = speedF; + // float rsSpeedFWas = rs_speedF; #endif - speedF = bracket(double(rs_speedFMin), speedF, 1.); -#if defined(_DEBUG) - if (speedF != speedFWas) - printf("\nrs_TSupForSpeedF limit: speedFWas=%0.4f speedF=%0.4f", speedFWas, - speedF); + speedF = bracket(double(rs_speedFMin), speedF, 1.); +#if defined( _DEBUG) + if (speedF != speedFWas) + printf("\nrs_TSupForSpeedF limit: speedFWas=%0.4f speedF=%0.4f", + speedFWas, speedF); #endif - // rs_ClearSubhrResults(1); // init for speed re-try - /*int ret = */ rs_SupplyAirState(rs_mode, speedF); + // rs_ClearSubhrResults(1); // init for speed re-try + /*int ret = */ rs_SupplyAirState(rs_mode, speedF); - return rs_asSup.as_tdb; + return rs_asSup.as_tdb; -} // RSYS::rs_TSupForSpeedF +} // RSYS::rs_TSupForSpeedF //----------------------------------------------------------------------------- -#if 0 // incomplete/unused ideas +#if 0 // incomplete/unused ideas void RSYS::rs_AvgZoneCond() { const ZNR* zp; @@ -6214,22 +6264,22 @@ void RSYS::rs_HeatDelivered( // heat delivered to zone(s) by this RSYS } // RSYS::rs_SenHeatDelivered #endif //---------------------------------------------------------------------------- -double RSYS::rs_AmfRequired( // find all-zone total AMF required for tSup - double tSup) // proposed supply air temp, F -{ - double rsAmf = 0.; - ZNR *zp; - RLUPC(ZrB, zp, rs_IsZoneServed(zp) && zp->zn_hcMode != RSYS::rsmOFF) { - double znAmf = zp->zn_AmfHvacCR(zp->zn_tzsp, tSup); -#if defined(_DEBUG) - if (znAmf < 0.) - printf("Zone '%s': Neg amf\n", zp->Name()); -#endif - rsAmf += znAmf; - } - float ductLkXF = rs_ducts[rs_DsHC()].ductLkXF[0]; - return rsAmf / ductLkXF; -} // RSYS::rs_AmfRequired +double RSYS::rs_AmfRequired( // find all-zone total AMF required for tSup + double tSup) // proposed supply air temp, F +{ + double rsAmf = 0.; + ZNR* zp; + RLUPC( ZrB, zp, rs_IsZoneServed( zp) && zp->zn_hcMode != RSYS::rsmOFF) + { double znAmf = zp->zn_AmfHvacCR( zp->zn_tzsp, tSup); +#if defined( _DEBUG) + if (znAmf < 0.) + printf( "Zone '%s': Neg amf\n", zp->Name()); +#endif + rsAmf += znAmf; + } + float ductLkXF = rs_ducts[rs_DsHC()].ductLkXF[0]; + return rsAmf / ductLkXF; +} // RSYS::rs_AmfRequired //---------------------------------------------------------------------------- #if 0 x unused idea @@ -6253,161 +6303,161 @@ x } // RSYS::rs_ACReqCap95 RC RSYS::rs_FinalizeSh() // rs_speedF known { - // determine run fraction - // Note: rs_runF modified below for ASHP C_AUXHEATCTRL_ALT - double amfRun = min(rs_amfReq[0], rs_amf); - if (amfRun < .0001) - rs_mode = rsmOFF; // no air actually delivered - if (rs_mode == rsmOFF) - rs_SetModeAndSpeedF(rsmOFF, 0.f); - // rs_runF = 0.f; // rs_ClearSubhrResults - else if (rs_amf > 0.) - // rs_SetModeAndSpeedF() called in rs_SupplyAirState - rs_runF = amfRun / rs_amf; - // else rs_runF = 0.f + // determine run fraction + // Note: rs_runF modified below for ASHP C_AUXHEATCTRL_ALT + double amfRun = min( rs_amfReq[ 0], rs_amf); + if (amfRun < .0001) + rs_mode = rsmOFF; // no air actually delivered + if (rs_mode == rsmOFF) + rs_SetModeAndSpeedF( rsmOFF, 0.f); + // rs_runF = 0.f; // rs_ClearSubhrResults + else if (rs_amf > 0.) + // rs_SetModeAndSpeedF() called in rs_SupplyAirState + rs_runF = amfRun / rs_amf; + // else rs_runF = 0.f #if 0 // restore if needed rs_AvgZoneCond(); #endif - // record peaks or calc energy - // Probably unnecessary for autosizing - // TODO: could skip accounting if autoSizing - // BUT may be side effects - float runFFan = 0.f; // subhour fan run fraction - if (rs_mode == rsmCOOL) { - rs_outSen = - rs_runF * - rs_capSenCt; // average output w/o fan (= gross cooling), Btuh (< 0) - rs_outLat = rs_runF * rs_capLatCt; - rs_outFan = rs_inFan = - rs_runF * rs_speedF * rs_fanHeatC; // all fan heat to air - runFFan = rs_runF; - rs_outSenTot = - rs_outSen + rs_outFan; // net cooling output, Btuh (generally < 0) - - if (rs_IsFanCoil()) { // rs_inPrimary = 0 - - } else if (!rs_IsVCClg() || rs_speedF == 1.f || - rs_speedF <= rs_speedFMin) { // cycling possible - // not variable capacity - // or variable capacity below min speed - rs_PLF = 1.f - rs_CdC * (1.f - rs_runF); - rs_inPrimary = fabs( - rs_outSen / max(.01f, rs_effCt * rs_PLF)); // compressor power, Btuh - } else { - // variable capacity: intermediate speed -#if defined(_DEBUG) - if (fabs(rs_runF - 1.f) > 0.001f) - printf("\nUnexpected rs_runF"); -#endif - rs_PLF = 1.f; - rs_inPrimary = - fabs(rs_outSen / max(.01f, rs_effCt)); // compressor power, Btuh - } - - if (rs_pMtrElec) { - rs_pMtrElec->H.clg += - rs_inPrimary * Top.tp_subhrDur; // compressor energy for step, Btu - rs_pMtrElec->H.fanC += rs_inFan * Top.tp_subhrDur; -#if defined(_DEBUG) - if (!rs_IsFanCoil() && rs_pMtrElec->H.clg < .01f && - rs_pMtrElec->H.fanC > .01f) - printf("\nFanC > 0"); -#endif - } - } else if (rs_mode == rsmHEAT) { - if (rs_IsHP()) { // rs_runFAux known at this point - if (rs_effHt == 0.f) // if compressor is off - { // rs_inPrimary = 0.; - // rs_COPHtAdj = 0.f; - runFFan = rs_runFAux; - rs_outFan = rs_runFAux * rs_fanHeatH; - rs_outAux = rs_runFAux * rs_capAuxH; - rs_runF = rs_PLF = 0.f; - rs_capHt = rs_capHtMin = rs_capDfHt = rs_capDfHtMin = - 0.f; // clear values for reports - // aux may be on, handled below - } else { - float fFanHeatPrim; // run fraction for both fan and primary - if (rs_runFAux > 0.f && - rs_ctrlAuxH == - C_AUXHEATCTRL_ALT) { // aux/primary alternate: primary runs when - // aux does not - rs_runF = 1.f - rs_runFAux; - fFanHeatPrim = rs_runF; // fan heat allocation to primary - runFFan = 1.f; -#if defined(_DEBUG) - if (rs_speedF != 1.f) - printf("\nUnexpected rs_speedF = %0.3f", rs_speedF); -#endif - } else { - runFFan = rs_runF; - fFanHeatPrim = 1.f; - } - - rs_capSenNetFS = rs_capHt - rs_capDfHt; // full-speed net capacity - - double outTot = - rs_runF * rs_CapHtCurSpeedF(); // total primary output (incl fan and - // defrost), Btuh - - rs_outAux = rs_runFAux * rs_capAuxH; + // record peaks or calc energy + // Probably unnecessary for autosizing + // TODO: could skip accounting if autoSizing + // BUT may be side effects + float runFFan = 0.f; // subhour fan run fraction + if (rs_mode == rsmCOOL) + { + rs_outSen = rs_runF * rs_capSenCt; // average output w/o fan (= gross cooling), Btuh (< 0) + rs_outLat = rs_runF * rs_capLatCt; + rs_outFan = rs_inFan = rs_runF * rs_speedF * rs_fanHeatC; // all fan heat to air + runFFan = rs_runF; + rs_outSenTot = rs_outSen + rs_outFan; // net cooling output, Btuh (generally < 0) - rs_outFan = min(outTot + rs_outAux, - runFFan * rs_speedF * - rs_fanHeatH); // fan output, Btuh - // insurance: limit to total output - rs_outSen = - outTot - rs_outFan * fFanHeatPrim; // compressor sensible output - // (note defrost adjustment - // below) rs_outLat = 0.; - // // total latent output + if (rs_IsFanCoil()) + { // rs_inPrimary = 0 + } + else if (!rs_IsVCClg() || rs_speedF == 1.f || rs_speedF <= rs_speedFMin) + { // cycling possible + // not variable capacity + // or variable capacity below min speed + rs_PLF = 1.f - rs_CdC * (1.f - rs_runF); + rs_inPrimary = fabs(rs_outSen / max(.01f, rs_effCt * rs_PLF)); // compressor power, Btuh + } + else + { + // variable capacity: intermediate speed #if defined(_DEBUG) - // some checking if aux is running - if (rs_runFAux > 0.f) { - if (rs_ctrlAuxH == C_AUXHEATCTRL_LO) { - if (rs_runF > 0.f) - printf("\nAux LO: compressor running"); - } else if (rs_ctrlAuxH == C_AUXHEATCTRL_ALT) { - if (abs(rs_runFAux + rs_runF - 1.f) > 0.0001f) - printf("\nAux alt: bad runF+runFAux"); - } else { - if (rs_runF != 1.f || rs_speedF != 1.f) - printf("\nAux cycle: not full speed"); - } - } -#endif - if (!rs_IsASHPVC() || - rs_speedF == 1.f) { // single speed or variable speed at max - // variable speed can cycle at max due to C_AUXHEATCTRL_ALT - rs_outDefrost = rs_runF * rs_capDfHt; - rs_outSen -= rs_outDefrost; - rs_PLF = 1.f - rs_CdH * (1.f - rs_runF); - rs_COPHtAdj = - rs_effHt * rs_PLF; // full load efficiency modified by PLF - rs_inPrimary = rs_outSen / rs_COPHtAdj; - ASSERT(rs_speedF == 1.f); - } else if (rs_speedF <= - rs_speedFMin) { // variable capacity: cycling at min speed - rs_outDefrost = rs_runF * rs_capDfHtMin; - rs_outSen -= rs_outDefrost; - rs_PLF = 1.f - rs_CdH * (1.f - rs_runF); - rs_COPHtAdj = rs_inpHtMin <= 0.f - ? 2.5f // rs_inpHtMin can be 0 during autosize - : rs_fEffH * rs_capHtMin * rs_PLF / rs_inpHtMin; - rs_inPrimary = rs_outSen / rs_COPHtAdj; - } else { // variable capacity: intermediate speed - rs_outDefrost = rs_CapDfHtCurSpeedF(); - rs_outSen -= rs_outDefrost; - rs_inPrimary = rs_InpHtCurSpeedF() / rs_fEffH; - rs_COPHtAdj = rs_inPrimary <= 0.f - ? 2.5f // rs_inPrimary can be 0 during autosize - : rs_outSen / rs_inPrimary; - rs_PLF = 1.f; - } + if (fabs(rs_runF - 1.f) > 0.001f) + printf("\nUnexpected rs_runF"); +#endif + rs_PLF = 1.f; + rs_inPrimary = fabs(rs_outSen / max(.01f, rs_effCt)); // compressor power, Btuh + } + + if (rs_pMtrElec) + { rs_pMtrElec->H.clg += rs_inPrimary * Top.tp_subhrDur; // compressor energy for step, Btu + rs_pMtrElec->H.fanC += rs_inFan * Top.tp_subhrDur; +#if defined( _DEBUG) + if (!rs_IsFanCoil() && rs_pMtrElec->H.clg < .01f && rs_pMtrElec->H.fanC > .01f) + printf("\nFanC > 0"); +#endif + } + } + else if (rs_mode == rsmHEAT) + { if (rs_IsHP()) + { // rs_runFAux known at this point + if (rs_effHt == 0.f) // if compressor is off + { // rs_inPrimary = 0.; + // rs_COPHtAdj = 0.f; + runFFan = rs_runFAux; + rs_outFan = rs_runFAux * rs_fanHeatH; + rs_outAux = rs_runFAux * rs_capAuxH; + rs_runF = rs_PLF = 0.f; + rs_capHt = rs_capHtMin = rs_capDfHt = rs_capDfHtMin = 0.f; // clear values for reports + // aux may be on, handled below + } + else + { + float fFanHeatPrim; // run fraction for both fan and primary + if (rs_runFAux > 0.f && rs_ctrlAuxH == C_AUXHEATCTRL_ALT) + { // aux/primary alternate: primary runs when aux does not + rs_runF = 1.f - rs_runFAux; + fFanHeatPrim = rs_runF; // fan heat allocation to primary + runFFan = 1.f; +#if defined( _DEBUG) + if (rs_speedF != 1.f) + printf("\nUnexpected rs_speedF = %0.3f", rs_speedF); +#endif + } + else + { runFFan = rs_runF; + fFanHeatPrim = 1.f; + } + + rs_capSenNetFS = rs_capHt - rs_capDfHt; // full-speed net capacity + + double outTot = rs_runF * rs_CapHtCurSpeedF(); // total primary output (incl fan and defrost), Btuh + + rs_outAux = rs_runFAux * rs_capAuxH; + + rs_outFan = min( outTot+rs_outAux, runFFan * rs_speedF * rs_fanHeatH); // fan output, Btuh + // insurance: limit to total output + rs_outSen = outTot - rs_outFan*fFanHeatPrim; // compressor sensible output (note defrost adjustment below) + // rs_outLat = 0.; // total latent output + +#if defined( _DEBUG) + // some checking if aux is running + if (rs_runFAux > 0.f) + { + if (rs_ctrlAuxH == C_AUXHEATCTRL_LO) + { + if (rs_runF > 0.f) + printf("\nAux LO: compressor running"); + } + else if (rs_ctrlAuxH == C_AUXHEATCTRL_ALT) + { + if (abs( rs_runFAux + rs_runF - 1.f) > 0.0001f) + printf("\nAux alt: bad runF+runFAux"); + } + else + { + if (rs_runF != 1.f || rs_speedF != 1.f) + printf("\nAux cycle: not full speed"); + } + } +#endif + if (!rs_IsASHPVC() || rs_speedF == 1.f) + { // single speed or variable speed at max + // variable speed can cycle at max due to C_AUXHEATCTRL_ALT + rs_outDefrost = rs_runF * rs_capDfHt; + rs_outSen -= rs_outDefrost; + rs_PLF = 1.f - rs_CdH * (1.f - rs_runF); + rs_COPHtAdj = rs_effHt * rs_PLF; // full load efficiency modified by PLF + rs_inPrimary = rs_outSen / rs_COPHtAdj; + ASSERT(rs_speedF == 1.f); + } + else if (rs_speedF <= rs_speedFMin) + { // variable capacity: cycling at min speed + rs_outDefrost = rs_runF * rs_capDfHtMin; + rs_outSen -= rs_outDefrost; + rs_PLF = 1.f - rs_CdH * (1.f - rs_runF); + rs_COPHtAdj = rs_inpHtMin <= 0.f + ? 2.5f // rs_inpHtMin can be 0 during autosize + : rs_fEffH * rs_capHtMin * rs_PLF / rs_inpHtMin; + rs_inPrimary = rs_outSen / rs_COPHtAdj; + } + else + { // variable capacity: intermediate speed + rs_outDefrost = rs_CapDfHtCurSpeedF(); + rs_outSen -= rs_outDefrost; + rs_inPrimary = rs_InpHtCurSpeedF() / rs_fEffH; + rs_COPHtAdj = rs_inPrimary <= 0.f + ? 2.5f // rs_inPrimary can be 0 during autosize + : rs_outSen / rs_inPrimary; + rs_PLF = 1.f; + } #if 0 x Not maintained, 8-2020 x#undef ASHP_COMPAREMP // define to enable Micropas comparison code for ASHP @@ -6428,384 +6478,402 @@ x#endif x x rs_inPrimary = rs_outSen / (rs_effHt * rs_PLF); #endif - } - - rs_inAux = rs_outAux / (rs_effAuxH * rs_fEffAuxHBackup); - rs_inDefrost = rs_outDefrost / (rs_effAuxH * rs_fEffAuxHDefrost); - - if (rs_pMtrAux) - rs_pMtrAux->H.hpBU += (rs_inAux + rs_inDefrost) * Top.tp_subhrDur; - } else if (rs_IsCHDHW()) { // cycling - // pump power - rs_outSenTot = rs_runF * rs_speedF * rs_capHt; - float avf; - float fanPwr; - rs_pCHDHW->hvt_BlowerAVFandPower(rs_outSenTot, avf, fanPwr); - // if (rs_runF < 1.) cycle? - - rs_outFan = rs_runF * fanPwr * Top.tp_subhrDur * BtuperWh; - rs_outSen = max(0., rs_outSenTot - rs_outFan); // net -> gross - runFFan = rs_runF; - - // flow (gpm) needed for gross output - float waterVolFlow = rs_pCHDHW->hvt_WaterVolFlow(rs_outSen, rs_tCoilEW); - float vol = rs_runF * waterVolFlow * Top.tp_subhrDur * 60.f; - - // return temp based on gross (coil) output - float tRet = - rs_tCoilEW - rs_outSen * Top.tp_subhrDur / (vol * waterRhoCp); - - // apply draw to DHWSYS - DHWSYS *pWS = rs_GetCHDHWSYS(); - pWS->ws_AccumCHDHWFlowSh(vol, tRet); - - // rs_inPrimary = 0.; - } else { // non-ASHP, non-CHDHW - rs_capSenNetFS = rs_capHt; // net capacity, Btuh - double outTot = rs_runF * rs_capHt; // total output (incl fan), Btuh - // rs_outLat = 0.; // total - // latent output - rs_outFan = min( - outTot, rs_runF * rs_fanHeatH); // fan output, Btuh - // insurance: limit to total output - runFFan = rs_runF; - rs_outSen = outTot - rs_outFan; - rs_PLF = 1.f; - if (rs_IsFanCoil()) - ; // nothing needed, energy supplied externally - else { // not fancoil, not CHDHW, not AHSP - rs_inPrimary = rs_outSen / rs_effHt; - } - } - - if (rs_pMtrHeat) - rs_pMtrHeat->H.htg += rs_inPrimary * Top.tp_subhrDur; - rs_inFan = rs_outFan; // fan input, Btuh (in = out, all fan heat into air) - if (rs_pMtrElec) - rs_pMtrElec->H.fanH += rs_inFan * Top.tp_subhrDur; - - rs_outSenTot = rs_outSen + rs_outDefrost + rs_outAux + rs_outFan; - } else if (rs_mode == rsmOAV) { - runFFan = rs_runF; - rs_outSenTot = rs_outFan = rs_inFan = rs_fanHeatOAV * rs_runF; - if (rs_pMtrElec) - rs_pMtrElec->H.fanC += rs_inFan * Top.tp_subhrDur; - } - // else if (rs_Mode == rsmOFF) - - if (rs_capSenNetFS != 0.f) { - rs_loadF = (rs_outSen + rs_outFan) / rs_capSenNetFS; - rs_PLR = rs_znLoad[0] / rs_capSenNetFS; -#if defined(_DEBUG) - if (rs_loadF < 0.f) - printf("\nNeg rs_loadF"); -#endif - } + } + + rs_inAux = rs_outAux / (rs_effAuxH * rs_fEffAuxHBackup); + rs_inDefrost = rs_outDefrost / (rs_effAuxH * rs_fEffAuxHDefrost); + + if (rs_pMtrAux) + rs_pMtrAux->H.hpBU += (rs_inAux + rs_inDefrost) * Top.tp_subhrDur; + } + else if (rs_IsCHDHW()) + { // cycling + // pump power + rs_outSenTot = rs_runF * rs_speedF * rs_capHt; + float avf; + float fanPwr; + rs_pCHDHW->hvt_BlowerAVFandPower(rs_outSenTot, avf, fanPwr); + // if (rs_runF < 1.) cycle? + + rs_outFan = rs_runF * fanPwr * Top.tp_subhrDur * BtuperWh; + rs_outSen = max(0., rs_outSenTot - rs_outFan); // net -> gross + runFFan = rs_runF; + + // flow (gpm) needed for gross output + float waterVolFlow = rs_pCHDHW->hvt_WaterVolFlow(rs_outSen, rs_tCoilEW); + float vol = rs_runF * waterVolFlow * Top.tp_subhrDur * 60.f; + + // return temp based on gross (coil) output + float tRet = rs_tCoilEW - rs_outSen * Top.tp_subhrDur / (vol * waterRhoCp); + + // apply draw to DHWSYS + DHWSYS* pWS = rs_GetCHDHWSYS(); + pWS->ws_AccumCHDHWFlowSh(vol, tRet); + + // rs_inPrimary = 0.; + } + else + { // non-ASHP, non-CHDHW + rs_capSenNetFS = rs_capHt; // net capacity, Btuh + double outTot = rs_runF * rs_capHt; // total output (incl fan), Btuh + // rs_outLat = 0.; // total latent output + rs_outFan = min( outTot, rs_runF * rs_fanHeatH); // fan output, Btuh + // insurance: limit to total output + runFFan = rs_runF; + rs_outSen = outTot - rs_outFan; + rs_PLF = 1.f; + if (rs_IsFanCoil()) + ; // nothing needed, energy supplied externally + else + { // not fancoil, not CHDHW, not AHSP + rs_inPrimary = rs_outSen / rs_effHt; + } + } + + if (rs_pMtrHeat) + rs_pMtrHeat->H.htg += rs_inPrimary * Top.tp_subhrDur; + rs_inFan = rs_outFan; // fan input, Btuh (in = out, all fan heat into air) + if (rs_pMtrElec) + rs_pMtrElec->H.fanH += rs_inFan * Top.tp_subhrDur; - // parasitic consumption - if (rs_pMtrElec) - rs_pMtrElec->H.aux += rs_parElec * Top.tp_subhrDur * BtuperWh; - if (rs_pMtrFuel) - rs_pMtrFuel->H.aux += rs_parFuel * Top.tp_subhrDur; + rs_outSenTot = rs_outSen + rs_outDefrost + rs_outAux + rs_outFan; + } + else if (rs_mode == rsmOAV) + { runFFan = rs_runF; + rs_outSenTot = rs_outFan = rs_inFan = rs_fanHeatOAV * rs_runF; + if (rs_pMtrElec) + rs_pMtrElec->H.fanC += rs_inFan * Top.tp_subhrDur; + } + // else if (rs_Mode == rsmOFF) + + if (rs_capSenNetFS != 0.f) + { rs_loadF = (rs_outSen + rs_outFan) / rs_capSenNetFS; + rs_PLR = rs_znLoad[0] / rs_capSenNetFS; +#if defined( _DEBUG) + if (rs_loadF < 0.f) + printf("\nNeg rs_loadF"); +#endif + } - if (runFFan > 0.f) { // duct conduction and leakage to adjacent zones - for (int iSR = 0; iSR < 2; iSR++) { - int iDS = rs_Dsi(iSR); - if (iDS > 0) - DsR[iDS].ds_FinalizeSh(runFFan); - } - } + // parasitic consumption + if (rs_pMtrElec) + rs_pMtrElec->H.aux += rs_parElec * Top.tp_subhrDur * BtuperWh; + if (rs_pMtrFuel) + rs_pMtrFuel->H.aux += rs_parFuel * Top.tp_subhrDur; + + if (runFFan > 0.f) + { // duct conduction and leakage to adjacent zones + for (int iSR=0; iSR<2; iSR++) + { int iDS = rs_Dsi( iSR); + if (iDS>0) + DsR[ iDS].ds_FinalizeSh( runFFan); + } + } - return RCOK; + return RCOK; -} // RSYS::rs_FinalizeSh +} // RSYS::rs_FinalizeSh //============================================================================ -static RC loadsIzxSh1() // interzone transfers, part 1 +static RC loadsIzxSh1() // interzone transfers, part 1 // does all non-AirNet IZXFERs + infil AirNet // accumulates zone .zn_qIzShXAnSh's, assumed pre-0'd. { - RC rc = RCOK; - - if (Top.tp_airNetActive && Top.tp_pAirNet) // if any AirNet - { - rc = Top.tp_pAirNet->an_Calc(0); // find all-zone pressure balance - if (rc == RCNOP) // if nothing done (no zones) - rc = RCOK; // treat as OK - } - - IZXRAT *ize; - RLUP(IzxR, ize) { - if (ize->iz_IsAirNet()) - ize->iz_ZoneXfers(0); // accum heat flows to zone - else - ize->iz_Calc(); // calc non-AirNet transfers - // accum to ZNR.zn_qIzXAnSh's - } - - ZNR *zp; - RLUPC(ZrB, zp, !zp->zn_IsConvRad()) - zp->zn_qIzSh = - zp->zn_qIzXAnSh + zp->zn_AnAmfCpT(0) - zp->zn_AnAmfCp(0) * zp->tzls; - // else zn_qIzXAnSh used in CR heat balance eqns elsewhere - - return rc; -} // ::loadsIzxSh1 + RC rc=RCOK; + + if (Top.tp_airNetActive && Top.tp_pAirNet) // if any AirNet + { rc = Top.tp_pAirNet->an_Calc(0); // find all-zone pressure balance + if (rc == RCNOP) // if nothing done (no zones) + rc = RCOK; // treat as OK + } + + IZXRAT* ize; + RLUP( IzxR, ize) + { if (ize->iz_IsAirNet()) + ize->iz_ZoneXfers( 0); // accum heat flows to zone + else + ize->iz_Calc(); // calc non-AirNet transfers + // accum to ZNR.zn_qIzXAnSh's + } + + ZNR* zp; + RLUPC( ZrB, zp, !zp->zn_IsConvRad()) + zp->zn_qIzSh = zp->zn_qIzXAnSh + + zp->zn_AnAmfCpT( 0) - zp->zn_AnAmfCp( 0)*zp->tzls; + // else zn_qIzXAnSh used in CR heat balance eqns elsewhere + + return rc; +} // ::loadsIzxSh1 //----------------------------------------------------------------------------- -static RC loadsIzxSh2() // interzone transfers, part 2 +static RC loadsIzxSh2() // interzone transfers, part 2 // calcs AirNet vent // accumulates zone .zn_qIzSh's, assumed pre-0'd. { - RC rc = RCOK; - - if (Top.tp_airNetActive && Top.tp_pAirNet) { - rc = Top.tp_pAirNet->an_Calc(1); // find pressure balance - if (rc == RCNOP) - rc = RCOK; - if (rc == RCOK) { - IZXRAT *ize; - RLUP(IzxR, ize) { - if (ize->iz_IsAirNet()) - ize->iz_ZoneXfers(1); // accum heat flows to zone - } - } - } - -#if defined(DEBUGDUMP) - if (DbDo(dbdAIRNET | dbdIZ)) { - ZNR *zp; - DbPrintf("\n"); - RLUP(ZrB, zp) - DbPrintf("%s vent: MCpVent=%.2f MCpTVent=%.1f\n", zp->Name(), - zp->zn_AnAmfCp(1), zp->zn_AnAmfCpT(1)); - } -#endif - return rc; -} // ::loadsIzxSh2 + RC rc=RCOK; + + if (Top.tp_airNetActive && Top.tp_pAirNet) + { rc = Top.tp_pAirNet->an_Calc( 1); // find pressure balance + if (rc == RCNOP) + rc = RCOK; + if (rc == RCOK) + { IZXRAT* ize; + RLUP(IzxR, ize) + { if (ize->iz_IsAirNet()) + ize->iz_ZoneXfers(1); // accum heat flows to zone + } + } + } + +#if defined( DEBUGDUMP) + if (DbDo( dbdAIRNET|dbdIZ)) + { ZNR* zp; + DbPrintf("\n"); + RLUP( ZrB, zp) + DbPrintf( "%s vent: MCpVent=%.2f MCpTVent=%.1f\n", + zp->Name(), + zp->zn_AnAmfCp( 1), zp->zn_AnAmfCpT( 1)); + } +#endif + return rc; +} // ::loadsIzxSh2 //-------------------------------------------------------------------- -static RC loadsXFans() // SIMULATE zone exhaust fans (xfans) -{ - RC rc = RCOK; - ZNR *zp; - RLUP(ZrB, zp) - rc |= zp->zn_XFan(); - return rc; -} // ::loadsXFans +static RC loadsXFans() // SIMULATE zone exhaust fans (xfans) +{ + RC rc = RCOK; + ZNR* zp; + RLUP( ZrB, zp) + rc |= zp->zn_XFan(); + return rc; +} // ::loadsXFans //-------------------------------------------------------------------- -RC ZNR::zn_XFan() // zone exhaust fan calcs (hourly) +RC ZNR::zn_XFan() // zone exhaust fan calcs (hourly) { - float fOn = i.xfanFOn; - i.xfan.fn_puteVf(i.xfan.vfDs * fOn, tzlh, fOn); + float fOn = i.xfanFOn; + i.xfan.fn_puteVf( i.xfan.vfDs*fOn, tzlh, fOn); - if (i.xfan.fn_mtri) - MtrB.p[i.xfan.fn_mtri].H.fan += i.xfan.p; + if (i.xfan.fn_mtri) + MtrB.p[ i.xfan.fn_mtri].H.fan += i.xfan.p; - return RCOK; + return RCOK; -} // ZNR::zn_XFan +} // ZNR::zn_XFan //==================================================================== -static RC loadsSurfaces( // surface runtime simulation - BOO subhrly) // 0 to do hourly masses, 1 to do subhourly surfaces/masses +static RC loadsSurfaces( // surface runtime simulation + BOO subhrly) // 0 to do hourly masses, 1 to do subhourly surfaces/masses // called before zone balances { - RC rc = RCOK; - - // Init zone surface heat xfers to 0, loops below sums to them - ZNR *zp; - RLUP(ZrB, zp) // for zp = ZNR record 1 to .n - { - if (subhrly) { - zp->zn_ieMassls = - zp->zn_ieMass; // re determination of mass internal energy change - zp->aMassSh = zp->zn_hcATMsSh = zp->zn_hrATMsSh = zp->zn_hcAMsSh = - zp->zn_hrAMsSh = zp->zn_qCondQS = zp->zn_qCondMS = zp->zn_ieMass = 0.; - zp->qSgTotSh = zp->zn_sgTotShTarg.st_tot; - // total solar gain to zone, Btuh (redundant re reporting and bal checks) - // add'l may be added by surfaces, e.g. ASHWAT inward flowing fraction - } else { - zp->aMassHr = 0.; - zp->qSgTot = zp->zn_sgTotTarg.st_tot; // see qSgTotSh comments just above - } - } - - if (subhrly) { - XSRAT *xr; - TMRSTART(TMR_BC); - RLUP(XsB, xr) - rc |= xr->x.xs_SubhrBC(); - TMRSTOP(TMR_BC); - - TMRSTART(TMR_AWTOT); // total ASHWAT time (incl setup) - // see also TMR_AWCALC - RLUPC(XsB, xr, xr->x.xs_IsASHWAT()) - rc |= xr->x.xs_ASHWAT(); - TMRSTOP(TMR_AWTOT); - - RLUPC(XsB, xr, xr->x.xs_ty != CTMXWALL) - rc |= xr->x.xs_SubhrQS(); - } - - float dur, tDbO; - if (subhrly) { - dur = Top.tp_subhrDur; // interval duration (hours) - tDbO = Top.tDbOShAv; // outdoor temp averaged over time interval - } else // hourly - { - dur = 1.f; - tDbO = Top.tDbOHrAv; - } - - // masses - TMRSTART(TMR_COND); - MSRAT *mse; - RLUPC(MsR, mse, subhrly == mse->isSubhrly) // do matching interval - { - if (mse->ms_isFD) - mse->ms_StepFD(); // FD (forward_difference) model is only subhourly - else - mse->ms_StepMX(dur, tDbO); - } // mass loop - TMRSTOP(TMR_COND); - - // kiva instances - TMRSTART(TMR_KIVA); - - BOO kivaIsSubhourly = Top.tp_grndTimeStep == C_TSCH_SH; - - if (subhrly == kivaIsSubhourly) // Only enter if correct interval - { - KIVA *ki; - RLUP(KvR, ki) { ki->kv_Step(dur); } - - // Loop over surfaces to assign weighted values and accummulate to zone - // balance - XSRAT *xr; - RLUP(XsB, xr) { rc |= xr->xr_ApplyKivaResults(); } - } - - // Loop over surfaces to accummulate to zone balance - XSRAT *xr; - RLUP(XsB, xr) { rc |= xr->xr_KivaZoneAccum(); } - - TMRSTOP(TMR_KIVA); - - return rc; -} // loadsSurfaces + RC rc = RCOK; + + // Init zone surface heat xfers to 0, loops below sums to them + ZNR* zp; + RLUP( ZrB, zp) // for zp = ZNR record 1 to .n + { if (subhrly) + { zp->zn_ieMassls = zp->zn_ieMass; // re determination of mass internal energy change + zp->aMassSh = zp->zn_hcATMsSh = zp->zn_hrATMsSh = zp->zn_hcAMsSh = zp->zn_hrAMsSh + = zp->zn_qCondQS = zp->zn_qCondMS = zp->zn_ieMass = 0.; + zp->qSgTotSh = zp->zn_sgTotShTarg.st_tot; + // total solar gain to zone, Btuh (redundant re reporting and bal checks) + // add'l may be added by surfaces, e.g. ASHWAT inward flowing fraction + } + else + { zp->aMassHr = 0.; + zp->qSgTot = zp->zn_sgTotTarg.st_tot; // see qSgTotSh comments just above + } + } + + if (subhrly) + { XSRAT* xr; + TMRSTART( TMR_BC); + RLUP (XsB, xr) + rc |= xr->x.xs_SubhrBC(); + TMRSTOP( TMR_BC); + + TMRSTART( TMR_AWTOT); // total ASHWAT time (incl setup) + // see also TMR_AWCALC + RLUPC(XsB, xr, xr->x.xs_IsASHWAT()) + rc |= xr->x.xs_ASHWAT(); + TMRSTOP( TMR_AWTOT); + + RLUPC(XsB, xr, xr->x.xs_ty != CTMXWALL) + rc |= xr->x.xs_SubhrQS(); + } + + float dur, tDbO; + if (subhrly) + { dur = Top.tp_subhrDur; // interval duration (hours) + tDbO = Top.tDbOShAv; // outdoor temp averaged over time interval + } + else // hourly + { dur = 1.f; + tDbO = Top.tDbOHrAv; + } + + // masses + TMRSTART( TMR_COND); + MSRAT* mse; + RLUPC( MsR, mse, subhrly == mse->isSubhrly) // do matching interval + { if (mse->ms_isFD) + mse->ms_StepFD(); // FD (forward_difference) model is only subhourly + else + mse->ms_StepMX( dur, tDbO); + } // mass loop + TMRSTOP( TMR_COND); + + // kiva instances + TMRSTART(TMR_KIVA); + + BOO kivaIsSubhourly = Top.tp_grndTimeStep == C_TSCH_SH; + + if (subhrly == kivaIsSubhourly) // Only enter if correct interval + { + KIVA* ki; + RLUP(KvR, ki) + { + ki->kv_Step(dur); + } + + // Loop over surfaces to assign weighted values and accummulate to zone balance + XSRAT* xr; + RLUP(XsB, xr) + { + rc |= xr->xr_ApplyKivaResults(); + } + + } + + // Loop over surfaces to accummulate to zone balance + XSRAT* xr; + RLUP(XsB, xr) + { + rc |= xr->xr_KivaZoneAccum(); + } + + TMRSTOP(TMR_KIVA); + + + return rc; +} // loadsSurfaces //----------------------------------------------------------------------------- -RC XSURF::xs_SubhrBC() // subhour: surface boundary conditions -{ - RC rc = RCOK; - - // set surface coefficients - double areaEff = xs_IsASHWAT() ? xs_AreaGlazed() : xs_area; - xs_sbcI.sb_SetCoeffs(areaEff, uC); - xs_sbcO.sb_SetCoeffs(areaEff, uC); - if (xs_IsASHWAT()) - xs_pFENAW[0]->fa_FrameBC(); - return rc; -} // XSURF::xs_SubbrBC +RC XSURF::xs_SubhrBC() // subhour: surface boundary conditions +{ + RC rc = RCOK; + + // set surface coefficients + double areaEff = xs_IsASHWAT() ? xs_AreaGlazed() : xs_area; + xs_sbcI.sb_SetCoeffs( areaEff, uC); + xs_sbcO.sb_SetCoeffs( areaEff, uC); + if (xs_IsASHWAT()) + xs_pFENAW[ 0]->fa_FrameBC(); + return rc; +} // XSURF::xs_SubbrBC //----------------------------------------------------------------------------- -RC XSURF::xs_ASHWAT() // subhour calcs for ASHWAT fenestration +RC XSURF::xs_ASHWAT() // subhour calcs for ASHWAT fenestration // do NOT call for if !xs_IsASHWAT() { - RC rc = RCOK; + RC rc = RCOK; - ZNR &z = ZrB[xs_GetZi(0)]; // zone on inside of surface + ZNR& z = ZrB[ xs_GetZi(0)]; // zone on inside of surface - // interior shades: determine fraction closed - float fSC = xs_HasControlledShade() ? z.i.znSC : 0.f; // fraction closed - int bDoFrm = 1; - if (fSC < .999f) { // shades open - xs_pFENAW[0]->fa_Subhr(1.f - max(fSC, 0.f), bDoFrm); - bDoFrm = 0; // do frame only once! - } - if (fSC > .001f) { // shades closed - xs_pFENAW[1]->fa_Subhr(min(fSC, 1.f), bDoFrm); - } - FPCHECK; + // interior shades: determine fraction closed + float fSC = xs_HasControlledShade() ? z.i.znSC : 0.f; // fraction closed + int bDoFrm = 1; + if (fSC < .999f) + { // shades open + xs_pFENAW[ 0]->fa_Subhr( 1.f - max( fSC, 0.f), bDoFrm); + bDoFrm = 0; // do frame only once! + } + if (fSC > .001f) + { // shades closed + xs_pFENAW[ 1]->fa_Subhr( min( fSC, 1.f), bDoFrm); + } + FPCHECK; - return rc; -} // XSURF::xs_ASHWAT + return rc; +} // XSURF::xs_ASHWAT //----------------------------------------------------------------------------- -RC XSURF::xs_SubhrQS() // subhour quick surface -{ - // Only call for quick surfaces! - RC rc = RCOK; - - ZNR &z = ZrB[xs_GetZi(0)]; // zone on inside of surface - if (xs_IsASHWAT()) - xs_pFENAW[0]->fa_FrameQS(); - else { - z.zn_AccumBalTermsQS(xs_area, xs_sbcI, xs_sbcO); - if (xs_sbcO.sb_zi) { // interzone quick surface: handle adjacent zone - ZNR &zx = ZrB[xs_sbcO.sb_zi]; - zx.zn_AccumBalTermsQS(xs_area, xs_sbcO, xs_sbcI); - } - } - - FPCHECK; - return rc; -} // XSURF::xs_SubhrQS +RC XSURF::xs_SubhrQS() // subhour quick surface +{ + // Only call for quick surfaces! + RC rc = RCOK; + + ZNR& z = ZrB[ xs_GetZi(0)]; // zone on inside of surface + if (xs_IsASHWAT()) + xs_pFENAW[ 0]->fa_FrameQS(); + else + { z.zn_AccumBalTermsQS( xs_area, xs_sbcI, xs_sbcO); + if (xs_sbcO.sb_zi) + { // interzone quick surface: handle adjacent zone + ZNR& zx = ZrB[ xs_sbcO.sb_zi]; + zx.zn_AccumBalTermsQS( xs_area, xs_sbcO, xs_sbcI); + } + } + + FPCHECK; + return rc; +} // XSURF::xs_SubhrQS //----------------------------------------------------------------------------- -RC XSURF::xs_EndSubhr() // end-of-subhr calcs for surface -{ - RC rc = RCOK; - - // update adjacent temps from zone heat bal outcome - // (nop if not adjacent to zone) - - xs_sbcI.sb_SetTx(); - xs_sbcO.sb_SetTx(); - - ZNR &z = ZrB[xs_GetZi(0)]; - - if (xs_IsASHWAT()) { - xs_pFENAW[0]->fa_FrameEndSubhr(); - float fSC = xs_HasControlledShade() ? z.i.znSC : 0.f; // fraction closed - if (fSC < .999f) - xs_pFENAW[0]->fa_EndSubhr(1.f - max(fSC, 0.f)); - if (fSC > .001f) - xs_pFENAW[1]->fa_EndSubhr(min(fSC, 1.f)); - } else if (xs_msi != 0) { - MSRAT &ms = MsR[xs_msi]; - xs_sbcI.sb_qSrf = ms.ms_pMM->mm_qSrf[0]; - xs_sbcO.sb_qSrf = ms.ms_pMM->mm_qSrf[1]; - - // accum to zone - // zn_ieMass and zn_qCondMS are experimental value re energy balance - // checking unused? as of 12-21-10 - z.zn_ieMass += ms.ms_InternalEnergy(); - double qSrfA = - xs_area * xs_sbcO.sb_qSrf; // conduction at outside surface, Btuh - z.zn_qCondMS += qSrfA; - if (sfAdjZi) { - ZNR &zx = ZrB[sfAdjZi]; - zx.zn_qCondMS -= qSrfA; - } - - if (Top.isEndHour) { // last step of hour: store layer boundary temps for - // probes - MASSMODEL *pMM = ms.ms_pMM; - pMM->mm_FillBoundaryTemps(xs_tLrB, XSMXTLRB); - } - } else if (xs_IsKiva()) { - // Kiva accounting? - } else - z.zn_EndSubhrQS(xs_area, uC, xs_sbcI, xs_sbcO); - - return rc; - -} // XSURF::xs_EndSubhr +RC XSURF::xs_EndSubhr() // end-of-subhr calcs for surface +{ + RC rc = RCOK; + + // update adjacent temps from zone heat bal outcome + // (nop if not adjacent to zone) + + xs_sbcI.sb_SetTx(); + xs_sbcO.sb_SetTx(); + + ZNR& z = ZrB[ xs_GetZi( 0)]; + + if (xs_IsASHWAT()) + { xs_pFENAW[ 0]->fa_FrameEndSubhr(); + float fSC = xs_HasControlledShade() ? z.i.znSC : 0.f; // fraction closed + if (fSC < .999f) + xs_pFENAW[ 0]->fa_EndSubhr( 1.f - max( fSC, 0.f)); + if (fSC > .001f) + xs_pFENAW[ 1]->fa_EndSubhr( min( fSC, 1.f)); + } + else if (xs_msi != 0) + { MSRAT& ms = MsR[ xs_msi]; + xs_sbcI.sb_qSrf = ms.ms_pMM->mm_qSrf[ 0]; + xs_sbcO.sb_qSrf = ms.ms_pMM->mm_qSrf[ 1]; + + // accum to zone + // zn_ieMass and zn_qCondMS are experimental value re energy balance checking + // unused? as of 12-21-10 + z.zn_ieMass += ms.ms_InternalEnergy(); + double qSrfA = xs_area*xs_sbcO.sb_qSrf; // conduction at outside surface, Btuh + z.zn_qCondMS += qSrfA; + if (sfAdjZi) + { ZNR& zx = ZrB[ sfAdjZi]; + zx.zn_qCondMS -= qSrfA; + } + + if (Top.isEndHour) + { // last step of hour: store layer boundary temps for probes + MASSMODEL* pMM = ms.ms_pMM; + pMM->mm_FillBoundaryTemps( xs_tLrB, XSMXTLRB); + } + } + else if (xs_IsKiva()) + { + // Kiva accounting? + } + else + z.zn_EndSubhrQS( xs_area, uC, xs_sbcI, xs_sbcO); + + return rc; + +} // XSURF::xs_EndSubhr //----------------------------------------------------------------------------- -void XSURF::xs_AfterSubhr() // end-of-subhr calcs for surface +void XSURF::xs_AfterSubhr() // end-of-subhr calcs for surface { - // last step surface temps (w/o *e, re probes) - xs_sbcI.sb_tSrfls = xs_sbcI.sb_tSrf; - xs_sbcO.sb_tSrfls = xs_sbcO.sb_tSrf; -} // XSURF::xs_AfterSubhr + // last step surface temps (w/o *e, re probes) + xs_sbcI.sb_tSrfls = xs_sbcI.sb_tSrf; + xs_sbcO.sb_tSrfls = xs_sbcO.sb_tSrf; +} // XSURF::xs_AfterSubhr //----------------------------------------------------------------------------- -void XSURF::xs_AfterHour() // end-of-hour calcs for surface +void XSURF::xs_AfterHour() // end-of-hour calcs for surface { #if 0 if (xs_msi != 0) @@ -6814,69 +6882,66 @@ void XSURF::xs_AfterHour() // end-of-hour calcs for surface pMM->mm_FillBoundaryTemps( xs_tLrB, XSMXTLRB); } #endif -} // XSURF::xs_AfterHour +} // XSURF::xs_AfterHour //----------------------------------------------------------------------------- RC FC loadsAfterSubhr() -// end-subhour after-exprs/reports stuff for loads: set 'prior interval' -// variables etc if done sooner, probes come out wrong. +// end-subhour after-exprs/reports stuff for loads: set 'prior interval' variables etc +// if done sooner, probes come out wrong. { - RC rc = RCOK; + RC rc=RCOK; - // zones - ZNR *zp; - RLUP(ZrB, zp) - rc |= zp->zn_AfterSubhr(); + // zones + ZNR* zp; + RLUP( ZrB, zp) + rc |= zp->zn_AfterSubhr(); - // surfaces - XSRAT *xr; - RLUP(XsB, xr) - xr->x.xs_AfterSubhr(); + // surfaces + XSRAT* xr; + RLUP( XsB, xr) + xr->x.xs_AfterSubhr(); - // systems - RSYS *rs; - RLUP(RsR, rs) - rc |= rs->rs_AfterSubhr(); + // systems + RSYS* rs; + RLUP( RsR, rs) + rc |= rs->rs_AfterSubhr(); - DUCTSEG *ds; - RLUP(DsR, ds) - ds->ds_AfterSubhr(); + DUCTSEG* ds; + RLUP( DsR, ds) + ds->ds_AfterSubhr(); - return rc; -} // loadsAfterSubhr + return rc; +} // loadsAfterSubhr //------------------------------------------------------------------------------------- RC ZNR::zn_AfterSubhr() -// end-subhour after-exprs/reports stuff for loads: set 'prior interval' -// variables etc if done sooner, probes come out wrong. +// end-subhour after-exprs/reports stuff for loads: set 'prior interval' variables etc +// if done sooner, probes come out wrong. { -#if 0 && defined(_DEBUG) +#if 0 && defined( _DEBUG) if (Top.iHr == 7) printf("\niHr=%d", Top.iHr); #endif - tzlsDelta = - tz - tzls; // last subhour t delta: may be used to extrapolate next tz - tzls = tz; // last subhr zone air temp for next subHour - wzlsDelta = - wz - wzls; // last subhour w delta: may be used to extrapolate next wz - wzls = wz; // last subhr zone hum rat for next subHour - znXLGainLs = znXLGain; // last subhour excess latent gain: next subhour - // sensible condensation gain. 5-97. - // pz0 (pressure relative to patm at nomimal z=0) initially 0, then known from - // prior AirNet solution - zn_rho0ls = zn_Rho0(); // last subhour moist air density at tz, wz, pz0 - zn_trls = zn_tr; // last subhour radiant temp - zn_relHumls = zn_relHum; // lass subhour relative humdity - -#if 0 // unused, 9-12 + tzlsDelta = tz - tzls; // last subhour t delta: may be used to extrapolate next tz + tzls = tz; // last subhr zone air temp for next subHour + wzlsDelta = wz - wzls; // last subhour w delta: may be used to extrapolate next wz + wzls = wz; // last subhr zone hum rat for next subHour + znXLGainLs = znXLGain; // last subhour excess latent gain: next subhour sensible condensation gain. 5-97. + // pz0 (pressure relative to patm at nomimal z=0) initially 0, then known from + // prior AirNet solution + zn_rho0ls = zn_Rho0(); // last subhour moist air density at tz, wz, pz0 + zn_trls = zn_tr; // last subhour radiant temp + zn_relHumls = zn_relHum; // lass subhour relative humdity + +#if 0 // unused, 9-12 x zn_dryAirMassls = zn_dryAirMass; // last subhour dry air mass, lbm x zn_qsHvacls = zn_qsHvac; // last subhour hvac sensible power x zn_qlHvacls = zn_qlHvac; // last subhour hvac latent power #endif - zn_hcAirXls = i.zn_hcAirX; // last subhour air exchange rate - // for convection coefficient models + zn_hcAirXls = i.zn_hcAirX; // last subhour air exchange rate + // for convection coefficient models - // ducts: nothing needed (current step values are used (lagged) at beg - // of next step, then initialized) + // ducts: nothing needed (current step values are used (lagged) at beg + // of next step, then initialized) #if 0 // 7-17-92 FAILS cuz solar gains recalculated hourly only. x // default zone shade closure (former shutter fraction): whether movable window shades open or closed for next subhour. @@ -6888,57 +6953,57 @@ x i.znSC = (float)(zrs->qsMech < 0.); // 1.0 if cooling in previous subhour, x } #endif - zn_sysDepAirIls = zn_ductLkI; // total duct leak flow into this zone + zn_sysDepAirIls = zn_ductLkI; // total duct leak flow into this zone - zn_rsAmfRetLs = zn_rsAmfRet; + zn_rsAmfRetLs = zn_rsAmfRet; - return RCOK; -} // ZNR::zn_AfterSubhr + return RCOK; +} // ZNR::zn_AfterSubhr //------------------------------------------------------------------------------------- RC FC loadsAfterHour() // end-hour after-exprs/reports stuff for loads // set 'prior interval' variables etc: if done sooner, probes come out wrong. { - RC rc = RCOK; + RC rc=RCOK; - // surfaces - XSRAT *xr; - RLUP(XsB, xr) - xr->x.xs_AfterHour(); + // surfaces + XSRAT* xr; + RLUP( XsB, xr) + xr->x.xs_AfterHour(); - // zones - ZNR *zp; - RLUP(ZrB, zp) - rc |= zp->zn_AfterHour(); + // zones + ZNR* zp; + RLUP( ZrB, zp) + rc |= zp->zn_AfterHour(); - // systems - RSYS *rs; - RLUP(RsR, rs) - rc |= rs->rs_AfterHour(); + // systems + RSYS* rs; + RLUP( RsR, rs) + rc |= rs->rs_AfterHour(); - return rc; -} // loadsAfterHour + return rc; +} // loadsAfterHour //----------------------------------------------------------------------------- RC ZNR::zn_AfterHour() // end-hour after-exprs/reports stuff for loads // set 'prior interval' variables etc: if done sooner, probes come out wrong. { - // next hour's "end last hour" zone air temp - tzlh = tzls; // air temp from final step of hour (NOT average here). - // needed? used in main eqn in loadsHourBeg, but .tzls cd be used - // directly 1-92. + // next hour's "end last hour" zone air temp + tzlh = tzls; // air temp from final step of hour (NOT average here). + // needed? used in main eqn in loadsHourBeg, but .tzls cd be used directly 1-92. - zn_trlh = zn_trls; // ditto radiant temp + zn_trlh = zn_trls; // ditto radiant temp - zn_relHumlh = zn_relHumls; // ditto relative humidity + zn_relHumlh = zn_relHumls; // ditto relative humidity + + // end of hour set points + // used re ramped setpoints for autosizing + zn_tzspHlh = zn_tzspH; + zn_tzspDlh = zn_tzspD; + zn_tzspClh = zn_tzspC; - // end of hour set points - // used re ramped setpoints for autosizing - zn_tzspHlh = zn_tzspH; - zn_tzspDlh = zn_tzspD; - zn_tzspClh = zn_tzspC; #if 0 // nHrCool is present but not set: no longer works. x // zone shutter fraction for next hour: determine whether movable window shades open or closed next hour @@ -6948,22 +7013,17 @@ x x // ?? This is *NOT* the full CEC current algorithm. Where is the 1 degF hysteresis? #else // 7-17-92 - // default zone shade closure (former shutter fraction): whether movable - // window shades open or closed for next hour. note this code responds to net - // cooling, whereas CALRES responded to ANY cooling (zrh->nHrCool, not now - // implemented). subhourly defaulting desirable (rob's opinion); must - // duplicate loadsHourBeg sg code in loadsSubhr w znSC-change trigger. - if (!znSCF) // not if expression given by user - { - ZNRES_IVL_SUB *zrh = - &ZnresB.p[ss].curr.H; // point zone current subhour results in resrat - i.znSC = (float)(zrh->qsMech < - -ABOUT0); // 1.0 if cooling (more than heating) in previous - // hour, else 0. -ABOUT0: no cooling if near 0 so - // not random when hvac off, 8-92. - } -#endif - return RCOK; -} // ZNR::zn_AfterHour + // default zone shade closure (former shutter fraction): whether movable window shades open or closed for next hour. + // note this code responds to net cooling, whereas CALRES responded to ANY cooling (zrh->nHrCool, not now implemented). + // subhourly defaulting desirable (rob's opinion); must duplicate loadsHourBeg sg code in loadsSubhr w znSC-change trigger. + if (!znSCF) // not if expression given by user + { + ZNRES_IVL_SUB *zrh = &ZnresB.p[ ss].curr.H; // point zone current subhour results in resrat + i.znSC = (float)(zrh->qsMech < -ABOUT0); // 1.0 if cooling (more than heating) in previous hour, else 0. + // -ABOUT0: no cooling if near 0 so not random when hvac off, 8-92. + } +#endif + return RCOK; +} // ZNR::zn_AfterHour // end of cnloads.cpp diff --git a/src/envpak.cpp b/src/envpak.cpp index 846e9a696..7b57a1adb 100644 --- a/src/envpak.cpp +++ b/src/envpak.cpp @@ -11,296 +11,309 @@ #include #endif -#include // FPE_UNDERFLOW FPE_INVALID etc -#include // signal SIGINT -#include // timeb structure +#include // signal SIGINT +#include // FPE_UNDERFLOW FPE_INVALID etc +#include // timeb structure -#include "lookup.h" // lookws wstable -#include "tdpak.h" // tdldti -#include "xiopak.h" // xchdir +#include "lookup.h" // lookws wstable +#include "tdpak.h" // tdldti +#include "xiopak.h" // xchdir -#include "envpak.h" // declares functions in this file +#include "envpak.h" // declares functions in this file -#if CSE_OS == CSE_OS_MACOS +#if CSE_OS==CSE_OS_MACOS #include // _NSGetExecutablePath #endif /*----------------------- LOCAL FUNCTION DECLARATIONS ---------------------*/ -LOCAL void CDEC enkiint(INT); // no NEAR -- passed to another function +LOCAL void CDEC enkiint(INT); // no NEAR -- passed to another function /*============================ TEST CODE =============================*/ /* #define TEST */ #ifdef TEST -t unmaintained 9 - 12 - 89 t main() t { - t SI i; - t float x; - t LDATETIME ldt; - t IDATETIME idt; - t t #ifdef KITEST t enkiinit(); - t enkimode(KICLEAR + KIBEEP); - t for (i = 0; i < 100; i++) t { - if (enkichk()) - t { - printf("\n\nInterrupt...."); - t gcne(); - t - } - t printf("Hi %d\n", i); - t - } - t enkimode(KILEAVEHIT); - t if (enkichk()) t printf("Still set\n"); - t else t printf("Clear\n"); - t t hello(NULL); - t enkimode(KICLEAR + KIBEEP); - t printf("\nlooping..."); - t for (i = 0; i < 10000; i++) t { - x = 2.; - t x = x * x * x * x * x; - t if (enkichk()) t { - printf("\n\nInterrupt...."); - t gcne(); - t enkimode(KICLEAR + KIBEEP); - t - } - t - } - t printf("Done."); - t enkimode(KILEAVEHIT); - t if (enkichk()) t printf("Still set\n"); - t else t printf("Clear\n"); - t #endif /* KITEST */ - t #ifdef DTTEST t ldt = ensysldt(); - t ensystd(&idt); - t printf("Time: %ld\n", ldt); - t #endif /* DTTEST */ - t -} +t unmaintained 9-12-89 +t main () +t { +t SI i; +t float x; +t LDATETIME ldt; +t IDATETIME idt; +t +t #ifdef KITEST +t enkiinit(); +t enkimode(KICLEAR+KIBEEP); +t for (i = 0; i < 100; i++) +t { if (enkichk()) +t { printf("\n\nInterrupt...."); +t gcne(); +t } +t printf("Hi %d\n",i); +t } +t enkimode(KILEAVEHIT); +t if (enkichk()) +t printf("Still set\n"); +t else +t printf("Clear\n"); +t +t hello(NULL); +t enkimode(KICLEAR+KIBEEP); +t printf("\nlooping..."); +t for (i = 0; i < 10000; i++) +t { x = 2.; +t x = x*x*x*x*x; +t if (enkichk()) +t { printf("\n\nInterrupt...."); +t gcne(); +t enkimode(KICLEAR+KIBEEP); +t } +t } +t printf("Done."); +t enkimode(KILEAVEHIT); +t if (enkichk()) +t printf("Still set\n"); +t else +t printf("Clear\n"); +t #endif /* KITEST */ +t #ifdef DTTEST +t ldt = ensysldt(); +t ensystd(&idt); +t printf("Time: %ld\n",ldt); +t #endif /* DTTEST */ +t } #endif //============================================================================= -WStr enExePath() // full path to current executable +WStr enExePath() // full path to current executable { - WStr t; - char exePath[FILENAME_MAX + 1]; + WStr t; + char exePath[FILENAME_MAX + 1]; #if CSE_OS == CSE_OS_MACOS - uint32_t pathSize = sizeof(exePath); - _NSGetExecutablePath(exePath, &pathSize); - t = exePath; - WStrLower(t); + uint32_t pathSize = sizeof(exePath); + _NSGetExecutablePath(exePath, &pathSize); + t = exePath; + WStrLower(t); #elif CSE_OS == CSE_OS_LINUX - ssize_t len = readlink("/proc/self/exe", exePath, sizeof(exePath) - 1); - if (len == -1) { - errCrit(PABT, "Unable to locate executable."); - } else { - exePath[len] = '\0'; - t = exePath; - WStrLower(t); - } + ssize_t len = readlink("/proc/self/exe", exePath, sizeof(exePath) - 1); + if (len == -1) { + errCrit(PABT, "Unable to locate executable."); + } + else { + exePath[len] = '\0'; + t = exePath; + WStrLower(t); + } #elif CSE_OS == CSE_OS_WINDOWS - if (GetModuleFileName(NULL, exePath, sizeof(exePath)) > 0) { - t = exePath; - WStrLower(t); - } + if (GetModuleFileName(NULL, exePath, sizeof(exePath)) > 0) + { + t = exePath; + WStrLower(t); + } #else #error Missing CSE_OS case #endif - WStrLower(t); - return t; -} // enExePath + WStrLower(t); + return t; +} // enExePath //============================================================================= -WStr enExeInfo( // retrieve build date/time, linker version, etc from exe - WStr exePath, // path to .exe - int &codeSize) // code size (bytes) +WStr enExeInfo( // retrieve build date/time, linker version, etc from exe + WStr exePath, // path to .exe + int& codeSize) // code size (bytes) // returns info as string or "?" if error { - const char *msg = "?"; - time_t timeDateStamp = 0; - codeSize = 0; - WStr linkerVersion; + const char* msg = "?"; + time_t timeDateStamp = 0; + codeSize = 0; + WStr linkerVersion; #if CSE_OS == CSE_OS_WINDOWS - HANDLE hFile = CreateFile(exePath.c_str(), GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); - - if (hFile == INVALID_HANDLE_VALUE) - msg = "Cannot open file"; - else { - HANDLE hFileMapping = - CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); - if (hFileMapping == 0) - msg = "Cannot create file mapping"; - else { - PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)MapViewOfFile( - hFileMapping, FILE_MAP_READ, 0, 0, 0); - if (pDosHeader == 0) - msg = "MapViewOfFile() failed"; - else { - if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) - msg = "Invalid EXE file"; - else { - try { // use try/catch re possible bad e_lfanew, EOF, etc. - PIMAGE_NT_HEADERS pNTHeader = - PIMAGE_NT_HEADERS(ULI(pDosHeader) + pDosHeader->e_lfanew); - if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) - msg = "Not a Portable Executable (PE) EXE"; - else { - timeDateStamp = pNTHeader->FileHeader.TimeDateStamp; - codeSize = pNTHeader->OptionalHeader.SizeOfCode; - linkerVersion = WStrPrintf( - "%d.%d", pNTHeader->OptionalHeader.MajorLinkerVersion, - pNTHeader->OptionalHeader.MinorLinkerVersion); - } - } catch (...) { - msg = "Invalid EXE file"; - } - } - UnmapViewOfFile(pDosHeader); - } - CloseHandle(hFileMapping); - } - CloseHandle(hFile); - } + HANDLE hFile = CreateFile( exePath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + + if (hFile == INVALID_HANDLE_VALUE) + msg = "Cannot open file"; + else + { + HANDLE hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (hFileMapping == 0) + msg = "Cannot create file mapping"; + else + { PIMAGE_DOS_HEADER pDosHeader = + (PIMAGE_DOS_HEADER)MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0); + if (pDosHeader == 0) + msg = "MapViewOfFile() failed"; + else + { if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) + msg = "Invalid EXE file"; + else + { try + { // use try/catch re possible bad e_lfanew, EOF, etc. + PIMAGE_NT_HEADERS pNTHeader = + PIMAGE_NT_HEADERS(ULI(pDosHeader) + pDosHeader->e_lfanew); + if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) + msg = "Not a Portable Executable (PE) EXE"; + else + { + timeDateStamp = pNTHeader->FileHeader.TimeDateStamp; + codeSize = pNTHeader->OptionalHeader.SizeOfCode; + linkerVersion = WStrPrintf( "%d.%d", + pNTHeader->OptionalHeader.MajorLinkerVersion, + pNTHeader->OptionalHeader.MinorLinkerVersion); + } + } + catch (...) + { msg = "Invalid EXE file"; + } + } + UnmapViewOfFile( pDosHeader); + } + CloseHandle( hFileMapping); + } + CloseHandle( hFile); + } #endif - WStr exeInfo; - if (timeDateStamp == 0) - exeInfo = "? (enExeInfo fail)"; - else - exeInfo = - WStrPrintf("%s (VS %s %d bytes)", tdldts(timeDateStamp, NULL), - linkerVersion.c_str(), codeSize); - return exeInfo; -} // enExeInfo + WStr exeInfo; + if (timeDateStamp == 0) + exeInfo = "? (enExeInfo fail)"; + else + exeInfo = WStrPrintf( "%s (VS %s %d bytes)", + tdldts( timeDateStamp, NULL), + linkerVersion.c_str(), + codeSize); + return exeInfo; +} // enExeInfo //===================================================================== -void FC ensystd( // Return the system date and time as IDATETIME +void FC ensystd( // Return the system date and time as IDATETIME - IDATETIME *dt) // Pointer to structure to receive date and time - // (dtypes.h struct, described in tdpak.cpp) + IDATETIME *dt ) // Pointer to structure to receive date and time + // (dtypes.h struct, described in tdpak.cpp) { - // Get time in LDATETIME format from system, convert to IDATETIME - tdldti( // convert format: gets year/month/mday/wday. tdpak.cpp - ensysldt(), // get time from system as seconds from 1/1/1970 - dt); // caller's destination -} // ensystd +// Get time in LDATETIME format from system, convert to IDATETIME + tdldti( // convert format: gets year/month/mday/wday. tdpak.cpp + ensysldt(), // get time from system as seconds from 1/1/1970 + dt ); // caller's destination +} // ensystd //===================================================================== -LDATETIME FC ensysldt() // Return system date and time as LDATETIME +LDATETIME FC ensysldt() // Return system date and time as LDATETIME { - return (LDATETIME)time(NULL); // seconds since 1/1/1970 (msc library) -} // ensysldt + return (LDATETIME)time(NULL); // seconds since 1/1/1970 (msc library) +} // ensysldt /* *************** Keyboard Interrupt Handling Routines ************ */ -#define KIWAITING 0 // Waiting for a keyboard interrupt -#define KIHIT 1 // KI received but not yet noticed by program -#define KISEEN \ - 2 // KI has been noticed by prog and is being processed. - // Don't bother user further. - // reverts to KIWAITING when enkimode called. -static SI kiflag = KIWAITING; // Interrupt flag used by routines -static SI kimode = 0; // KIBEEP bit on to beep at ^C. +#define KIWAITING 0 // Waiting for a keyboard interrupt +#define KIHIT 1 // KI received but not yet noticed by program +#define KISEEN 2 // KI has been noticed by prog and is being processed. + // Don't bother user further. + // reverts to KIWAITING when enkimode called. +static SI kiflag = KIWAITING; // Interrupt flag used by routines +static SI kimode = 0; // KIBEEP bit on to beep at ^C. //===================================================================== -void FC enkiinit( // Initialize for keyboard interrupt handling +void FC enkiinit( // Initialize for keyboard interrupt handling - SI mode) // Keyboard interrupt mode, passed to enkimode(). - // Should contain a KICLEAR to properly initialize. + SI mode ) // Keyboard interrupt mode, passed to enkimode(). + // Should contain a KICLEAR to properly initialize. { - signal(SIGINT, enkiint); // tell C lib to call enkiint() on ^C - enkimode(mode); // next. save beep bit, clear kiflag. -} // enkiinit + signal( SIGINT, enkiint); // tell C lib to call enkiint() on ^C + enkimode(mode); // next. save beep bit, clear kiflag. +} // enkiinit //===================================================================== -void FC -enkimode( // Set keyboard interrupt mode and conditionally reset interrupt flag - - SI mode) // Keyboard interrupt mode. defines in envpak.h: - // KIBEEP: beep on ^C. Always used 11-12-89. - // KISILENT(0): don't beep. No uses. - // KICLEAR (0): always clear internal control flag - // KILEAVEHIT: do not clear hit not yet seen by program (any real - // uses? dospak:pause() 11-89... */ +void FC enkimode( // Set keyboard interrupt mode and conditionally reset interrupt flag + + SI mode ) // Keyboard interrupt mode. defines in envpak.h: + // KIBEEP: beep on ^C. Always used 11-12-89. + // KISILENT(0): don't beep. No uses. + // KICLEAR (0): always clear internal control flag + // KILEAVEHIT: do not clear hit not yet seen by program (any real uses? dospak:pause() 11-89... */ { - kimode = mode; // save mode (beep bit tested in enkiint) - if (kiflag == KISEEN // if hit has been seen by program - || ((mode & KILEAVEHIT) == 0)) // or not KILEAVEHIT (ie is KICLEAR) - kiflag = KIWAITING; // clear internal control flag -} // enkimode + kimode = mode; // save mode (beep bit tested in enkiint) + if (kiflag == KISEEN // if hit has been seen by program + || ((mode & KILEAVEHIT) == 0)) // or not KILEAVEHIT (ie is KICLEAR) + kiflag = KIWAITING; // clear internal control flag +} // enkimode //===================================================================== -LOCAL void CDEC enkiint( // no NEAR -- passed to another function +LOCAL void CDEC enkiint( // no NEAR -- passed to another function - // Control is transferred here when user issues a keyboard interrupt +// Control is transferred here when user issues a keyboard interrupt - INT /*val*/) // MSC runtimes pass an arg. UNUSED ARG. + INT /*val*/ ) // MSC runtimes pass an arg. UNUSED ARG. { - signal(SIGINT, enkiint); // reactivate at C library level + signal( SIGINT, enkiint); // reactivate at C library level #ifdef ENVDEBUG - 0 printf("\nENKIINT called..."); +0 printf("\nENKIINT called..."); #endif - if (kiflag == KIWAITING) // change "waiting" to "hit" - kiflag = KIHIT; // but leave KISEEN unchanged -#if 0 // sound not supported, 4-12 + if (kiflag == KIWAITING) // change "waiting" to "hit" + kiflag = KIHIT; // but leave KISEEN unchanged +#if 0 // sound not supported, 4-12 x if (kimode & KIBEEP) // if requested when mode set x enbeep(BEEPINT); // beep. sound.cpp. #endif -} // enkiint +} // enkiint //===================================================================== -int enkichk() // Check whether a keyboard interrupt has been received +int enkichk() // Check whether a keyboard interrupt has been received -// Returns non-0 if ^C has occurred and has not yet been cleared by calling -// enkimode(). +// Returns non-0 if ^C has occurred and has not yet been cleared by calling enkimode(). { #if 1 - return CheckAbort(); + return CheckAbort(); #else - // currently not implemented under Windows 2-94. - // to implement, watch keyboard messages in (main) window proc. + // currently not implemented under Windows 2-94. + // to implement, watch keyboard messages in (main) window proc. - return FALSE; // claim no ^C typed + return FALSE; // claim no ^C typed #endif -} // enkichk +} // enkichk //===================================================================== + + /*-------------------------- FUNCTION DECLARATIONS ------------------------*/ // PUBLIC functions called only from msc lib or interrupt code; no prototype // in envpak.h so inadvertent external calls are flagged. 8-31-91 // INT matherr( struct exception *); private math runtime error fcn; -// actual -// decl in CRT (formerly math.h), now (12/23) not known -// void CDEC fpeErr( INT, INT); // intercepts floating point errors, and -// integer errors under Borland -#if 0 // Defined but not declated, 5-22 +// actual decl in CRT (formerly math.h), now (12/23) not known +// void CDEC fpeErr( INT, INT); // intercepts floating point errors, and integer errors under Borland +#if 0 // Defined but not declated, 5-22 void __cdecl fpeErr( INT, INT); // intercepts floating point errors, and integer errors under Borland #endif /*---------------------------- LOCAL VARIABLES ----------------------------*/ // saved by hello() for byebye() -LOCAL char cwdSave[CSE_MAX_PATH] = {0}; // current directory to restore at exit +LOCAL char cwdSave[CSE_MAX_PATH] = {0}; // current directory to restore at exit + /*----------------------------- TEST CODE ----------------------------------*/ #ifdef TEST -t t main() /* Test routines */ - t { - t SI sec, i, j; - struct exception tx; - double x; - RC rc; - t t j = 0; - t dminit(); /* initialize dynamic memory */ - t hello(NULL); - t t x = sqrt(-1.); - t x = asin(5.); - t t #if 0 t x = 0.; - t x = 1. / x; - t #else t i = j; - t i = 1 / i; - t #endif t t return 0; - t -} /* test main */ -#endif /* TEST */ +t +t main() /* Test routines */ +t{ +t SI sec, i, j; +struct exception tx; +double x; +RC rc; +t +t j = 0; +t dminit(); /* initialize dynamic memory */ +t hello( NULL); +t +t x = sqrt(-1.); +t x = asin(5.); +t +t #if 0 +t x = 0.; +t x = 1./x; +t #else +t i = j; +t i = 1/i; +t #endif +t +t return 0; +t} /* test main */ +#endif /* TEST */ //===================================================================== -void FC hello() // initializes envpak, including re library code error exits and - // user exits. +void FC hello() // initializes envpak, including re library code error exits and user exits. // Inits re the floating point and divide by zero interrupts, // and saves the current dir for use at any exit @@ -313,135 +326,134 @@ signal( SIGFPE, // floating point errors, and integer errors (changes 0:0) un // also, our matherr (below) is linked in for C lib math fcn errs. #endif - //--- integer divide by 0 error setup - // _dos_setvect( 0, (void (interrupt *)())iDiv0Err ); // 0: integer divide by - // 0 - // to ease reporting calling location (in which case aborts, no return). */ +//--- integer divide by 0 error setup + // _dos_setvect( 0, (void (interrupt *)())iDiv0Err ); // 0: integer divide by 0 + // to ease reporting calling location (in which case aborts, no return). */ - //--- save current drive/dir to restore at exit - strcpy(cwdSave, xgetdir()); // save current dir (including drive) in static - // for byebye to use at exit -} // hello +//--- save current drive/dir to restore at exit + strcpy(cwdSave,xgetdir()); // save current dir (including drive) in static for byebye to use at exit +} // hello //===================================================================== -void FC byebye( // cleanup and normal or error exit. +void FC byebye( // cleanup and normal or error exit. - int exitCode) // DOS errorlevel or other code to return - // to parent process or caller of subroutine package or DLL. - // 0 means ok; use 2 or more for errors to reserve 1 for - // possible alternate good exit (eg output file unchanged, ) + int exitCode ) // DOS errorlevel or other code to return + // to parent process or caller of subroutine package or DLL. + // 0 means ok; use 2 or more for errors to reserve 1 for + // possible alternate good exit (eg output file unchanged, ) // does not return to caller. { - //--- restore current directory - xchdir(cwdSave, WRN); // restore drive and dir, xiopak.cpp. - // Ok NOP if "". cwdSave set in hello, just above. - cwdSave[0] = '\0'; // clear saved dir eg for when subr pkg recalled. +//--- restore current directory + xchdir( cwdSave, WRN ); // restore drive and dir, xiopak.cpp. + // Ok NOP if "". cwdSave set in hello, just above. + cwdSave[0] = '\0'; // clear saved dir eg for when subr pkg recalled. - throw exitCode; + throw exitCode; -} // byebye +} // byebye //========================================================================== -UINT doControlFP() { -#if defined(_DEBUG) && (CSE_COMPILER == CSE_COMPILER_MSVC) - int cw = _controlfp(0, 0); - cw &= ~(_EM_OVERFLOW | _EM_UNDERFLOW | _EM_ZERODIVIDE); - _controlfp(cw, _MCW_EM); +UINT doControlFP() +{ +#if defined( _DEBUG) && (CSE_COMPILER==CSE_COMPILER_MSVC) + int cw = _controlfp( 0, 0); + cw &= ~(_EM_OVERFLOW | _EM_UNDERFLOW | _EM_ZERODIVIDE); + _controlfp( cw, _MCW_EM); #endif - return 0; -} // doControlFP + return 0; +} // doControlFP //========================================================================== -#if CSE_COMPILER == CSE_COMPILER_MSVC // TODO (MP) Add table for other compilers -INT CDEC matherr( // Handle errors detected in Microsoft/Borland math library +#if CSE_COMPILER==CSE_COMPILER_MSVC // TODO (MP) Add table for other compilers +INT CDEC matherr( // Handle errors detected in Microsoft/Borland math library - struct _exception *x) // Exception info structure provided by MSVC library + struct _exception *x ) // Exception info structure provided by MSVC library -// Calls rmkerr fcns to report error (as a warning), then returns a 1 to prevent -// error reporting out of library. +// Calls rmkerr fcns to report error (as a warning), then returns a 1 to prevent error reporting out of library. { - static WSTABLE /* { SI key, value; } */ table[] = { - {DOMAIN, "domain"}, - {SING, "argument singularity"}, - {OVERFLOW, "overflow"}, - {UNDERFLOW, "underflow"}, - {TLOSS, "total loss of significance"}, - {PLOSS, "partial loss of significance"}, - {32767, "unknown exception type"}}; - - errCrit(PWRN, // display critical msg - "X0104: matherr exception (%s), possible argument values are " - "arg1=%lg arg2=%lg", - lookws(x->type, table), // exception type text - x->arg1, x->arg2); // POSSIBLE arg values; not reliable since - // intrinsic fcns do not set these members - - // limit return value to (float)-able range so no subsequent FPE overflow: - // most math fcn calls in products cast result to float. - - if (x->retval > FHuge) // FHuge = 1.E38, cons.cpp - x->retval = FHuge; - else if (x->retval < -FHuge) - x->retval = -FHuge; - - return 1; // 1 -> consider error corrected, continue -} // matherr +static WSTABLE /* { SI key, value; } */ table[] = +{ + { DOMAIN, "domain" }, + { SING, "argument singularity" }, + { OVERFLOW, "overflow" }, + { UNDERFLOW, "underflow" }, + { TLOSS, "total loss of significance" }, + { PLOSS, "partial loss of significance" }, + { 32767, "unknown exception type" } +}; + + errCrit( PWRN, // display critical msg + "X0104: matherr exception (%s), possible argument values are " + "arg1=%lg arg2=%lg", + lookws( x->type, table), // exception type text + x->arg1, x->arg2); // POSSIBLE arg values; not reliable since intrinsic fcns do not set these members + + // limit return value to (float)-able range so no subsequent FPE overflow: + // most math fcn calls in products cast result to float. + + if (x->retval > FHuge) // FHuge = 1.E38, cons.cpp + x->retval = FHuge; + else if (x->retval < -FHuge ) + x->retval = -FHuge; + + return 1; // 1 -> consider error corrected, continue +} // matherr #endif //========================================================================== -void CDEC fpeErr( // Handle floating point error exceptions - [[maybe_unused]] INT sigfpe, // SIGFPE is passed (currently not used) - INT code) // code indicating error +void CDEC fpeErr( // Handle floating point error exceptions + [[maybe_unused]] INT sigfpe, // SIGFPE is passed (currently not used) + INT code ) // code indicating error // Calls BSG error routines to report error with PABT. // Note: initialization for this (using signal() ) is in hello() (above). { -#if CSE_COMPILER == CSE_COMPILER_MSVC // TODO (MP) Add table for other compilers - static WSTABLE /* { SI key, char *s; } */ table[] = { - {FPE_ZERODIVIDE, "divide by 0"}, - {FPE_OVERFLOW, "overflow"}, - {FPE_UNDERFLOW, "underflow"}, - {FPE_INVALID, "invalid (NAN or infinity)"}, - {FPE_INEXACT, "inexact"}, - {FPE_SQRTNEG, "negative sqrt"}, - {FPE_DENORMAL, "denormal"}, - {FPE_UNEMULATED, "unemulated"}, - {FPE_STACKOVERFLOW, "stack overflow"}, - {FPE_STACKUNDERFLOW, "stack underflow"}, - {32767, "unknown error code"}}; - - // NOTE: CR/TK version contains code which extracts the error address from - // the stack, allowing msg include info about where error occurred. - // Could be adapted for use here if FPE trackdown is a problem. - - // issue message and abort - - errCrit(PABT, // display critical msg - "X0103: floating point exception %d:\n %s", - code, // show code for unknown cases 1-31-94 - lookws(code, table)); +#if CSE_COMPILER==CSE_COMPILER_MSVC // TODO (MP) Add table for other compilers + static WSTABLE /* { SI key, char *s; } */ table[] = + { + { FPE_ZERODIVIDE, "divide by 0" }, + { FPE_OVERFLOW, "overflow" }, + { FPE_UNDERFLOW, "underflow" }, + { FPE_INVALID, "invalid (NAN or infinity)" }, + { FPE_INEXACT, "inexact" }, + { FPE_SQRTNEG, "negative sqrt" }, + { FPE_DENORMAL, "denormal" }, + { FPE_UNEMULATED, "unemulated" }, + { FPE_STACKOVERFLOW, "stack overflow" }, + { FPE_STACKUNDERFLOW, "stack underflow" }, + { 32767, "unknown error code" } + }; + +// NOTE: CR/TK version contains code which extracts the error address from +// the stack, allowing msg include info about where error occurred. +// Could be adapted for use here if FPE trackdown is a problem. + +// issue message and abort + + errCrit( PABT, // display critical msg + "X0103: floating point exception %d:\n %s", + code, // show code for unknown cases 1-31-94 + lookws( code, table)); #endif - // no return - /* DO NOT ATTEMPT TO RETURN and continue program: state of 8087 unknown; - registers probably clobbered in ways that matter. */ -} // fpeErr + // no return + /* DO NOT ATTEMPT TO RETURN and continue program: state of 8087 unknown; + registers probably clobbered in ways that matter. */ +} // fpeErr //--------------------------------------------------------------------------- -int CheckFP(double v, const char *vTag) // check for valid FP value +int CheckFP( double v, const char* vTag) // check for valid FP value // see CHECKFP macro (cnglob.h) // returns 0 if OK, else 1 -{ - const char *t; - if (std::isnan(v)) - t = "NAN"; - else if (!std::isfinite(v)) - t = "Inf"; - else - return 0; - printf("Bad value: %s %s\n", vTag, t); - return 1; -} // ::CheckFP +{ const char* t; + if (std::isnan( v)) + t = "NAN"; + else if (!std::isfinite( v)) + t = "Inf"; + else + return 0; + printf( "Bad value: %s %s\n", vTag, t); + return 1; +} // ::CheckFP //============================================================================ -#if 0 && !defined(__BORLANDC__) // used under microsoft C; borland generates - // floating point error +#if 0 && !defined( __BORLANDC__) // used under microsoft C; borland generates floating point error 0 //========================================================================== 0 void CDEC iDiv0Err( // report integer divide by zero under MSDOS (MicroSoft C) 0 diff --git a/src/nummeth.cpp b/src/nummeth.cpp index 3b0dca783..06dc95d95 100644 --- a/src/nummeth.cpp +++ b/src/nummeth.cpp @@ -7,7 +7,7 @@ /*------------------------------- INCLUDES --------------------------------*/ #include "cnglob.h" -#include "nummeth.h" // decls for this file +#include "nummeth.h" // decls for this file #if 0 // Eigen experiments 3-29-2023 @@ -42,136 +42,145 @@ int FC solveEigen( // Solve a system of equations using Gauss-Jordan eliminatio return 0; } + #endif //============================================================================== -int FC gaussjb( // Solve a system of equations using Gauss-Jordan elimination - - double *a, // square matrix of coefficients. NOTE: this is a - // a C 2D array, NOT a Numerical Recipes "matrix" - int n, // dimension of a -- e.g. a is a[n,n] - // n *MUST BE* <= MAXN (see code) - double *b, // Set of m right hand side vectors for which - // solutions are sought. m can be 0. - int m, // b is b[n][m] - int invflg /*=0*/) // invert flag - if nz, a-inverse is returned in a - // if 0, a is returned garbage (not generating inverse - // faster) +int FC gaussjb( // Solve a system of equations using Gauss-Jordan elimination + + double* a, // square matrix of coefficients. NOTE: this is a + // a C 2D array, NOT a Numerical Recipes "matrix" + int n, // dimension of a -- e.g. a is a[n,n] + // n *MUST BE* <= MAXN (see code) + double* b, // Set of m right hand side vectors for which + // solutions are sought. m can be 0. + int m, // b is b[n][m] + int invflg /*=0*/) // invert flag - if nz, a-inverse is returned in a + // if 0, a is returned garbage (not generating inverse faster) // returns: // -1: failure: n too large // 0: success (a-inverse in place of a (if invflg nz), b-solutions in b ) // 1: matrix is singular { - static const int MAXN = 1000; // max n supported - int ipiv[MAXN]; - int indxr[MAXN]; - int indxc[MAXN]; - - int i, j, ll, irow = 0, icol = 0, l, k; - double f, big, pivinv; - double *arow, *arcol, *arow2; - double *brow, *brcol, *brow2; + static const int MAXN = 1000; // max n supported + int ipiv[ MAXN]; + int indxr[ MAXN]; + int indxc[ MAXN]; - if (n > MAXN) - return -1; + int i, j, ll, irow = 0, icol = 0, l, k; + double f, big, pivinv; + double *arow, *arcol, *arow2; + double *brow, *brcol, *brow2; - memset(ipiv, 0, n * sizeof(int)); + if (n > MAXN) + return -1; - for (i = 0; i < n; i++) /* Main loop */ - { - big = 0.; - arow = a; - for (j = 0; j < n; j++) { - if (*(ipiv + j) != 1) { - arcol = arow; - for (k = 0; k < n; k++) { - if (*(ipiv + k) == 0) { - if ((f = fabs(*arcol)) >= big) { - big = f; - irow = j; - icol = k; - } - } else if (*(ipiv + k) > 1) - return 1; /* singular */ - arcol++; - } - } - arow += n; - } - - /* Now that the pivot point is found we rotate rows */ - *(ipiv + icol) += 1; - arow = a + irow * n; - brow = b + irow * m; - arow2 = a + icol * n; - brow2 = b + icol * m; - if (irow != icol) { - VSwap(arow, n, arow2, n); - VSwap(brow, n, brow2, m); - } - *(indxr + i) = irow; - *(indxc + i) = icol; - - double *arcol2 = arow2 + icol; - if (*arcol2 == 0.) - return 1; /* singular */ - pivinv = 1. / (*arcol2); - *arcol2 = 1.; - arcol2 = arow2; - double *brcol2 = brow2; - /*lint -e564 suppress "variable arcol2 depends on order of eval" */ - for (l = 0; l < n; l++) - *(arcol2++) *= pivinv; - for (l = 0; l < m; l++) - *(brcol2++) *= pivinv; - /*lint +e564 */ - - arow = a; - brow = b; - for (ll = 0; ll < n; ll++) { - if (ll != icol) { - double dum = *(arow + icol); - *(arow + icol) = 0.; - arcol = arow; - arcol2 = arow2; - brcol = brow; - brcol2 = brow2; - /*lint -e564 suppress "variable arcol depends on order of eval"*/ - for (l = 0; l < n; l++) - *arcol++ -= *(arcol2++) * dum; - for (l = 0; l < m; l++) - *brcol++ -= *(brcol2++) * dum; - /*lint +e654 */ - } - arow += n; - brow += m; - } - } /* End of main loop */ + memset( ipiv, 0, n * sizeof( int) ); - if (invflg) - for (l = n - 1; l >= 0; l--) - if ((irow = *(indxr + l)) != (icol = *(indxc + l))) - VSwap(a + irow, n, a + icol, n); + for (i = 0; i < n; i++) /* Main loop */ + { + big = 0.; + arow = a; + for (j = 0; j < n; j++) + { + if (*(ipiv+j) != 1) + { + arcol = arow; + for (k = 0; k < n; k++) + { + if (*(ipiv+k) == 0) + { + if ((f=fabs(*arcol)) >= big) + { + big = f; + irow = j; + icol = k; + } + } + else if (*(ipiv+k) > 1) + return 1; /* singular */ + arcol++; + } + } + arow += n; + } + + /* Now that the pivot point is found we rotate rows */ + *(ipiv+icol) += 1; + arow = a + irow*n; + brow = b + irow*m; + arow2 = a + icol*n; + brow2 = b + icol*m; + if (irow != icol) + { + VSwap( arow, n, arow2, n); + VSwap( brow, n, brow2, m); + } + *(indxr+i) = irow; + *(indxc+i) = icol; + + double* arcol2 = arow2 + icol; + if (*arcol2 == 0.) + return 1; /* singular */ + pivinv = 1./(*arcol2); + *arcol2 = 1.; + arcol2 = arow2; + double* brcol2 = brow2; + /*lint -e564 suppress "variable arcol2 depends on order of eval" */ + for (l = 0; l < n; l++) + *(arcol2++) *= pivinv; + for (l = 0; l < m; l++) + *(brcol2++) *= pivinv; + /*lint +e564 */ + + arow = a; + brow = b; + for (ll = 0; ll < n; ll++) + { + if (ll != icol) + { + double dum = *(arow+icol); + *(arow+icol) = 0.; + arcol = arow; + arcol2 = arow2; + brcol = brow; + brcol2 = brow2; + /*lint -e564 suppress "variable arcol depends on order of eval"*/ + for (l = 0; l < n; l++) + *arcol++ -= *(arcol2++)*dum; + for (l = 0; l < m; l++) + *brcol++ -= *(brcol2++)*dum; + /*lint +e654 */ + } + arow += n; + brow += m; + } + } /* End of main loop */ + + if ( invflg ) + for (l = n-1; l >= 0; l--) + if ( (irow = *(indxr+l)) != (icol = *(indxc+l)) ) + VSwap( a+irow, n, a+icol, n); - return 0; + return 0; } //----------------------------------------------------------------------------- -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. - 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 +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. + 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 // convergence = f - eps(Lo) <= f1 <= f + eps @@ -183,50 +192,49 @@ int actualSecant( // find x given f(x) (secant method) // >0= failed to converge, returned value = # of iterations // <0= 0 slope encountered, returned value = -# of iterations { - double fHi = f + eps; - double fLo = f - (epsLo >= 0. ? epsLo : eps); - - if (f1 == DBL_MIN) - f1 = (*pFunc)(pO, x1); + double fHi = f + eps; + double fLo = f - (epsLo >= 0. ? epsLo : eps); - if (f1 <= fHi && f1 >= fLo) // if 1st guess good - return 0; // success: don't do *pFunc( x2) - // (side effects) + if (f1 == DBL_MIN) + f1 = (*pFunc)( pO, x1); - if (f2 == DBL_MIN) - f2 = (*pFunc)(pO, x2); + if (f1 <= fHi && f1 >= fLo) // if 1st guess good + return 0; // success: don't do *pFunc( x2) + // (side effects) - if (fabs(f - f1) > fabs(f - f2)) // make point 1 the closer - { - std::swap(x1, x2); - std::swap(f1, f2); - } + if (f2 == DBL_MIN) + f2 = (*pFunc)( pO, x2); - int i; - for (i = 0; ++i < 20;) // iterate to refine solution - { - 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(f-f1) > fabs(f-f2)) // make point 1 the closer + { double swap; + swap = x1; x1=x2; x2=swap; + swap = f1; f1=f2; f2=swap; } - if (fabs(f1 - f2) < 1.e-20) // if slope is 0 - { - i = -i; // tell caller - break; + int i; + for (i=0; ++i < 20; ) // iterate to refine solution + { 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 + 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 } - - 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 - } - return i; -} // ::secant + return i; +} // ::secant //----------------------------------------------------------------------------- int secant( // screen secant success; report calcuation if failure @@ -279,7 +287,7 @@ int secant( // screen secant success; report calcuation if failure int i; for (i = 0; ++i < 20;) // iterate to refine solution { - warn("begin iter {%i}", i + 1); + warn("begin iter {%i} ----------", i + 1); warn("before: x1 = {%d}, x2 = {%d}, f1 = {%d}, f2 = {%d}", x1, x2, f1, f2); if (f1 <= fHi && f1 >= fLo) { @@ -296,7 +304,7 @@ int secant( // screen secant success; report calcuation if failure } double xN = x1 + (x2 - x1) * (f - f1) / (f2 - f1); - warn("during: xN = {%d}", xN); + warn("xN = {%d}", xN); // secant method: new guess assuming local linearity. x2 = x1; // replace older point @@ -309,209 +317,232 @@ int secant( // screen secant success; report calcuation if failure 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 - // may CHANGE x re domain limits etc. - void *pO, // pointer passed to *pFunc, typ object pointer - double f, // f( x) value sought - double eps, // convergence tolerance, hi- or both sides - double &x1, // x 1st guess, - // returned with result - double xMin, // minimum value of x - double xMax) // maximum value of x + //----------------------------------------------------------------------------- +int regula( // find x given f(x) (regula-falsi method) + double (*pFunc)(void* pO, double& x), + // function under investigation; note that it + // may CHANGE x re domain limits etc. + void* pO, // pointer passed to *pFunc, typ object pointer + double f, // f( x) value sought + double eps, // convergence tolerance, hi- or both sides + double& x1, // x 1st guess, + // returned with result + double xMin, // minimum value of x + double xMax) // maximum value of x // returns result code, x1 and f1 always best known solution // 0: success // >0= failed to converge, returned value = # of iterations // <0= bad min / max { - double fHi = f + eps; - double fLo = f - eps; - - double f1 = (*pFunc)(pO, x1); + double fHi = f + eps; + double fLo = f - eps; - if (f1 <= fHi && f1 >= fLo) // if 1st guess good - return 0; // success: don't do *pFunc( x2) - // (side effects) + double f1 = (*pFunc)(pO, x1); - double fMin = (*pFunc)(pO, xMin); + if (f1 <= fHi && f1 >= fLo) // if 1st guess good + return 0; // success: don't do *pFunc( x2) + // (side effects) - if (fMin <= fHi && fMin >= fLo) // if 1st guess good - { - x1 = xMin; - return 0; // success: don't do *pFunc( x2) - // (side effects) - } + double fMin = (*pFunc)(pO, xMin); - double fMax = (*pFunc)(pO, xMax); + if (fMin <= fHi && fMin >= fLo) // if 1st guess good + { + x1 = xMin; + return 0; // success: don't do *pFunc( x2) + // (side effects) + } - if (fMax <= fHi && fMax >= fLo) // if 1st guess good - { - x1 = xMax; - return 0; // success: don't do *pFunc( x2) - // (side effects) - } + double fMax = (*pFunc)(pO, xMax); - // Error if fMin and fMax are the same sign - if ((fMin - f) * (fMax - f) > 0.) { - return -1; - } + if (fMax <= fHi && fMax >= fLo) // if 1st guess good + { + x1 = xMax; + return 0; // success: don't do *pFunc( x2) + // (side effects) + } - if ((f1 - f) * (fMin - f) < 0.) { - xMax = x1; - fMax = f1; - } else { - xMin = x1; - fMin = f1; - } + // Error if fMin and fMax are the same sign + if ((fMin - f) * (fMax - f) > 0.) + { + return -1; + } - int i; - for (i = 0; ++i < 20;) // iterate to refine solution - { - x1 = xMin - (xMax - xMin) * (fMin - f) / (fMax - fMin); - f1 = (*pFunc)(pO, x1); + if ((f1 - f) * (fMin - f) < 0.) + { + xMax = x1; + fMax = f1; + } + else + { + xMin = x1; + fMin = f1; + } - if (f1 <= fHi && f1 >= fLo) { - i = 0; // success - break; // done; last *pFunc call ... - // 1st iteration: *pFunc( x2) + swap - // >1st iteration: *pFunc( x1) below - } + int i; + for (i = 0; ++i < 20; ) // iterate to refine solution + { + x1 = xMin - (xMax - xMin) * (fMin - f) / (fMax - fMin); + f1 = (*pFunc)(pO, x1); + + if (f1 <= fHi && f1 >= fLo) + { + i = 0; // success + break; // done; last *pFunc call ... + // 1st iteration: *pFunc( x2) + swap + // >1st iteration: *pFunc( x1) below + } + + if ((f1 - f) * (fMin - f) < 0.) + { + xMax = x1; + fMax = f1; + } + else + { + xMin = x1; + fMin = f1; + } - if ((f1 - f) * (fMin - f) < 0.) { - xMax = x1; - fMax = f1; - } else { - xMin = x1; - fMin = f1; - } - } - return i; -} // ::regula + } + return i; +} // ::regula //============================================================================= /////////////////////////////////////////////////////////////////////////////// // class DGRAPH -- directed graph //============================================================================= -bool DGRAPH::dg_TopologicalSort( // topological sort - std::vector &vSorted) // sorted result ("bottom up" order) - // vSorted[ 0] = deepest vertex - // returns true on success (vSorted filled) -// else false iff cyclic (vSorted[ 0] set to 1st offending vertex) +bool DGRAPH::dg_TopologicalSort( // topological sort + std::vector< int>& vSorted) // sorted result ("bottom up" order) + // vSorted[ 0] = deepest vertex + // returns true on success (vSorted filled) + // else false iff cyclic (vSorted[ 0] set to 1st offending vertex) { - vSorted.clear(); - dg_status.assign(dg_nV, 0); + vSorted.clear(); + dg_status.assign(dg_nV, 0); - // Depth-first search starting from each vertex - for (int iV = 0; iV < dg_nV; iV++) { - if (!dg_TopologicalSortDFS(iV, vSorted)) - return false; // cyclic detected, abandon - } - return true; -} // DGRAPH::dg_TopologicalSort + // Depth-first search starting from each vertex + for (int iV = 0; iV < dg_nV; iV++) + { if (!dg_TopologicalSortDFS(iV, vSorted)) + return false; // cyclic detected, abandon + } + return true; +} // DGRAPH::dg_TopologicalSort //----------------------------------------------------------------------------- bool DGRAPH::dg_TopologicalSortDFS( - int iV, // starting vertex - std::vector &vSorted) // returned: updated sorted list - // recursive helper for dg_TopologicalSort() - // returns true iff success - // false if cyclic + int iV, // starting vertex + std::vector< int>& vSorted) // returned: updated sorted list + // recursive helper for dg_TopologicalSort() + // returns true iff success + // false if cyclic { - if (dg_status[iV] == 2) - return true; // already seen - if (dg_status[iV] == 1) { // cyclic: put offending vertex into vSorted[ 0] - vSorted.clear(); - vSorted.push_back(iV); - return false; - } + if (dg_status[iV] == 2) + return true; // already seen + if (dg_status[iV] == 1) + { // cyclic: put offending vertex into vSorted[ 0] + vSorted.clear(); + vSorted.push_back(iV); + return false; + } - dg_status[iV] = 1; // active vertex + dg_status[iV] = 1; // active vertex - // Recurs for all the vertices adjacent to this vertex - // (depth first) - for (auto tV : dg_edges[iV]) { - if (!dg_TopologicalSortDFS(tV, vSorted)) - return false; // cyclic somewhere below here - } + // Recurs for all the vertices adjacent to this vertex + // (depth first) + for (auto tV : dg_edges[iV]) + { + if (!dg_TopologicalSortDFS(tV, vSorted)) + return false; // cyclic somewhere below here + } - vSorted.push_back(iV); - dg_status[iV] = 2; // seen - return true; + vSorted.push_back(iV); + dg_status[iV] = 2; // seen + return true; -} // DGRAPH::dg_topologicalSortDFS +} // DGRAPH::dg_topologicalSortDFS //------------------------------------------------------------------------------ -bool DGRAPH::dg_CountRefs( // # of refs to each vertex in tree - int ivRoot, // root vertex - std::vector &vRefCounts) -// WHY: allows identifying duplicate references in a specific subtree -// returns true iff success (vRefCounts[ iV] = refs/visits to each vertex) -// else false (cyclic) +bool DGRAPH::dg_CountRefs( // # of refs to each vertex in tree + int ivRoot, // root vertex + std::vector< int>& vRefCounts) + // WHY: allows identifying duplicate references in a specific subtree + // returns true iff success (vRefCounts[ iV] = refs/visits to each vertex) + // else false (cyclic) { - vRefCounts.assign(dg_nV, 0); - dg_status.assign(dg_nV, 0); + vRefCounts.assign(dg_nV, 0); + dg_status.assign(dg_nV, 0); - return dg_CountRefsDFS(ivRoot, vRefCounts); -} // DGRAPH::dg_CountRefs + return dg_CountRefsDFS(ivRoot, vRefCounts); +} // DGRAPH::dg_CountRefs //----------------------------------------------------------------------------- -bool DGRAPH::dg_CountRefsDFS(int iV, // current vertex - std::vector &vRefCounts) { - if (dg_status[iV] == 1) { // cyclic: put offending vertex into vRefCounts[ 0] - vRefCounts.clear(); - vRefCounts.push_back(iV); - return false; - } +bool DGRAPH::dg_CountRefsDFS( + int iV, // current vertex + std::vector< int>& vRefCounts) +{ + if (dg_status[iV] == 1) + { // cyclic: put offending vertex into vRefCounts[ 0] + vRefCounts.clear(); + vRefCounts.push_back(iV); + return false; + } - ++vRefCounts[iV]; // count current + ++vRefCounts[iV]; // count current - for (auto tV : dg_edges[iV]) { - if (!dg_CountRefsDFS(tV, vRefCounts)) - return false; // cyclic somewhere below here - } - return true; -} // DGRAPH::dg_CountRefsDFS + for (auto tV : dg_edges[iV]) + { + if (!dg_CountRefsDFS(tV, vRefCounts)) + return false; // cyclic somewhere below here + } + return true; +} // DGRAPH::dg_CountRefsDFS //============================================================================= -template class PWLFUNC { +template< typename T, size_t NI> class PWLFUNC +{ public: - PWLFUNC(T (*pFunc)(T x), T xMin, T xMax); - T operator()(T x) { - int i = int(x / NI); - if (i < 0 || i >= NI) - return (*pwl_pFunc)(x); - else - return pwl_a[i] + pwl_b[i] * x; - } + PWLFUNC(T (*pFunc)(T x), T xMin, T xMax); + T operator()( T x) + { + int i = int(x / NI); + if (i < 0 || i >= NI) + return (*pwl_pFunc)(x); + else + return pwl_a[i] + pwl_b[i] * x; + } private: - T (*pwl_pFunc)(T x); - T pwl_xMin; - T pwl_xMax; - T pwl_a[NI]; - T pwl_b[NI]; + T (*pwl_pFunc)(T x); + T pwl_xMin; + T pwl_xMax; + T pwl_a[NI]; + T pwl_b[NI]; + + void pwl_Setup(); - void pwl_Setup(); }; //------------------------------------------------------------------------------- -template -PWLFUNC::PWLFUNC(T (*pFunc)(T x), T xMin, T xMax) - : pwl_pFunc(pFunc), pwl_xMin(xMin), pwl_xMax(xMax) { - pwl_Setup(); -} // PWLFUNC::PWLFUNC +template< typename T, size_t NI> PWLFUNC< T, NI>::PWLFUNC( + T(*pFunc)(T x), T xMin, T xMax) + : pwl_pFunc( pFunc), pwl_xMin( xMin), pwl_xMax( xMax) +{ + pwl_Setup(); +} // PWLFUNC::PWLFUNC //------------------------------------------------------------------------------- -template void PWLFUNC::pwl_Setup() { - T xStep = (pwl_xMax - pwl_xMin) / (NI - 1); - for (int i = 0; i < NI; i++) { - T x = pwl_xMin + i * xStep; - pwl_a[i] = (*pwl_pFunc)(x); - } +template< typename T, size_t NI> void PWLFUNC< T, NI>::pwl_Setup() +{ + T xStep = (pwl_xMax - pwl_xMin) / (NI-1); + for (int i = 0; i < NI; i++) + { + T x = pwl_xMin + i*xStep; + pwl_a[i] = (*pwl_pFunc)(x); + } + + } // =============================================================================== -static float pow33(float x) { return powf(x, 1.f / 3.f); } -static PWLFUNC powk(pow33, 0.f, 10.f); +static float pow33(float x) { return powf(x, 1.f/3.f); } +static PWLFUNC< float, 11> powk(pow33,0.f, 10.f); #if 0 void test() From 770018e7c0189b945a22598bf8df272a93699c1a Mon Sep 17 00:00:00 2001 From: Phil Ahrenkiel Date: Mon, 8 Apr 2024 13:08:11 -0600 Subject: [PATCH 08/21] Add nummeth unit tests. --- CMakeLists.txt | 1 + src/nummeth.cpp | 3 ++- src/nummeth.h | 1 - test/unit/CMakeLists.txt | 1 + test/unit/nummeth.unit.cpp | 55 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 test/unit/nummeth.unit.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1603b7947..d083820fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,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/nummeth.cpp b/src/nummeth.cpp index 06dc95d95..383d76731 100644 --- a/src/nummeth.cpp +++ b/src/nummeth.cpp @@ -317,7 +317,7 @@ int secant( // screen secant success; report calcuation if failure 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 @@ -552,4 +552,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..b4b9bad03 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -3,6 +3,7 @@ set(source strpak.unit.cpp xiopak.unit.cpp cvpak.unit.cpp + nummeth.unit.cpp ${CSE_SOURCE_DIR}/src/libstubs.cpp ) diff --git a/test/unit/nummeth.unit.cpp b/test/unit/nummeth.unit.cpp new file mode 100644 index 000000000..bfa3520ba --- /dev/null +++ b/test/unit/nummeth.unit.cpp @@ -0,0 +1,55 @@ +// nummeth.unit.cpp - test uses of numerical-method functions + +#include "gtest/gtest.h" + +#include "cnglob.h" +#include "cvpak.h" +#include "nummeth.h" + +constexpr double a = 2., xi = 3.; + +// linear function +static double contin_func(void *no_obj, double &x) { + + double f = a * (x - xi); + return f; +} + +// inverse linear function +static double discontin_func(void *no_obj, double &x) { + double f = DBL_MAX; + if (abs(x - xi) > 0.0001) { + f = a / (x - xi); + } + return f; +} + +TEST(nummeth, secant_test) { + + { // solution of linear function + double x1 = 3.5, x2 = 4.5; + double f1 = DBL_MIN, f2 = DBL_MIN; + double fTarg = 2.; + + int ret = secant(contin_func, NULL, fTarg, .0001 * fTarg, x1, f1, // x1, f1 + x2, f2); // x2, f2 + + EXPECT_EQ(ret, 0) << "secant solution of continuous function failed."; + EXPECT_NEAR(x1, fTarg / a + xi, 1.e-12) + << "expected solution of continuous function not found."; + } + + { // solution of inverse linear function + double x1 = 3.5, x2 = 4.5; + double f1 = DBL_MIN, f2 = DBL_MIN; + double fTarg = 2.; + + int ret = + secant(discontin_func, NULL, fTarg, .0001 * fTarg, x1, f1, // x1, f1 + x2, f2); // x2, f2 + + EXPECT_EQ(ret, 0) << "secant solution of discontinuous function failed."; + EXPECT_NEAR(x1, a / fTarg + xi, 1.e-3) + << "expected solution of discontinuous function not found."; + } +} From 1ebc96ef23cd8debc3ed9257ebf407827058e4cf Mon Sep 17 00:00:00 2001 From: Phil Ahrenkiel Date: Mon, 8 Apr 2024 14:51:39 -0600 Subject: [PATCH 09/21] Invert twice. --- test/unit/nummeth.unit.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/unit/nummeth.unit.cpp b/test/unit/nummeth.unit.cpp index bfa3520ba..35c540ee9 100644 --- a/test/unit/nummeth.unit.cpp +++ b/test/unit/nummeth.unit.cpp @@ -15,13 +15,13 @@ static double contin_func(void *no_obj, double &x) { return f; } -// inverse linear function +// inverted linear function static double discontin_func(void *no_obj, double &x) { double f = DBL_MAX; if (abs(x - xi) > 0.0001) { - f = a / (x - xi); + f = 1. / (a * (x - xi)); } - return f; + return 1./ f; } TEST(nummeth, secant_test) { @@ -35,11 +35,12 @@ TEST(nummeth, secant_test) { x2, f2); // x2, f2 EXPECT_EQ(ret, 0) << "secant solution of continuous function failed."; - EXPECT_NEAR(x1, fTarg / a + xi, 1.e-12) + double xExpected = fTarg / a + xi; + EXPECT_NEAR(x1, xExpected, 1.e-12) << "expected solution of continuous function not found."; } - { // solution of inverse linear function + { // solution of inverted linear function double x1 = 3.5, x2 = 4.5; double f1 = DBL_MIN, f2 = DBL_MIN; double fTarg = 2.; @@ -49,7 +50,8 @@ TEST(nummeth, secant_test) { x2, f2); // x2, f2 EXPECT_EQ(ret, 0) << "secant solution of discontinuous function failed."; - EXPECT_NEAR(x1, a / fTarg + xi, 1.e-3) + double xExpected = fTarg / a + xi; + EXPECT_NEAR(x1, xExpected, 1.e-12) << "expected solution of discontinuous function not found."; } } From 7f21148d00be4f0e34266e8e35c11d6b1a7fdccc Mon Sep 17 00:00:00 2001 From: Phil Ahrenkiel Date: Tue, 9 Apr 2024 18:06:47 -0600 Subject: [PATCH 10/21] Review comments. --- src/cnglob.h | 1 - src/cnloads.cpp | 2 ++ src/cvpak.cpp | 10 +++++++--- src/nummeth.cpp | 8 ++++---- test/unit/cvpak.unit.cpp | 6 +++++- 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/cnglob.h b/src/cnglob.h index 9423834fe..94140f00b 100644 --- a/src/cnglob.h +++ b/src/cnglob.h @@ -271,7 +271,6 @@ constexpr double tAbs0F // 0 F in Rankine #else = 459.67; #endif -constexpr double tol_tF = 1.e-12; // temperature tolerance (degF) /*------------------------------- math -------------------------------------*/ // error handling diff --git a/src/cnloads.cpp b/src/cnloads.cpp index 5573a8415..60780f432 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 -------------------------------*/ +constexpr double tol_tF = 1.e-12; // temperature tolerance (degF) /*------------------------ The MAIN EQUATION story ------------------------*/ // rob 12-89 prelim diff --git a/src/cvpak.cpp b/src/cvpak.cpp index 694a4e5b7..8d0ddf959 100644 --- a/src/cvpak.cpp +++ b/src/cvpak.cpp @@ -297,6 +297,10 @@ p break; case DTDBL: val = *(double *)data; + if (std::isnan(val)) { + strcpy(str, "nan"); + break; + } goto valValue; #ifdef DTPERCENT // put back in cndtypes.def to restore code, 12-3-91 @@ -329,6 +333,7 @@ p break; val = *(float*)data; // conver float value to print to double } valValue: // double, [percent] join here + val = cvIntoEx( val, units); // convert value to ext units #ifdef FMTPVMASK p wsign = !(pv==FMTPVNULL && val >= 0.); // sign width @@ -528,9 +533,8 @@ p Cvnchars = sprintf( str, sif[lj][ipv], wid, ppos, 0); for (i = 0; ; i++) { // more robust to give up HERE if i too big ?? 10-88 rob - if ((fabs(val) < maxfit // if now might fit width - && cvdd( mfw-1, dfw)) || // format it and see - std::isnan(val)) // guard against inf loop + if (fabs(val) < maxfit // if now might fit width + && cvdd( mfw-1, dfw)) // format it and see break; // if now ok val /= 1000.; // divide by 1000 and bump i till it works } diff --git a/src/nummeth.cpp b/src/nummeth.cpp index 383d76731..7884c102d 100644 --- a/src/nummeth.cpp +++ b/src/nummeth.cpp @@ -237,7 +237,7 @@ int actualSecant( // find x given f(x) (secant method) } // ::secant //----------------------------------------------------------------------------- -int secant( // screen secant success; report calcuation if failure +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 @@ -252,14 +252,14 @@ int secant( // screen secant success; report calcuation if failure double epsLo /*=-1.*/) // lo-side convergence tolerance { - double x1_prev = x1; - double f1_prev = f1; + 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; + x1 = x1_prev; // secant (above) failed. Restore values and repeat with output f1 = f1_prev; warn("secant failed; target = {%d}", f); diff --git a/test/unit/cvpak.unit.cpp b/test/unit/cvpak.unit.cpp index 1c8ff7cdd..87388e6fb 100644 --- a/test/unit/cvpak.unit.cpp +++ b/test/unit/cvpak.unit.cpp @@ -66,6 +66,10 @@ TEST(cvpak, output_convert) // Returns pointer to result in Tmpstr. // Also sets global Cvnchars to the number of characters placed in str (not incl. '\0'). #endif - + { + double y = NAN; + str = cvin2s(&y, DTDBL, UNNONE, 10, 0, 0); + EXPECT_STREQ(str, "nan"); + } } From 3ec8e2ddff92ea496c0ce952d8574c102064cbad Mon Sep 17 00:00:00 2001 From: Phil Ahrenkiel Date: Tue, 23 Apr 2024 08:19:23 -0600 Subject: [PATCH 11/21] Make static. --- src/cnloads.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cnloads.cpp b/src/cnloads.cpp index 60780f432..40261f9f2 100644 --- a/src/cnloads.cpp +++ b/src/cnloads.cpp @@ -51,7 +51,7 @@ static RC loadsSurfaces( BOO subhrly); static RC loadsXFans(); /*------------------------------- CONSTANTS -------------------------------*/ -constexpr double tol_tF = 1.e-12; // temperature tolerance (degF) +static constexpr double tol_tF = 1.e-12; // temperature tolerance (degF) /*------------------------ The MAIN EQUATION story ------------------------*/ // rob 12-89 prelim From f61b83c890d016cdc2b06eea04fe20e99720b08f Mon Sep 17 00:00:00 2001 From: Neal Kruis Date: Wed, 1 May 2024 15:33:20 -0600 Subject: [PATCH 12/21] Clean ups per code review conversation. Warning string still needs to be compiled separately. --- src/cvpak.cpp | 9 +++-- src/nummeth.cpp | 10 ++--- test/unit/CMakeLists.txt | 2 + test/unit/cvpak.unit.cpp | 39 ++++++++++++++---- test/unit/nummeth.unit.cpp | 82 +++++++++++++++++++++++++++----------- test/unit/xiopak.unit.cpp | 7 ---- 6 files changed, 102 insertions(+), 47 deletions(-) diff --git a/src/cvpak.cpp b/src/cvpak.cpp index 8d0ddf959..1b050383b 100644 --- a/src/cvpak.cpp +++ b/src/cvpak.cpp @@ -297,10 +297,6 @@ p break; case DTDBL: val = *(double *)data; - if (std::isnan(val)) { - strcpy(str, "nan"); - break; - } goto valValue; #ifdef DTPERCENT // put back in cndtypes.def to restore code, 12-3-91 @@ -334,6 +330,11 @@ p break; } 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 diff --git a/src/nummeth.cpp b/src/nummeth.cpp index 7884c102d..ad52ec27a 100644 --- a/src/nummeth.cpp +++ b/src/nummeth.cpp @@ -262,8 +262,8 @@ int secant( // screen secant success (see above); report calculation if failure x1 = x1_prev; // secant (above) failed. Restore values and repeat with output f1 = f1_prev; - warn("secant failed; target = {%d}", f); - warn("initial: x1 = {%d}, x2 = {%d}, f1 = {%d}, f2 = {%d}", x1, x2, f1, f2); + warn("secant failed; target = {%g}", f); + warn("initial: x1 = {%g}, x2 = {%g}, f1 = {%g}, f2 = {%g}", x1, x2, f1, f2); double fHi = f + eps; double fLo = f - (epsLo >= 0. ? epsLo : eps); @@ -288,7 +288,7 @@ int secant( // screen secant success (see above); report calculation if failure for (i = 0; ++i < 20;) // iterate to refine solution { warn("begin iter {%i} ----------", i + 1); - warn("before: x1 = {%d}, x2 = {%d}, f1 = {%d}, f2 = {%d}", x1, x2, f1, f2); + warn("before: x1 = {%g}, x2 = {%g}, f1 = {%g}, f2 = {%g}", x1, x2, f1, f2); if (f1 <= fHi && f1 >= fLo) { i = 0; // success @@ -304,7 +304,7 @@ int secant( // screen secant success (see above); report calculation if failure } double xN = x1 + (x2 - x1) * (f - f1) / (f2 - f1); - warn("xN = {%d}", xN); + warn("xN = {%g}", xN); // secant method: new guess assuming local linearity. x2 = x1; // replace older point @@ -312,7 +312,7 @@ int secant( // screen secant success (see above); report calculation if failure x1 = xN; f1 = (*pFunc)(pO, x1); // new value - warn("after: x1 = {%d}, x2 = {%d}, f1 = {%d}, f2 = {%d}", x1, x2, f1, f2); + warn("after: x1 = {%g}, x2 = {%g}, f1 = {%g}, f2 = {%g}", x1, x2, f1, f2); } return i; } // ::secant diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index b4b9bad03..272212cc7 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -5,6 +5,8 @@ set(source cvpak.unit.cpp nummeth.unit.cpp ${CSE_SOURCE_DIR}/src/libstubs.cpp + ${CSE_BINARY_DIR}/src/untab.cpp + ${CSE_BINARY_DIR}/src/dttab.cpp ) list(APPEND source ${cse_common_source}) diff --git a/test/unit/cvpak.unit.cpp b/test/unit/cvpak.unit.cpp index 87388e6fb..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 @@ -66,10 +96,5 @@ TEST(cvpak, output_convert) // Returns pointer to result in Tmpstr. // Also sets global Cvnchars to the number of characters placed in str (not incl. '\0'). #endif - { - double y = NAN; - str = cvin2s(&y, DTDBL, UNNONE, 10, 0, 0); - EXPECT_STREQ(str, "nan"); - } } diff --git a/test/unit/nummeth.unit.cpp b/test/unit/nummeth.unit.cpp index 35c540ee9..0d88efb8a 100644 --- a/test/unit/nummeth.unit.cpp +++ b/test/unit/nummeth.unit.cpp @@ -6,52 +6,86 @@ #include "cvpak.h" #include "nummeth.h" -constexpr double a = 2., xi = 3.; - // linear function -static double contin_func(void *no_obj, double &x) { - - double f = a * (x - xi); - return f; +static double linear_func(void *, double &x) { + return x; } -// inverted linear function -static double discontin_func(void *no_obj, double &x) { +// inverse function +static double inverse_func(void *, double &x) { double f = DBL_MAX; - if (abs(x - xi) > 0.0001) { - f = 1. / (a * (x - xi)); + if (abs(x) > 0.0001) { + f = 1. / x; } - return 1./ f; + return f; } TEST(nummeth, secant_test) { + double eps = .0001; + { // solution of linear function - double x1 = 3.5, x2 = 4.5; + double x1 = 1., x2 = 3.; double f1 = DBL_MIN, f2 = DBL_MIN; double fTarg = 2.; - int ret = secant(contin_func, NULL, fTarg, .0001 * fTarg, x1, f1, // x1, f1 + int ret = secant(linear_func, NULL, fTarg, eps * fTarg, x1, f1, // x1, f1 x2, f2); // x2, f2 - EXPECT_EQ(ret, 0) << "secant solution of continuous function failed."; - double xExpected = fTarg / a + xi; - EXPECT_NEAR(x1, xExpected, 1.e-12) - << "expected solution of continuous function not found."; + 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 inverted linear function - double x1 = 3.5, x2 = 4.5; + { // solution of inverse function + double x1 = 0.01, x2 = 1.0; double f1 = DBL_MIN, f2 = DBL_MIN; double fTarg = 2.; int ret = - secant(discontin_func, NULL, fTarg, .0001 * fTarg, x1, f1, // x1, f1 + 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 discontinuous function failed."; - double xExpected = fTarg / a + xi; - EXPECT_NEAR(x1, xExpected, 1.e-12) - << "expected solution of discontinuous function not found."; + 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 From cab3aa32794379b885abef9ed1ed12f8fd5818d1 Mon Sep 17 00:00:00 2001 From: Phil Ahrenkiel Date: Sat, 4 May 2024 18:01:04 -0600 Subject: [PATCH 13/21] Include fmt in cnglob.h --- src/CMakeLists.txt | 1 + src/RCDEF/CMakeLists.txt | 2 +- src/cnglob.h | 1 + src/cvpak.cpp | 56 ++++++++++++++++++---------------------- src/nummeth.cpp | 16 +++++++----- 5 files changed, 37 insertions(+), 39 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dcdfa4e86..000478aa5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -254,6 +254,7 @@ if(USE_BINRES) endif() set(libs + fmt glad penumbra glfw diff --git a/src/RCDEF/CMakeLists.txt b/src/RCDEF/CMakeLists.txt index 44e9f72e6..4e72d4217 100644 --- a/src/RCDEF/CMakeLists.txt +++ b/src/RCDEF/CMakeLists.txt @@ -25,4 +25,4 @@ add_executable(RCDEF ${source} ${headers}) target_compile_features(RCDEF PRIVATE cxx_std_17) target_include_directories(RCDEF PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" "${CSE_SOURCE_DIR}/src") target_compile_definitions(RCDEF PRIVATE NODTYPES) -target_link_libraries(RCDEF PRIVATE cse_common_interface) +target_link_libraries(RCDEF PRIVATE cse_common_interface fmt) diff --git a/src/cnglob.h b/src/cnglob.h index 94140f00b..c8eb731ae 100644 --- a/src/cnglob.h +++ b/src/cnglob.h @@ -480,6 +480,7 @@ typedef USI PSOP; // type for pseudo-code opcodes -- used in sevaral .cpp files namespace Pumbra { class Penumbra; } namespace Kiva { class Instance; class Aggregator; class Foundation; } namespace Btwxt { class RegularGridInterpolator; } +#include #ifdef WINorDLL // control of scattered code for returning file names used via a cse() argument. diff --git a/src/cvpak.cpp b/src/cvpak.cpp index 1b050383b..4ea03c28d 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 @@ -329,12 +329,6 @@ p break; val = *(float*)data; // conver float value to print to double } 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 @@ -376,7 +370,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 @@ -449,12 +443,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 @@ -477,12 +471,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 @@ -501,7 +495,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) @@ -515,7 +509,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 @@ -534,8 +528,8 @@ p Cvnchars = sprintf( str, sif[lj][ipv], wid, ppos, 0); for (i = 0; ; i++) { // more robust to give up HERE if i too big ?? 10-88 rob - if (fabs(val) < maxfit // if now might fit width - && cvdd( mfw-1, dfw)) // format it and see + if ( fabs(val) < maxfit // if now might fit width + && cvdd( mfw-1, dfw) ) // format it and see break; // if now ok val /= 1000.; // divide by 1000 and bump i till it works } @@ -587,7 +581,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 @@ -596,7 +590,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. @@ -607,8 +601,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. @@ -680,7 +674,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 = @@ -722,7 +716,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 @@ -767,7 +761,7 @@ x } DTDBL, UNNONE, // no units mfw-1, // save a column for ' - fmt ); + fmtv ); *(str+Cvnchars++) = '\''; // add foot mark ' to end *(str+Cvnchars) = '\0'; } @@ -815,7 +809,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 @@ -857,7 +851,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. @@ -900,7 +894,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 @@ -911,7 +905,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 ad52ec27a..f1675a901 100644 --- a/src/nummeth.cpp +++ b/src/nummeth.cpp @@ -5,6 +5,7 @@ // nummeth.cpp -- numerical method functions /*------------------------------- INCLUDES --------------------------------*/ + #include "cnglob.h" #include "nummeth.h" // decls for this file @@ -262,9 +263,9 @@ int secant( // screen secant success (see above); report calculation if failure x1 = x1_prev; // secant (above) failed. Restore values and repeat with output f1 = f1_prev; - warn("secant failed; target = {%g}", f); - warn("initial: x1 = {%g}, x2 = {%g}, f1 = {%g}, f2 = {%g}", x1, x2, f1, f2); - + auto msg = fmt::format("secant failed; target = {%g}", f); + msg += fmt::format("initial: x1 = {%g}, x2 = {%g}, f1 = {%g}, f2 = {%g}", x1, x2, f1, f2); + double fHi = f + eps; double fLo = f - (epsLo >= 0. ? epsLo : eps); @@ -287,8 +288,8 @@ int secant( // screen secant success (see above); report calculation if failure int i; for (i = 0; ++i < 20;) // iterate to refine solution { - warn("begin iter {%i} ----------", i + 1); - warn("before: x1 = {%g}, x2 = {%g}, f1 = {%g}, f2 = {%g}", x1, x2, f1, f2); + msg += fmt::format("begin iter {%i} ----------", i + 1); + msg += fmt::format("before: x1 = {%g}, x2 = {%g}, f1 = {%g}, f2 = {%g}", x1, x2, f1, f2); if (f1 <= fHi && f1 >= fLo) { i = 0; // success @@ -304,7 +305,7 @@ int secant( // screen secant success (see above); report calculation if failure } double xN = x1 + (x2 - x1) * (f - f1) / (f2 - f1); - warn("xN = {%g}", xN); + msg += fmt::format("xN = {%g}", xN); // secant method: new guess assuming local linearity. x2 = x1; // replace older point @@ -312,8 +313,9 @@ int secant( // screen secant success (see above); report calculation if failure x1 = xN; f1 = (*pFunc)(pO, x1); // new value - warn("after: x1 = {%g}, x2 = {%g}, f1 = {%g}, f2 = {%g}", x1, x2, f1, f2); + msg += fmt::format("after: x1 = {%g}, x2 = {%g}, f1 = {%g}, f2 = {%g}", x1, x2, f1, f2); } + warn(msg.c_str()); return i; } // ::secant From 152841583b72e65229dd985d35b027988d118e61 Mon Sep 17 00:00:00 2001 From: Phil Ahrenkiel Date: Sat, 4 May 2024 20:51:59 -0600 Subject: [PATCH 14/21] Fix cmakelist. --- src/nummeth.cpp | 1 + test/unit/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nummeth.cpp b/src/nummeth.cpp index f1675a901..3062f753c 100644 --- a/src/nummeth.cpp +++ b/src/nummeth.cpp @@ -5,6 +5,7 @@ // nummeth.cpp -- numerical method functions /*------------------------------- INCLUDES --------------------------------*/ +#include #include "cnglob.h" diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 272212cc7..b23f0d297 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -20,7 +20,7 @@ target_include_directories(cse_tests PRIVATE "${gtest_SOURCE_DIR}/include" ) -target_link_libraries(cse_tests PRIVATE gtest cse_common_interface) +target_link_libraries(cse_tests PRIVATE gtest cse_common_interface fmt) target_compile_features(cse_tests PUBLIC cxx_std_17) include(GoogleTest) From 3fcdc032936af99dbf493eb1a4951f9bfdf8d784 Mon Sep 17 00:00:00 2001 From: Neal Kruis Date: Mon, 6 May 2024 09:58:13 -0600 Subject: [PATCH 15/21] Move fmt link to common interface. Fix RCDEF generated dependencies. --- CMakeLists.txt | 2 ++ src/CMakeLists.txt | 9 +++++++-- src/RCDEF/CMakeLists.txt | 2 +- test/unit/CMakeLists.txt | 4 ++-- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d083820fe..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) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 000478aa5..35d438db8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -55,7 +55,13 @@ add_custom_command( ) # Needed to establish dependency for other targets that depend on dtypes.h -add_custom_target(generate_dtypes DEPENDS ${CSE_BINARY_DIR}/src/dtypes.h) +add_custom_target(generate_rcdef_outputs DEPENDS + "${CMAKE_CURRENT_BINARY_DIR}/dtypes.h" + "${CMAKE_CURRENT_BINARY_DIR}/rccn.h" + "${CMAKE_CURRENT_BINARY_DIR}/dttab.cpp" + "${CMAKE_CURRENT_BINARY_DIR}/srfd.cpp" + "${CMAKE_CURRENT_BINARY_DIR}/untab.cpp" +) # Set CSE Version add_custom_target(version_header @@ -254,7 +260,6 @@ if(USE_BINRES) endif() set(libs - fmt glad penumbra glfw diff --git a/src/RCDEF/CMakeLists.txt b/src/RCDEF/CMakeLists.txt index 4e72d4217..44e9f72e6 100644 --- a/src/RCDEF/CMakeLists.txt +++ b/src/RCDEF/CMakeLists.txt @@ -25,4 +25,4 @@ add_executable(RCDEF ${source} ${headers}) target_compile_features(RCDEF PRIVATE cxx_std_17) target_include_directories(RCDEF PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" "${CSE_SOURCE_DIR}/src") target_compile_definitions(RCDEF PRIVATE NODTYPES) -target_link_libraries(RCDEF PRIVATE cse_common_interface fmt) +target_link_libraries(RCDEF PRIVATE cse_common_interface) diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index b23f0d297..9d958784d 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -11,7 +11,7 @@ set(source 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 @@ -20,7 +20,7 @@ target_include_directories(cse_tests PRIVATE "${gtest_SOURCE_DIR}/include" ) -target_link_libraries(cse_tests PRIVATE gtest cse_common_interface fmt) +target_link_libraries(cse_tests PRIVATE gtest cse_common_interface) target_compile_features(cse_tests PUBLIC cxx_std_17) include(GoogleTest) From 3b0d205e0c558e3b3bfb9036275eb0b87e6197b1 Mon Sep 17 00:00:00 2001 From: Neal Kruis Date: Mon, 6 May 2024 13:26:58 -0600 Subject: [PATCH 16/21] Add GENERATED property to RCDEF files. --- src/CMakeLists.txt | 12 ++++++------ test/unit/CMakeLists.txt | 7 +++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 35d438db8..6f512dfd7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,13 +54,13 @@ add_custom_command( WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" ) -# Needed to establish dependency for other targets that depend on dtypes.h +# Needed to establish dependency for other targets that depend on generated RCDEF outputs add_custom_target(generate_rcdef_outputs DEPENDS - "${CMAKE_CURRENT_BINARY_DIR}/dtypes.h" - "${CMAKE_CURRENT_BINARY_DIR}/rccn.h" - "${CMAKE_CURRENT_BINARY_DIR}/dttab.cpp" - "${CMAKE_CURRENT_BINARY_DIR}/srfd.cpp" - "${CMAKE_CURRENT_BINARY_DIR}/untab.cpp" + "${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 diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 9d958784d..c3ceaaea5 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -9,6 +9,13 @@ set(source ${CSE_BINARY_DIR}/src/dttab.cpp ) +set_source_files_properties( + "${CSE_SOURCE_DIR}/src/libstubs.cpp" + "${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_rcdef_outputs) From 0f21e2f36171a4e90df9a4cb14b4a2cdf680f281 Mon Sep 17 00:00:00 2001 From: Phil Ahrenkiel Date: Wed, 8 May 2024 12:29:24 -0600 Subject: [PATCH 17/21] Review comments. --- src/cnglob.h | 1 - src/cvpak.cpp | 7 ++++++- src/nummeth.cpp | 15 ++++++++------- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/cnglob.h b/src/cnglob.h index c8eb731ae..94140f00b 100644 --- a/src/cnglob.h +++ b/src/cnglob.h @@ -480,7 +480,6 @@ typedef USI PSOP; // type for pseudo-code opcodes -- used in sevaral .cpp files namespace Pumbra { class Penumbra; } namespace Kiva { class Instance; class Aggregator; class Foundation; } namespace Btwxt { class RegularGridInterpolator; } -#include #ifdef WINorDLL // control of scattered code for returning file names used via a cse() argument. diff --git a/src/cvpak.cpp b/src/cvpak.cpp index 4ea03c28d..4fab2bf7f 100644 --- a/src/cvpak.cpp +++ b/src/cvpak.cpp @@ -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 diff --git a/src/nummeth.cpp b/src/nummeth.cpp index 3062f753c..af2c8fa8e 100644 --- a/src/nummeth.cpp +++ b/src/nummeth.cpp @@ -5,12 +5,13 @@ // nummeth.cpp -- numerical method functions /*------------------------------- INCLUDES --------------------------------*/ -#include #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> @@ -264,8 +265,8 @@ int secant( // screen secant success (see above); report calculation if failure x1 = x1_prev; // secant (above) failed. Restore values and repeat with output f1 = f1_prev; - auto msg = fmt::format("secant failed; target = {%g}", f); - msg += fmt::format("initial: x1 = {%g}, x2 = {%g}, f1 = {%g}, f2 = {%g}", x1, x2, f1, f2); + auto msg = fmt::format("secant failed; target = {%g}\n", f); + msg += fmt::format("initial: x1 = {%g}, x2 = {%g}, f1 = {%g}, f2 = {%g}\n", x1, x2, f1, f2); double fHi = f + eps; double fLo = f - (epsLo >= 0. ? epsLo : eps); @@ -289,8 +290,8 @@ int secant( // screen secant success (see above); report calculation if failure int i; for (i = 0; ++i < 20;) // iterate to refine solution { - msg += fmt::format("begin iter {%i} ----------", i + 1); - msg += fmt::format("before: x1 = {%g}, x2 = {%g}, f1 = {%g}, f2 = {%g}", x1, x2, f1, f2); + msg += fmt::format("begin iter {%i} ----------\n", i + 1); + msg += fmt::format("before: x1 = {%g}, x2 = {%g}, f1 = {%g}, f2 = {%g}\n", x1, x2, f1, f2); if (f1 <= fHi && f1 >= fLo) { i = 0; // success @@ -306,7 +307,7 @@ int secant( // screen secant success (see above); report calculation if failure } double xN = x1 + (x2 - x1) * (f - f1) / (f2 - f1); - msg += fmt::format("xN = {%g}", xN); + msg += fmt::format("xN = {%g}\n", xN); // secant method: new guess assuming local linearity. x2 = x1; // replace older point @@ -314,7 +315,7 @@ int secant( // screen secant success (see above); report calculation if failure x1 = xN; f1 = (*pFunc)(pO, x1); // new value - msg += fmt::format("after: x1 = {%g}, x2 = {%g}, f1 = {%g}, f2 = {%g}", x1, x2, f1, f2); + msg += fmt::format("after: x1 = {%g}, x2 = {%g}, f1 = {%g}, f2 = {%g}\n", x1, x2, f1, f2); } warn(msg.c_str()); return i; From 152ad5996b48ab47538c3f052019700694d4cf4a Mon Sep 17 00:00:00 2001 From: Phil Ahrenkiel Date: Wed, 8 May 2024 14:59:58 -0600 Subject: [PATCH 18/21] Fix secant msg. --- src/nummeth.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/nummeth.cpp b/src/nummeth.cpp index af2c8fa8e..68eddeb3a 100644 --- a/src/nummeth.cpp +++ b/src/nummeth.cpp @@ -265,8 +265,8 @@ int secant( // screen secant success (see above); report calculation if failure x1 = x1_prev; // secant (above) failed. Restore values and repeat with output f1 = f1_prev; - auto msg = fmt::format("secant failed; target = {%g}\n", f); - msg += fmt::format("initial: x1 = {%g}, x2 = {%g}, f1 = {%g}, f2 = {%g}\n", x1, x2, f1, f2); + auto msg = fmt::format("secant failed; target {}\n", f); + msg += fmt::format("initial: x1 = {}, x2 = {}, f1 = {}, f2 = {}\n", x1, x2, f1, f2); double fHi = f + eps; double fLo = f - (epsLo >= 0. ? epsLo : eps); @@ -290,8 +290,8 @@ int secant( // screen secant success (see above); report calculation if failure int i; for (i = 0; ++i < 20;) // iterate to refine solution { - msg += fmt::format("begin iter {%i} ----------\n", i + 1); - msg += fmt::format("before: x1 = {%g}, x2 = {%g}, f1 = {%g}, f2 = {%g}\n", x1, x2, f1, f2); + msg += fmt::format("begin iter {} ----------\n", i); + msg += fmt::format("before: x1 = {}, x2 = {}, f1 = {}, f2 = {}\n", x1, x2, f1, f2); if (f1 <= fHi && f1 >= fLo) { i = 0; // success @@ -307,7 +307,7 @@ int secant( // screen secant success (see above); report calculation if failure } double xN = x1 + (x2 - x1) * (f - f1) / (f2 - f1); - msg += fmt::format("xN = {%g}\n", xN); + msg += fmt::format("xN = {}\n", xN); // secant method: new guess assuming local linearity. x2 = x1; // replace older point @@ -315,8 +315,9 @@ int secant( // screen secant success (see above); report calculation if failure x1 = xN; f1 = (*pFunc)(pO, x1); // new value - msg += fmt::format("after: x1 = {%g}, x2 = {%g}, f1 = {%g}, f2 = {%g}\n", x1, x2, f1, f2); + msg += fmt::format("after: x1 = {}, x2 = {}, f1 = {}, f2 = {}\n", x1, x2, f1, f2); } + warn(msg.c_str()); return i; } // ::secant From 17b33f75d07db3ce91426c823b199f0d7b8b00b4 Mon Sep 17 00:00:00 2001 From: Phil Ahrenkiel Date: Wed, 8 May 2024 15:53:51 -0600 Subject: [PATCH 19/21] Number format. --- src/nummeth.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/nummeth.cpp b/src/nummeth.cpp index 68eddeb3a..932f6acab 100644 --- a/src/nummeth.cpp +++ b/src/nummeth.cpp @@ -265,8 +265,8 @@ int secant( // screen secant success (see above); report calculation if failure x1 = x1_prev; // secant (above) failed. Restore values and repeat with output f1 = f1_prev; - auto msg = fmt::format("secant failed; target {}\n", f); - msg += fmt::format("initial: x1 = {}, x2 = {}, f1 = {}, f2 = {}\n", x1, x2, f1, f2); + auto msg = fmt::format("secant failed; target {:g}\n", f); + msg += fmt::format("initial: x1 = {:g}, x2 = {:g}, f1 = {:g}, f2 = {:g}\n", x1, x2, f1, f2); double fHi = f + eps; double fLo = f - (epsLo >= 0. ? epsLo : eps); @@ -290,8 +290,8 @@ int secant( // screen secant success (see above); report calculation if failure int i; for (i = 0; ++i < 20;) // iterate to refine solution { - msg += fmt::format("begin iter {} ----------\n", i); - msg += fmt::format("before: x1 = {}, x2 = {}, f1 = {}, f2 = {}\n", x1, x2, f1, f2); + msg += fmt::format("begin iter {: } ----------\n", i); + msg += fmt::format("before: x1 = {:g}, x2 = {:g}, f1 = {:g}, f2 = {:g}\n", x1, x2, f1, f2); if (f1 <= fHi && f1 >= fLo) { i = 0; // success @@ -315,7 +315,7 @@ int secant( // screen secant success (see above); report calculation if failure x1 = xN; f1 = (*pFunc)(pO, x1); // new value - msg += fmt::format("after: x1 = {}, x2 = {}, f1 = {}, f2 = {}\n", x1, x2, f1, f2); + msg += fmt::format("after: x1 = {:g}, x2 = {:g}, f1 = {:g}, f2 = {:g}\n", x1, x2, f1, f2); } warn(msg.c_str()); From 86931f5a2df48157aeea85889bdf2ef19e139d09 Mon Sep 17 00:00:00 2001 From: Neal Kruis Date: Tue, 28 May 2024 13:42:05 -0600 Subject: [PATCH 20/21] Clean up ASHP aux supply temp warning message. --- src/cnloads.cpp | 5 +++-- test/unit/CMakeLists.txt | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cnloads.cpp b/src/cnloads.cpp index f0ad35599..4dd7b1492 100644 --- a/src/cnloads.cpp +++ b/src/cnloads.cpp @@ -5909,8 +5909,9 @@ RC RSYS::rs_AllocateZoneAir() // finalize zone air flows tSup, amfX, // x1, f1 rs_asSupAux.as_tdb, DBL_MIN); // x2, f2 if (ret != 0) { - oWarn("ASHP aux heat supply temp fail; restoring previous value"); - tSup = rs_asSup.as_tdb; + 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/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index c3ceaaea5..0ceba702a 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -10,7 +10,6 @@ set(source ) set_source_files_properties( - "${CSE_SOURCE_DIR}/src/libstubs.cpp" "${CSE_BINARY_DIR}/src/untab.cpp" "${CSE_BINARY_DIR}/src/dttab.cpp" PROPERTIES GENERATED TRUE From e691844f200209ca756d46dba37d20ef217500bb Mon Sep 17 00:00:00 2001 From: Neal Kruis Date: Tue, 28 May 2024 14:28:44 -0600 Subject: [PATCH 21/21] Clean up secant failure formatting. --- src/nummeth.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/nummeth.cpp b/src/nummeth.cpp index 932f6acab..3d71d4604 100644 --- a/src/nummeth.cpp +++ b/src/nummeth.cpp @@ -265,9 +265,8 @@ int secant( // screen secant success (see above); report calculation if failure x1 = x1_prev; // secant (above) failed. Restore values and repeat with output f1 = f1_prev; - auto msg = fmt::format("secant failed; target {:g}\n", f); - msg += fmt::format("initial: x1 = {:g}, x2 = {:g}, f1 = {:g}, f2 = {:g}\n", x1, x2, f1, f2); - + 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); @@ -290,8 +289,8 @@ int secant( // screen secant success (see above); report calculation if failure int i; for (i = 0; ++i < 20;) // iterate to refine solution { - msg += fmt::format("begin iter {: } ----------\n", i); - msg += fmt::format("before: x1 = {:g}, x2 = {:g}, f1 = {:g}, f2 = {:g}\n", x1, x2, f1, f2); + + 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 @@ -303,11 +302,11 @@ int secant( // screen secant success (see above); report calculation if failure 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); - msg += fmt::format("xN = {}\n", xN); // secant method: new guess assuming local linearity. x2 = x1; // replace older point @@ -315,9 +314,13 @@ int secant( // screen secant success (see above); report calculation if failure x1 = xN; f1 = (*pFunc)(pO, x1); // new value - msg += fmt::format("after: x1 = {:g}, x2 = {:g}, f1 = {:g}, f2 = {:g}\n", x1, x2, f1, f2); + } + if (i > 0) { + msg += " Maximum iterations hit.\n"; + } + warn(msg.c_str()); return i; } // ::secant