climate.c
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | //----------------------------------------------------------------------------- | ||
| 2 | // climate.c | ||
| 3 | // | ||
| 4 | // Project: EPA SWMM5 | ||
| 5 | // Version: 5.2 | ||
| 6 | // Date: 11/01/21 (Build 5.2.0) | ||
| 7 | // Author: L. Rossman | ||
| 8 | // | ||
| 9 | // Climate related functions. | ||
| 10 | // | ||
| 11 | // Update History | ||
| 12 | // ============== | ||
| 13 | // Build 5.1.007: | ||
| 14 | // - NCDC GHCN climate file format added. | ||
| 15 | // - Monthly adjustments for temperature, evaporation & rainfall added. | ||
| 16 | // Build 5.1.008: | ||
| 17 | // - Monthly adjustments for hyd. conductivity added. | ||
| 18 | // - Time series evaporation rates can now vary within a day. | ||
| 19 | // - Evaporation rates are now properly updated when only flow routing | ||
| 20 | // is being simulated. | ||
| 21 | // Build 5.1.010: | ||
| 22 | // - Hargreaves evaporation now computed using 7-day average temperatures. | ||
| 23 | // Build 5.1.011: | ||
| 24 | // - Monthly adjustment for hyd. conductivity <= 0 is ignored. | ||
| 25 | // Build 5.1.013: | ||
| 26 | // - Reads names of monthly adjustment patterns for various parameters | ||
| 27 | // of a subcatchment from the [ADJUSTMENTS] section of input file. | ||
| 28 | // Build 5.2.0: | ||
| 29 | // - Reads temperature units for use with GHCND climate files. | ||
| 30 | // - Support added for relative file names. | ||
| 31 | ///----------------------------------------------------------------------------- | ||
| 32 | #define _CRT_SECURE_NO_DEPRECATE | ||
| 33 | |||
| 34 | #include <stdlib.h> | ||
| 35 | #include <stdio.h> | ||
| 36 | #include <string.h> | ||
| 37 | #include <math.h> | ||
| 38 | #include "headers.h" | ||
| 39 | |||
| 40 | //----------------------------------------------------------------------------- | ||
| 41 | // Constants | ||
| 42 | //----------------------------------------------------------------------------- | ||
| 43 | enum ClimateFileFormats {UNKNOWN_FORMAT, | ||
| 44 | USER_PREPARED, // SWMM 5's own user format | ||
| 45 | GHCND, // NCDC GHCN Daily format | ||
| 46 | TD3200, // NCDC TD3200 format | ||
| 47 | DLY0204}; // Canadian DLY02 or DLY04 format | ||
| 48 | static const int MAXCLIMATEVARS = 4; | ||
| 49 | static const int MAXDAYSPERMONTH = 32; | ||
| 50 | |||
| 51 | // These variables are used when processing climate files. | ||
| 52 | enum ClimateVarType {TMIN, TMAX, EVAP, WIND}; | ||
| 53 | enum WindSpeedType {WDMV, AWND}; | ||
| 54 | enum TempUnitsType {DEG_C10, DEG_C, DEG_F}; | ||
| 55 | static char* ClimateVarWords[] = {"TMIN", "TMAX", "EVAP", "WDMV", "AWND", | ||
| 56 | NULL}; | ||
| 57 | static char* TempUnitsWords[] = {"C10", "C", "F", NULL}; | ||
| 58 | |||
| 59 | //----------------------------------------------------------------------------- | ||
| 60 | // Data Structures | ||
| 61 | //----------------------------------------------------------------------------- | ||
| 62 | typedef struct | ||
| 63 | { | ||
| 64 | double tAve; // moving avg. for daily temperature (deg F) | ||
| 65 | double tRng; // moving avg. for daily temp. range (deg F) | ||
| 66 | double ta[7]; // data window for tAve | ||
| 67 | double tr[7]; // data window for tRng | ||
| 68 | int count; // length of moving average window | ||
| 69 | int maxCount; // maximum length of moving average window | ||
| 70 | int front; // index of front of moving average window | ||
| 71 | } TMovAve; | ||
| 72 | |||
| 73 | |||
| 74 | //----------------------------------------------------------------------------- | ||
| 75 | // Shared variables | ||
| 76 | //----------------------------------------------------------------------------- | ||
| 77 | // Temperature variables | ||
| 78 | static double Tmin; // min. daily temperature (deg F) | ||
| 79 | static double Tmax; // max. daily temperature (deg F) | ||
| 80 | static double Trng; // 1/2 range of daily temperatures | ||
| 81 | static double Trng1; // prev. max - current min. temp. | ||
| 82 | static double Tave; // average daily temperature (deg F) | ||
| 83 | static double Hrsr; // time of min. temp. (hrs) | ||
| 84 | static double Hrss; // time of max. temp (hrs) | ||
| 85 | static double Hrday; // avg. of min/max temp times | ||
| 86 | static double Dhrdy; // hrs. between min. & max. temp. times | ||
| 87 | static double Dydif; // hrs. between max. & min. temp. times | ||
| 88 | static DateTime LastDay; // date of last day with temp. data | ||
| 89 | static TMovAve Tma; // moving average of daily temperatures | ||
| 90 | |||
| 91 | // Evaporation variables | ||
| 92 | static DateTime NextEvapDate; // next date when evap. rate changes | ||
| 93 | static double NextEvapRate; // next evaporation rate (user units) | ||
| 94 | |||
| 95 | // Climate file variables | ||
| 96 | static int FileFormat; // file format (see ClimateFileFormats) | ||
| 97 | static int FileYear; // current year of file data | ||
| 98 | static int FileMonth; // current month of year of file data | ||
| 99 | static int FileDay; // current day of month of file data | ||
| 100 | static int FileLastDay; // last day of current month of file data | ||
| 101 | static int FileElapsedDays; // number of days read from file | ||
| 102 | static double FileValue[4]; // current day's values of climate data | ||
| 103 | static double FileData[4][32]; // month's worth of daily climate data | ||
| 104 | static char FileLine[MAXLINE+1]; // line from climate data file | ||
| 105 | |||
| 106 | static int FileFieldPos[4]; // start of data fields for file record | ||
| 107 | static int FileDateFieldPos; // start of date field for file record | ||
| 108 | static int FileWindType; // wind speed type | ||
| 109 | static int FileTempUnits; // GHCND file temperature units (C10, C or F) | ||
| 110 | |||
| 111 | //----------------------------------------------------------------------------- | ||
| 112 | // External functions (defined in funcs.h) | ||
| 113 | //----------------------------------------------------------------------------- | ||
| 114 | // climate_readParams // called by input_parseLine | ||
| 115 | // climate_readEvapParams // called by input_parseLine | ||
| 116 | // climate_validate // called by project_validate | ||
| 117 | // climate_openFile // called by runoff_open | ||
| 118 | // climate_initState // called by project_init | ||
| 119 | // climate_setState // called by runoff_execute | ||
| 120 | // climate_getNextEvapDate // called by runoff_getTimeStep | ||
| 121 | |||
| 122 | //----------------------------------------------------------------------------- | ||
| 123 | // Local functions | ||
| 124 | //----------------------------------------------------------------------------- | ||
| 125 | static int getFileFormat(void); | ||
| 126 | static void readFileLine(int *year, int *month); | ||
| 127 | static void readUserFileLine(int *year, int *month); | ||
| 128 | static void readTD3200FileLine(int *year, int *month); | ||
| 129 | static void readDLY0204FileLine(int *year, int *month); | ||
| 130 | static void readFileValues(void); | ||
| 131 | |||
| 132 | static void setNextEvapDate(DateTime thedate); | ||
| 133 | static void setEvap(DateTime theDate); | ||
| 134 | static void setTemp(DateTime theDate); | ||
| 135 | static void setWind(DateTime theDate); | ||
| 136 | static void updateTempTimes(int day); | ||
| 137 | static void updateTempMoveAve(double tmin, double tmax); | ||
| 138 | static double getTempEvap(int day, double ta, double tr); | ||
| 139 | |||
| 140 | static void updateFileValues(DateTime theDate); | ||
| 141 | static void parseUserFileLine(void); | ||
| 142 | static void parseTD3200FileLine(void); | ||
| 143 | static void parseDLY0204FileLine(void); | ||
| 144 | static void setTD3200FileValues(int param); | ||
| 145 | |||
| 146 | static int isGhcndFormat(char* line); | ||
| 147 | static void readGhcndFileLine(int *year, int *month); | ||
| 148 | static void parseGhcndFileLine(void); | ||
| 149 | static double convertGhcndValue(int var, double v); | ||
| 150 | |||
| 151 | //============================================================================= | ||
| 152 | |||
| 153 | 48 | int climate_readParams(char* tok[], int ntoks) | |
| 154 | // | ||
| 155 | // Input: tok[] = array of string tokens | ||
| 156 | // ntoks = number of tokens | ||
| 157 | // Output: returns error code | ||
| 158 | // Purpose: reads climate/temperature parameters from input line of data | ||
| 159 | // | ||
| 160 | // Format of data can be | ||
| 161 | // TIMESERIES name | ||
| 162 | // FILE name (start) (units) | ||
| 163 | // WINDSPEED MONTHLY v1 v2 ... v12 | ||
| 164 | // WINDSPEED FILE | ||
| 165 | // SNOWMELT v1 v2 ... v6 | ||
| 166 | // ADC IMPERV/PERV v1 v2 ... v10 | ||
| 167 | // | ||
| 168 | { | ||
| 169 | int i, j, k; | ||
| 170 | double x[6], y; | ||
| 171 | DateTime aDate; | ||
| 172 | char fname[MAXFNAME + 1]; | ||
| 173 | |||
| 174 | // --- identify keyword | ||
| 175 | 48 | k = findmatch(tok[0], TempKeyWords); | |
| 176 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
|
48 | if ( k < 0 ) return error_setInpError(ERR_KEYWORD, tok[0]); |
| 177 |
5/6✓ Branch 0 taken 1 time.
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
|
48 | switch (k) |
| 178 | { | ||
| 179 | 1 | case 0: // Time series name | |
| 180 | // --- check that time series name exists | ||
| 181 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
|
1 | if ( ntoks < 2 ) return error_setInpError(ERR_ITEMS, ""); |
| 182 | 1 | i = project_findObject(TSERIES, tok[1]); | |
| 183 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
|
1 | if ( i < 0 ) return error_setInpError(ERR_NAME, tok[1]); |
| 184 | |||
| 185 | // --- record the time series as being the data source for temperature | ||
| 186 | 1 | Temp.dataSource = TSERIES_TEMP; | |
| 187 | 1 | Temp.tSeries = i; | |
| 188 | 1 | Tseries[i].refersTo = TSERIES_TEMP; | |
| 189 | 1 | break; | |
| 190 | |||
| 191 | 11 | case 1: // Climate file | |
| 192 | // --- record file as being source of temperature data | ||
| 193 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if ( ntoks < 2 ) return error_setInpError(ERR_ITEMS, ""); |
| 194 | 11 | Temp.dataSource = FILE_TEMP; | |
| 195 | |||
| 196 | // --- save name and usage mode of external climate file | ||
| 197 | 11 | Fclimate.mode = USE_FILE; | |
| 198 | 11 | sstrncpy(fname, tok[1], MAXFNAME); | |
| 199 | 11 | sstrncpy(Fclimate.name, addAbsolutePath(fname), MAXFNAME); | |
| 200 | |||
| 201 | // --- save starting date to read from file if one is provided | ||
| 202 | 11 | Temp.fileStartDate = NO_DATE; | |
| 203 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 7 times.
|
11 | if ( ntoks > 2 ) |
| 204 | { | ||
| 205 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | if ( *tok[2] != '*') |
| 206 | { | ||
| 207 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
|
4 | if ( !datetime_strToDate(tok[2], &aDate) ) |
| 208 | ✗ | return error_setInpError(ERR_DATETIME, tok[2]); | |
| 209 | 4 | Temp.fileStartDate = aDate; | |
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | // --- file temperature units | ||
| 214 | 11 | FileTempUnits = DEG_F; | |
| 215 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (UnitSystem == SI) |
| 216 | ✗ | FileTempUnits = DEG_C; | |
| 217 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 7 times.
|
11 | if (ntoks > 3) |
| 218 | { | ||
| 219 | 4 | i = findmatch(tok[3], TempUnitsWords); | |
| 220 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (i < 0) |
| 221 | ✗ | return error_setInpError(ERR_KEYWORD, tok[3]); | |
| 222 | 4 | FileTempUnits = i; | |
| 223 | } | ||
| 224 | 11 | break; | |
| 225 | |||
| 226 | 11 | case 2: // Wind speeds | |
| 227 | // --- check if wind speeds will be supplied from climate file | ||
| 228 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 10 times.
|
11 | if ( strcomp(tok[1], w_FILE) ) |
| 229 | { | ||
| 230 | 1 | Wind.type = FILE_WIND; | |
| 231 | } | ||
| 232 | |||
| 233 | // --- otherwise read 12 monthly avg. wind speed values | ||
| 234 | else | ||
| 235 | { | ||
| 236 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | if ( ntoks < 14 ) return error_setInpError(ERR_ITEMS, ""); |
| 237 | 10 | Wind.type = MONTHLY_WIND; | |
| 238 |
2/2✓ Branch 0 taken 120 times.
✓ Branch 1 taken 10 times.
|
130 | for (i=0; i<12; i++) |
| 239 | { | ||
| 240 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 120 times.
|
120 | if ( !getDouble(tok[i+2], &y) ) |
| 241 | ✗ | return error_setInpError(ERR_NUMBER, tok[i+2]); | |
| 242 | 120 | Wind.aws[i] = y; | |
| 243 | } | ||
| 244 | } | ||
| 245 | 11 | break; | |
| 246 | |||
| 247 | 9 | case 3: // Snowmelt params | |
| 248 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if ( ntoks < 7 ) return error_setInpError(ERR_ITEMS, ""); |
| 249 |
2/2✓ Branch 0 taken 54 times.
✓ Branch 1 taken 9 times.
|
63 | for (i=1; i<7; i++) |
| 250 | { | ||
| 251 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 54 times.
|
54 | if ( !getDouble(tok[i], &x[i-1]) ) |
| 252 | ✗ | return error_setInpError(ERR_NUMBER, tok[i]); | |
| 253 | } | ||
| 254 | // --- convert deg. C to deg. F for snowfall temperature | ||
| 255 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if ( UnitSystem == SI ) x[0] = 9./5.*x[0] + 32.0; |
| 256 | 9 | Snow.snotmp = x[0]; | |
| 257 | 9 | Snow.tipm = x[1]; | |
| 258 | 9 | Snow.rnm = x[2]; | |
| 259 | 9 | Temp.elev = x[3] / UCF(LENGTH); | |
| 260 | 9 | Temp.anglat = x[4]; | |
| 261 | 9 | Temp.dtlong = x[5] / 60.0; | |
| 262 | 9 | break; | |
| 263 | |||
| 264 | 16 | case 4: // Areal Depletion Curve data | |
| 265 | // --- check if data is for impervious or pervious areas | ||
| 266 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if ( ntoks < 12 ) return error_setInpError(ERR_ITEMS, ""); |
| 267 |
2/2✓ Branch 1 taken 8 times.
✓ Branch 2 taken 8 times.
|
16 | if ( match(tok[1], w_IMPERV) ) i = 0; |
| 268 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | else if ( match(tok[1], w_PERV) ) i = 1; |
| 269 | ✗ | else return error_setInpError(ERR_KEYWORD, tok[1]); | |
| 270 | |||
| 271 | // --- read 10 fractional values | ||
| 272 |
2/2✓ Branch 0 taken 160 times.
✓ Branch 1 taken 16 times.
|
176 | for (j=0; j<10; j++) |
| 273 | { | ||
| 274 |
3/6✓ Branch 1 taken 160 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 160 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 160 times.
|
160 | if ( !getDouble(tok[j+2], &y) || y < 0.0 || y > 1.0 ) |
| 275 | ✗ | return error_setInpError(ERR_NUMBER, tok[j+2]); | |
| 276 | 160 | Snow.adc[i][j] = y; | |
| 277 | } | ||
| 278 | 16 | break; | |
| 279 | } | ||
| 280 | 48 | return 0; | |
| 281 | } | ||
| 282 | |||
| 283 | //============================================================================= | ||
| 284 | |||
| 285 | 92 | int climate_readEvapParams(char* tok[], int ntoks) | |
| 286 | // | ||
| 287 | // Input: tok[] = array of string tokens | ||
| 288 | // ntoks = number of tokens | ||
| 289 | // Output: returns error code | ||
| 290 | // Purpose: reads evaporation parameters from input line of data. | ||
| 291 | // | ||
| 292 | // Data formats are: | ||
| 293 | // CONSTANT value | ||
| 294 | // MONTHLY v1 ... v12 | ||
| 295 | // TIMESERIES name | ||
| 296 | // TEMPERATURE | ||
| 297 | // FILE (v1 ... v12) | ||
| 298 | // RECOVERY name | ||
| 299 | // DRY_ONLY YES/NO | ||
| 300 | // | ||
| 301 | { | ||
| 302 | int i, k; | ||
| 303 | double x; | ||
| 304 | |||
| 305 | // --- find keyword indicating what form the evaporation data is in | ||
| 306 | 92 | k = findmatch(tok[0], EvapTypeWords); | |
| 307 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 92 times.
|
92 | if ( k < 0 ) return error_setInpError(ERR_KEYWORD, tok[0]); |
| 308 | |||
| 309 | // --- check for RECOVERY pattern data | ||
| 310 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 90 times.
|
92 | if ( k == RECOVERY ) |
| 311 | { | ||
| 312 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if ( ntoks < 2 ) return error_setInpError(ERR_ITEMS, ""); |
| 313 | 2 | i = project_findObject(TIMEPATTERN, tok[1]); | |
| 314 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if ( i < 0 ) return error_setInpError(ERR_NAME, tok[1]); |
| 315 | 2 | Evap.recoveryPattern = i; | |
| 316 | 2 | return 0; | |
| 317 | } | ||
| 318 | |||
| 319 | // --- check for no evaporation in wet periods | ||
| 320 |
2/2✓ Branch 0 taken 44 times.
✓ Branch 1 taken 46 times.
|
90 | if ( k == DRYONLY ) |
| 321 | { | ||
| 322 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
|
44 | if ( ntoks < 2 ) return error_setInpError(ERR_ITEMS, ""); |
| 323 |
1/2✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
|
44 | if ( strcomp(tok[1], w_NO ) ) Evap.dryOnly = FALSE; |
| 324 | ✗ | else if ( strcomp(tok[1], w_YES ) ) Evap.dryOnly = TRUE; | |
| 325 | ✗ | else return error_setInpError(ERR_KEYWORD, tok[1]); | |
| 326 | 44 | return 0; | |
| 327 | } | ||
| 328 | |||
| 329 | // --- process data depending on its form | ||
| 330 | 46 | Evap.type = k; | |
| 331 |
3/4✓ Branch 0 taken 37 times.
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 37 times.
|
46 | if ( k != TEMPERATURE_EVAP && ntoks < 2 ) |
| 332 | ✗ | return error_setInpError(ERR_ITEMS, ""); | |
| 333 |
5/5✓ Branch 0 taken 32 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 9 times.
|
46 | switch ( k ) |
| 334 | { | ||
| 335 | 32 | case CONSTANT_EVAP: | |
| 336 | // --- for constant evap., fill monthly avg. values with same number | ||
| 337 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 32 times.
|
32 | if ( !getDouble(tok[1], &x) ) |
| 338 | ✗ | return error_setInpError(ERR_NUMBER, tok[1]); | |
| 339 |
2/2✓ Branch 0 taken 384 times.
✓ Branch 1 taken 32 times.
|
416 | for (i=0; i<12; i++) Evap.monthlyEvap[i] = x; |
| 340 | 32 | break; | |
| 341 | |||
| 342 | 2 | case MONTHLY_EVAP: | |
| 343 | // --- for monthly evap., read a value for each month of year | ||
| 344 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if ( ntoks < 13 ) return error_setInpError(ERR_ITEMS, ""); |
| 345 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 2 times.
|
26 | for ( i=0; i<12; i++) |
| 346 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
|
24 | if ( !getDouble(tok[i+1], &Evap.monthlyEvap[i]) ) |
| 347 | ✗ | return error_setInpError(ERR_NUMBER, tok[i+1]); | |
| 348 | 2 | break; | |
| 349 | |||
| 350 | 1 | case TIMESERIES_EVAP: | |
| 351 | // --- for time series evap., read name of time series | ||
| 352 | 1 | i = project_findObject(TSERIES, tok[1]); | |
| 353 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
|
1 | if ( i < 0 ) return error_setInpError(ERR_NAME, tok[1]); |
| 354 | 1 | Evap.tSeries = i; | |
| 355 | 1 | Tseries[i].refersTo = TIMESERIES_EVAP; | |
| 356 | 1 | break; | |
| 357 | |||
| 358 | 2 | case FILE_EVAP: | |
| 359 | // --- for evap. from climate file, read monthly pan coeffs. | ||
| 360 | // if they are provided (default values are 1.0) | ||
| 361 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if ( ntoks > 1 ) |
| 362 | { | ||
| 363 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if ( ntoks < 13 ) return error_setInpError(ERR_ITEMS, ""); |
| 364 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 2 times.
|
26 | for (i=0; i<12; i++) |
| 365 | { | ||
| 366 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
|
24 | if ( !getDouble(tok[i+1], &Evap.panCoeff[i]) ) |
| 367 | ✗ | return error_setInpError(ERR_NUMBER, tok[i+1]); | |
| 368 | } | ||
| 369 | } | ||
| 370 | 2 | break; | |
| 371 | } | ||
| 372 | 46 | return 0; | |
| 373 | } | ||
| 374 | |||
| 375 | //============================================================================= | ||
| 376 | |||
| 377 | 8 | int climate_readAdjustments(char* tok[], int ntoks) | |
| 378 | // | ||
| 379 | // Input: tok[] = array of string tokens | ||
| 380 | // ntoks = number of tokens | ||
| 381 | // Output: returns error code | ||
| 382 | // Purpose: reads adjustments to monthly evaporation or rainfall | ||
| 383 | // from input line of data. | ||
| 384 | // | ||
| 385 | // Data formats are: | ||
| 386 | // TEMPERATURE v1 ... v12 | ||
| 387 | // EVAPORATION v1 ... v12 | ||
| 388 | // RAINFALL v1 ... v12 | ||
| 389 | // CONDUCTIVITY v1 ... v12 | ||
| 390 | // N-PERV subcatchID patternID | ||
| 391 | // DSTORE subcatchID patternID | ||
| 392 | // INFIL subcatchID patternID | ||
| 393 | { | ||
| 394 | int i, j; | ||
| 395 | |||
| 396 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | if (ntoks == 1) return 0; |
| 397 | |||
| 398 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 6 times.
|
8 | if ( match(tok[0], "TEMP") ) |
| 399 | { | ||
| 400 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if ( ntoks < 13 ) return error_setInpError(ERR_ITEMS, ""); |
| 401 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 2 times.
|
26 | for (i = 1; i < 13; i++) |
| 402 | { | ||
| 403 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
|
24 | if ( !getDouble(tok[i], &Adjust.temp[i-1]) ) |
| 404 | ✗ | return error_setInpError(ERR_NUMBER, tok[i]); | |
| 405 | } | ||
| 406 | 2 | return 0; | |
| 407 | } | ||
| 408 | |||
| 409 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4 times.
|
6 | if ( match(tok[0], "EVAP") ) |
| 410 | { | ||
| 411 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if ( ntoks < 13 ) return error_setInpError(ERR_ITEMS, ""); |
| 412 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 2 times.
|
26 | for (i = 1; i < 13; i++) |
| 413 | { | ||
| 414 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
|
24 | if ( !getDouble(tok[i], &Adjust.evap[i-1]) ) |
| 415 | ✗ | return error_setInpError(ERR_NUMBER, tok[i]); | |
| 416 | } | ||
| 417 | 2 | return 0; | |
| 418 | } | ||
| 419 | |||
| 420 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
|
4 | if ( match(tok[0], "RAIN") ) |
| 421 | { | ||
| 422 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if ( ntoks < 13 ) return error_setInpError(ERR_ITEMS, ""); |
| 423 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 2 times.
|
26 | for (i = 1; i < 13; i++) |
| 424 | { | ||
| 425 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
|
24 | if ( !getDouble(tok[i], &Adjust.rain[i-1]) ) |
| 426 | ✗ | return error_setInpError(ERR_NUMBER, tok[i]); | |
| 427 | } | ||
| 428 | 2 | return 0; | |
| 429 | } | ||
| 430 | |||
| 431 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | if ( match(tok[0], "CONDUCT") ) |
| 432 | { | ||
| 433 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if ( ntoks < 13 ) return error_setInpError(ERR_ITEMS, ""); |
| 434 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 2 times.
|
26 | for (i = 1; i < 13; i++) |
| 435 | { | ||
| 436 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
|
24 | if ( !getDouble(tok[i], &Adjust.hydcon[i-1]) ) |
| 437 | ✗ | return error_setInpError(ERR_NUMBER, tok[i]); | |
| 438 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | if ( Adjust.hydcon[i-1] <= 0.0 ) Adjust.hydcon[i-1] = 1.0; |
| 439 | } | ||
| 440 | 2 | return 0; | |
| 441 | } | ||
| 442 | |||
| 443 | ✗ | if ( match(tok[0], "N-PERV") ) | |
| 444 | { | ||
| 445 | ✗ | if ( ntoks < 3 ) return error_setInpError(ERR_ITEMS, ""); | |
| 446 | ✗ | i = project_findObject(SUBCATCH, tok[1]); | |
| 447 | ✗ | if (i < 0) return error_setInpError(ERR_NAME, tok[1]); | |
| 448 | ✗ | j = project_findObject(TIMEPATTERN, tok[2]); | |
| 449 | ✗ | if (j < 0) return error_setInpError(ERR_NAME, tok[2]); | |
| 450 | ✗ | Subcatch[i].nPervPattern = j; | |
| 451 | ✗ | return 0; | |
| 452 | } | ||
| 453 | |||
| 454 | ✗ | if ( match(tok[0], "DSTORE") ) | |
| 455 | { | ||
| 456 | ✗ | if (ntoks < 3) return error_setInpError(ERR_ITEMS, ""); | |
| 457 | ✗ | i = project_findObject(SUBCATCH, tok[1]); | |
| 458 | ✗ | if (i < 0) return error_setInpError(ERR_NAME, tok[1]); | |
| 459 | ✗ | j = project_findObject(TIMEPATTERN, tok[2]); | |
| 460 | ✗ | if (j < 0) return error_setInpError(ERR_NAME, tok[2]); | |
| 461 | ✗ | Subcatch[i].dStorePattern = j; | |
| 462 | ✗ | return 0; | |
| 463 | } | ||
| 464 | |||
| 465 | ✗ | if (match(tok[0], "INFIL")) | |
| 466 | { | ||
| 467 | ✗ | if (ntoks < 3) return error_setInpError(ERR_ITEMS, ""); | |
| 468 | ✗ | i = project_findObject(SUBCATCH, tok[1]); | |
| 469 | ✗ | if (i < 0) return error_setInpError(ERR_NAME, tok[1]); | |
| 470 | ✗ | j = project_findObject(TIMEPATTERN, tok[2]); | |
| 471 | ✗ | if (j < 0) return error_setInpError(ERR_NAME, tok[2]); | |
| 472 | ✗ | Subcatch[i].infilPattern = j; | |
| 473 | ✗ | return 0; | |
| 474 | } | ||
| 475 | ✗ | return error_setInpError(ERR_KEYWORD, tok[0]); | |
| 476 | } | ||
| 477 | |||
| 478 | //============================================================================= | ||
| 479 | |||
| 480 | 58 | void climate_validate() | |
| 481 | // | ||
| 482 | // Input: none | ||
| 483 | // Output: none | ||
| 484 | // Purpose: validates climatological variables | ||
| 485 | // | ||
| 486 | { | ||
| 487 | int i; | ||
| 488 | double a, z, pa; | ||
| 489 | |||
| 490 | // --- check if climate data comes from external data file | ||
| 491 |
4/4✓ Branch 0 taken 57 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 56 times.
✓ Branch 3 taken 1 time.
|
58 | if ( Wind.type == FILE_WIND || Evap.type == FILE_EVAP || |
| 492 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 47 times.
|
56 | Evap.type == TEMPERATURE_EVAP ) |
| 493 | { | ||
| 494 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if ( Fclimate.mode == NO_FILE ) |
| 495 | { | ||
| 496 | ✗ | report_writeErrorMsg(ERR_NO_CLIMATE_FILE, ""); | |
| 497 | } | ||
| 498 | } | ||
| 499 | |||
| 500 | // --- open the climate data file | ||
| 501 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 47 times.
|
58 | if ( Fclimate.mode == USE_FILE ) climate_openFile(); |
| 502 | |||
| 503 | // --- snow melt parameters tipm & rnm must be fractions | ||
| 504 |
1/2✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
|
58 | if ( Snow.tipm < 0.0 || |
| 505 |
1/2✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
|
58 | Snow.tipm > 1.0 || |
| 506 |
1/2✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
|
58 | Snow.rnm < 0.0 || |
| 507 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
|
58 | Snow.rnm > 1.0 ) report_writeErrorMsg(ERR_SNOWMELT_PARAMS, ""); |
| 508 | |||
| 509 | // --- latitude should be between -90 & 90 degrees | ||
| 510 | 58 | a = Temp.anglat; | |
| 511 |
2/4✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 58 times.
|
58 | if ( a <= -89.99 || |
| 512 | ✗ | a >= 89.99 ) report_writeErrorMsg(ERR_SNOWMELT_PARAMS, ""); | |
| 513 | 58 | else Temp.tanAnglat = tan(a * PI / 180.0); | |
| 514 | |||
| 515 | // --- compute psychrometric constant | ||
| 516 | 58 | z = Temp.elev / 1000.0; | |
| 517 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 9 times.
|
58 | if ( z <= 0.0 ) pa = 29.9; |
| 518 | 9 | else pa = 29.9 - 1.02*z + 0.0032*pow(z, 2.4); // atmos. pressure | |
| 519 | 58 | Temp.gamma = 0.000359 * pa; | |
| 520 | |||
| 521 | // --- convert units of monthly temperature & evap adjustments | ||
| 522 |
2/2✓ Branch 0 taken 696 times.
✓ Branch 1 taken 58 times.
|
754 | for (i = 0; i < 12; i++) |
| 523 | { | ||
| 524 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 660 times.
|
696 | if (UnitSystem == SI) Adjust.temp[i] *= 9.0/5.0; |
| 525 | 696 | Adjust.evap[i] /= UCF(EVAPRATE); | |
| 526 | } | ||
| 527 | 58 | } | |
| 528 | |||
| 529 | //============================================================================= | ||
| 530 | |||
| 531 | 11 | void climate_openFile() | |
| 532 | // | ||
| 533 | // Input: none | ||
| 534 | // Output: none | ||
| 535 | // Purpose: opens a climate file and reads in first set of values. | ||
| 536 | // | ||
| 537 | { | ||
| 538 | int i, m, y; | ||
| 539 | |||
| 540 | // --- open the file | ||
| 541 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
|
11 | if ( (Fclimate.file = fopen(Fclimate.name, "rt")) == NULL ) |
| 542 | { | ||
| 543 | ✗ | report_writeErrorMsg(ERR_CLIMATE_FILE_OPEN, Fclimate.name); | |
| 544 | ✗ | return; | |
| 545 | } | ||
| 546 | |||
| 547 | // --- initialize values of file's climate variables | ||
| 548 | // (Temp.ta was previously initialized in project.c) | ||
| 549 | 11 | FileValue[TMIN] = Temp.ta; | |
| 550 | 11 | FileValue[TMAX] = Temp.ta; | |
| 551 | 11 | FileValue[EVAP] = 0.0; | |
| 552 | 11 | FileValue[WIND] = 0.0; | |
| 553 | |||
| 554 | // --- find climate file's format | ||
| 555 | 11 | FileFormat = getFileFormat(); | |
| 556 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if ( FileFormat == UNKNOWN_FORMAT ) |
| 557 | { | ||
| 558 | ✗ | report_writeErrorMsg(ERR_CLIMATE_FILE_READ, Fclimate.name); | |
| 559 | ✗ | return; | |
| 560 | } | ||
| 561 | |||
| 562 | // --- position file to begin reading climate file at either user-specified | ||
| 563 | // month/year or at start of simulation period. | ||
| 564 | 11 | rewind(Fclimate.file); | |
| 565 | 11 | sstrncpy(FileLine, "", 0); | |
| 566 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 4 times.
|
11 | if ( Temp.fileStartDate == NO_DATE ) |
| 567 | 7 | datetime_decodeDate(StartDate, &FileYear, &FileMonth, &FileDay); | |
| 568 | else | ||
| 569 | 4 | datetime_decodeDate(Temp.fileStartDate, &FileYear, &FileMonth, &FileDay); | |
| 570 |
1/2✓ Branch 1 taken 29367 times.
✗ Branch 2 not taken.
|
29367 | while ( !feof(Fclimate.file) ) |
| 571 | { | ||
| 572 | 29367 | sstrncpy(FileLine, "", 0); | |
| 573 | 29367 | readFileLine(&y, &m); | |
| 574 |
4/4✓ Branch 0 taken 346 times.
✓ Branch 1 taken 29021 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 335 times.
|
29367 | if ( y == FileYear && m == FileMonth ) break; |
| 575 | } | ||
| 576 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
|
11 | if ( feof(Fclimate.file) ) |
| 577 | { | ||
| 578 | ✗ | report_writeErrorMsg(ERR_CLIMATE_END_OF_FILE, Fclimate.name); | |
| 579 | ✗ | return; | |
| 580 | } | ||
| 581 | |||
| 582 | // --- initialize file dates and current climate variable values | ||
| 583 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | if ( !ErrorCode ) |
| 584 | { | ||
| 585 | 11 | FileElapsedDays = 0; | |
| 586 | 11 | FileLastDay = datetime_daysPerMonth(FileYear, FileMonth); | |
| 587 | 11 | readFileValues(); | |
| 588 |
2/2✓ Branch 0 taken 44 times.
✓ Branch 1 taken 11 times.
|
55 | for (i=TMIN; i<=WIND; i++) |
| 589 | { | ||
| 590 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 24 times.
|
44 | if ( FileData[i][FileDay] == MISSING ) continue; |
| 591 | 24 | FileValue[i] = FileData[i][FileDay]; | |
| 592 | } | ||
| 593 | } | ||
| 594 | } | ||
| 595 | |||
| 596 | //============================================================================= | ||
| 597 | |||
| 598 | 58 | void climate_initState() | |
| 599 | // | ||
| 600 | // Input: none | ||
| 601 | // Output: none | ||
| 602 | // Purpose: initializes climate state variables. | ||
| 603 | // | ||
| 604 | { | ||
| 605 | 58 | LastDay = NO_DATE; | |
| 606 | 58 | Temp.tmax = MISSING; | |
| 607 | 58 | Snow.removed = 0.0; | |
| 608 | 58 | NextEvapDate = StartDate; | |
| 609 | 58 | NextEvapRate = 0.0; | |
| 610 | |||
| 611 | // --- initialize variables for time series evaporation | ||
| 612 |
3/4✓ Branch 0 taken 1 time.
✓ Branch 1 taken 57 times.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
|
58 | if ( Evap.type == TIMESERIES_EVAP && Evap.tSeries >= 0 ) |
| 613 | { | ||
| 614 | // --- initialize NextEvapDate & NextEvapRate to first entry of | ||
| 615 | // time series whose date <= the simulation start date | ||
| 616 | 1 | table_getFirstEntry(&Tseries[Evap.tSeries], | |
| 617 | &NextEvapDate, &NextEvapRate); | ||
| 618 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
|
1 | if ( NextEvapDate < StartDate ) |
| 619 | { | ||
| 620 | ✗ | setNextEvapDate(StartDate); | |
| 621 | } | ||
| 622 | 1 | Evap.rate = NextEvapRate / UCF(EVAPRATE); | |
| 623 | |||
| 624 | // --- find the next time evaporation rates change after this | ||
| 625 | 1 | setNextEvapDate(NextEvapDate); | |
| 626 | } | ||
| 627 | |||
| 628 | // --- initialize variables for temperature evaporation | ||
| 629 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 49 times.
|
58 | if ( Evap.type == TEMPERATURE_EVAP ) |
| 630 | { | ||
| 631 | 9 | Tma.maxCount = sizeof(Tma.ta) / sizeof(double); | |
| 632 | 9 | Tma.count = 0; | |
| 633 | 9 | Tma.front = 0; | |
| 634 | 9 | Tma.tAve = 0.0; | |
| 635 | 9 | Tma.tRng = 0.0; | |
| 636 | } | ||
| 637 | 58 | } | |
| 638 | |||
| 639 | //============================================================================= | ||
| 640 | |||
| 641 | 686476 | void climate_setState(DateTime theDate) | |
| 642 | // | ||
| 643 | // Input: theDate = simulation date | ||
| 644 | // Output: none | ||
| 645 | // Purpose: sets climate variables for current date. | ||
| 646 | // | ||
| 647 | { | ||
| 648 |
2/2✓ Branch 0 taken 11592 times.
✓ Branch 1 taken 674884 times.
|
686476 | if ( Fclimate.mode == USE_FILE ) updateFileValues(theDate); |
| 649 |
2/2✓ Branch 0 taken 12012 times.
✓ Branch 1 taken 674464 times.
|
686476 | if ( Temp.dataSource != NO_TEMP ) setTemp(theDate); |
| 650 | 686476 | setEvap(theDate); | |
| 651 | 686476 | setWind(theDate); | |
| 652 | 686476 | Adjust.rainFactor = Adjust.rain[datetime_monthOfYear(theDate)-1]; | |
| 653 | 686476 | Adjust.hydconFactor = Adjust.hydcon[datetime_monthOfYear(theDate)-1]; | |
| 654 | 686476 | setNextEvapDate(theDate); | |
| 655 | 686476 | } | |
| 656 | |||
| 657 | //============================================================================= | ||
| 658 | |||
| 659 | 33090 | DateTime climate_getNextEvapDate() | |
| 660 | // | ||
| 661 | // Input: none | ||
| 662 | // Output: returns the current value of NextEvapDate | ||
| 663 | // Purpose: gets the next date when evaporation rate changes. | ||
| 664 | // | ||
| 665 | { | ||
| 666 | 33090 | return NextEvapDate; | |
| 667 | } | ||
| 668 | |||
| 669 | //============================================================================= | ||
| 670 | |||
| 671 | 686477 | void setNextEvapDate(DateTime theDate) | |
| 672 | // | ||
| 673 | // Input: theDate = current simulation date | ||
| 674 | // Output: sets a new value for NextEvapDate | ||
| 675 | // Purpose: finds date for next change in evaporation after the current date. | ||
| 676 | // | ||
| 677 | { | ||
| 678 | int yr, mon, day, k; | ||
| 679 | double d, e; | ||
| 680 | |||
| 681 | // --- do nothing if current date hasn't reached the current next date | ||
| 682 |
2/2✓ Branch 0 taken 686411 times.
✓ Branch 1 taken 66 times.
|
686477 | if ( NextEvapDate > theDate ) return; |
| 683 | |||
| 684 |
5/5✓ Branch 0 taken 44 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 9 times.
|
66 | switch ( Evap.type ) |
| 685 | { | ||
| 686 | // --- for constant evaporation, use a next date far in the future | ||
| 687 | 44 | case CONSTANT_EVAP: | |
| 688 | 44 | NextEvapDate = theDate + 365.; | |
| 689 | 44 | break; | |
| 690 | |||
| 691 | // --- for monthly evaporation, use the start of the next month | ||
| 692 | 2 | case MONTHLY_EVAP: | |
| 693 | 2 | datetime_decodeDate(theDate, &yr, &mon, &day); | |
| 694 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if ( mon == 12 ) |
| 695 | { | ||
| 696 | ✗ | mon = 1; | |
| 697 | ✗ | yr++; | |
| 698 | } | ||
| 699 | 2 | else mon++; | |
| 700 | 2 | NextEvapDate = datetime_encodeDate(yr, mon, 1); | |
| 701 | 2 | break; | |
| 702 | |||
| 703 | // --- for time series evaporation, find the next entry in the | ||
| 704 | // series on or after the current date | ||
| 705 | 3 | case TIMESERIES_EVAP: | |
| 706 | 3 | k = Evap.tSeries; | |
| 707 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if ( k >= 0 ) |
| 708 | { | ||
| 709 | 3 | NextEvapDate = theDate + 365.; | |
| 710 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
|
3 | while ( table_getNextEntry(&Tseries[k], &d, &e) && |
| 711 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | d <= EndDateTime ) |
| 712 | { | ||
| 713 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if ( d >= theDate ) |
| 714 | { | ||
| 715 | 2 | NextEvapDate = d; | |
| 716 | 2 | NextEvapRate = e; | |
| 717 | 2 | break; | |
| 718 | } | ||
| 719 | } | ||
| 720 | } | ||
| 721 | 3 | break; | |
| 722 | |||
| 723 | // --- for climate file daily evaporation, use the next day | ||
| 724 | 8 | case FILE_EVAP: | |
| 725 | 8 | NextEvapDate = floor(theDate) + 1.0; | |
| 726 | 8 | break; | |
| 727 | |||
| 728 | 9 | default: NextEvapDate = theDate + 365.; | |
| 729 | } | ||
| 730 | } | ||
| 731 | |||
| 732 | //============================================================================= | ||
| 733 | |||
| 734 | 11592 | void updateFileValues(DateTime theDate) | |
| 735 | // | ||
| 736 | // Input: theDate = current simulation date | ||
| 737 | // Output: none | ||
| 738 | // Purpose: updates daily climate variables for new day or reads in | ||
| 739 | // another month worth of values if a new month begins. | ||
| 740 | // | ||
| 741 | // NOTE: counters FileElapsedDays, FileDay, FileMonth, FileYear and | ||
| 742 | // FileLastDay were initialized in climate_openFile(). | ||
| 743 | // | ||
| 744 | { | ||
| 745 | int i; | ||
| 746 | int deltaDays; | ||
| 747 | |||
| 748 | // --- see if a new day has begun | ||
| 749 | 11592 | deltaDays = (int)(floor(theDate) - floor(StartDateTime)); | |
| 750 |
2/2✓ Branch 0 taken 41 times.
✓ Branch 1 taken 11551 times.
|
11592 | if ( deltaDays > FileElapsedDays ) |
| 751 | { | ||
| 752 | // --- advance day counters | ||
| 753 | 41 | FileElapsedDays++; | |
| 754 | 41 | FileDay++; | |
| 755 | |||
| 756 | // --- see if new month of data needs to be read from file | ||
| 757 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 40 times.
|
41 | if ( FileDay > FileLastDay ) |
| 758 | { | ||
| 759 | 1 | FileMonth++; | |
| 760 |
1/2✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
|
1 | if ( FileMonth > 12 ) |
| 761 | { | ||
| 762 | 1 | FileMonth = 1; | |
| 763 | 1 | FileYear++; | |
| 764 | } | ||
| 765 | 1 | readFileValues(); | |
| 766 | 1 | FileDay = 1; | |
| 767 | 1 | FileLastDay = datetime_daysPerMonth(FileYear, FileMonth); | |
| 768 | } | ||
| 769 | |||
| 770 | // --- set climate variables for new day | ||
| 771 |
2/2✓ Branch 0 taken 164 times.
✓ Branch 1 taken 41 times.
|
205 | for (i=TMIN; i<=WIND; i++) |
| 772 | { | ||
| 773 | // --- no change in current value if its missing | ||
| 774 |
2/2✓ Branch 0 taken 76 times.
✓ Branch 1 taken 88 times.
|
164 | if ( FileData[i][FileDay] == MISSING ) continue; |
| 775 | 88 | FileValue[i] = FileData[i][FileDay]; | |
| 776 | } | ||
| 777 | } | ||
| 778 | 11592 | } | |
| 779 | |||
| 780 | //============================================================================= | ||
| 781 | |||
| 782 | 12012 | void setTemp(DateTime theDate) | |
| 783 | // | ||
| 784 | // Input: theDate = simulation date | ||
| 785 | // Output: none | ||
| 786 | // Purpose: updates temperatures for new simulation date. | ||
| 787 | // | ||
| 788 | { | ||
| 789 | int j; // snow data object index | ||
| 790 | int k; // time series index | ||
| 791 | int mon; // month of year | ||
| 792 | int day; // day of year | ||
| 793 | DateTime theDay; // calendar day | ||
| 794 | double hour; // hour of day | ||
| 795 | double tmp; // temporary temperature | ||
| 796 | |||
| 797 | // --- see if a new day has started | ||
| 798 | 12012 | mon = datetime_monthOfYear(theDate); | |
| 799 | 12012 | theDay = floor(theDate); | |
| 800 |
2/2✓ Branch 0 taken 54 times.
✓ Branch 1 taken 11958 times.
|
12012 | if ( theDay > LastDay ) |
| 801 | { | ||
| 802 | // --- update min. & max. temps & their time of day | ||
| 803 | 54 | day = datetime_dayOfYear(theDate); | |
| 804 |
2/2✓ Branch 0 taken 52 times.
✓ Branch 1 taken 2 times.
|
54 | if ( Temp.dataSource == FILE_TEMP ) |
| 805 | { | ||
| 806 | 52 | Tmin = FileValue[TMIN] + Adjust.temp[mon-1]; | |
| 807 | 52 | Tmax = FileValue[TMAX] + Adjust.temp[mon-1]; | |
| 808 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
|
52 | if ( Tmin > Tmax ) |
| 809 | { | ||
| 810 | ✗ | tmp = Tmin; | |
| 811 | ✗ | Tmin = Tmax; | |
| 812 | ✗ | Tmax = tmp; | |
| 813 | } | ||
| 814 | 52 | updateTempTimes(day); | |
| 815 |
2/2✓ Branch 0 taken 44 times.
✓ Branch 1 taken 8 times.
|
52 | if ( Evap.type == TEMPERATURE_EVAP ) |
| 816 | { | ||
| 817 | 44 | updateTempMoveAve(Tmin, Tmax); | |
| 818 | 44 | FileValue[EVAP] = getTempEvap(day, Tma.tAve, Tma.tRng); | |
| 819 | } | ||
| 820 | } | ||
| 821 | |||
| 822 | // --- compute snow melt coefficients based on day of year | ||
| 823 | 54 | Snow.season = sin(0.0172615*(day-81.0)); | |
| 824 |
2/2✓ Branch 0 taken 40 times.
✓ Branch 1 taken 54 times.
|
94 | for (j=0; j<Nobjects[SNOWMELT]; j++) |
| 825 | { | ||
| 826 | 40 | snow_setMeltCoeffs(j, Snow.season); | |
| 827 | } | ||
| 828 | |||
| 829 | // --- update date of last day analyzed | ||
| 830 | 54 | LastDay = theDate; | |
| 831 | } | ||
| 832 | |||
| 833 | // --- for min/max daily temps. from climate file, | ||
| 834 | // compute hourly temp. by sinusoidal interp. | ||
| 835 |
2/2✓ Branch 0 taken 11592 times.
✓ Branch 1 taken 420 times.
|
12012 | if ( Temp.dataSource == FILE_TEMP ) |
| 836 | { | ||
| 837 | 11592 | hour = (theDate - theDay) * 24.0; | |
| 838 |
2/2✓ Branch 0 taken 3183 times.
✓ Branch 1 taken 8409 times.
|
11592 | if ( hour < Hrsr ) |
| 839 | 3183 | Temp.ta = Tmin + Trng1/2.0 * sin(PI/Dydif * (Hrsr - hour)); | |
| 840 |
3/4✓ Branch 0 taken 8409 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3429 times.
✓ Branch 3 taken 4980 times.
|
8409 | else if ( hour >= Hrsr && hour <= Hrss ) |
| 841 | 3429 | Temp.ta = Tave + Trng * sin(PI/Dhrdy * (Hrday - hour)); | |
| 842 | else | ||
| 843 | 4980 | Temp.ta = Tmax - Trng * sin(PI/Dydif * (hour - Hrss)); | |
| 844 | } | ||
| 845 | |||
| 846 | // --- for user-supplied temperature time series, | ||
| 847 | // get temperature value from time series | ||
| 848 |
2/2✓ Branch 0 taken 420 times.
✓ Branch 1 taken 11592 times.
|
12012 | if ( Temp.dataSource == TSERIES_TEMP ) |
| 849 | { | ||
| 850 | 420 | k = Temp.tSeries; | |
| 851 |
1/2✓ Branch 0 taken 420 times.
✗ Branch 1 not taken.
|
420 | if ( k >= 0) |
| 852 | { | ||
| 853 | 420 | Temp.ta = table_tseriesLookup(&Tseries[k], theDate, TRUE); | |
| 854 | |||
| 855 | // --- convert from deg. C to deg. F if need be | ||
| 856 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 420 times.
|
420 | if ( UnitSystem == SI ) |
| 857 | { | ||
| 858 | ✗ | Temp.ta = (9./5.) * Temp.ta + 32.0; | |
| 859 | } | ||
| 860 | |||
| 861 | // --- apply climate change adjustment factor | ||
| 862 | 420 | Temp.ta += Adjust.temp[mon-1]; | |
| 863 | } | ||
| 864 | } | ||
| 865 | |||
| 866 | // --- compute saturation vapor pressure | ||
| 867 | 12012 | Temp.ea = 8.1175e6 * exp(-7701.544 / (Temp.ta + 405.0265) ); | |
| 868 | 12012 | } | |
| 869 | |||
| 870 | //============================================================================= | ||
| 871 | |||
| 872 | 686476 | void setEvap(DateTime theDate) | |
| 873 | // | ||
| 874 | // Input: theDate = simulation date | ||
| 875 | // Output: none | ||
| 876 | // Purpose: sets evaporation rate (ft/sec) for a specified date. | ||
| 877 | // | ||
| 878 | { | ||
| 879 | int k; | ||
| 880 | 686476 | int mon = datetime_monthOfYear(theDate); | |
| 881 | |||
| 882 |
5/6✓ Branch 0 taken 674218 times.
✓ Branch 1 taken 90 times.
✓ Branch 2 taken 576 times.
✓ Branch 3 taken 833 times.
✓ Branch 4 taken 10759 times.
✗ Branch 5 not taken.
|
686476 | switch ( Evap.type ) |
| 883 | { | ||
| 884 | 674218 | case CONSTANT_EVAP: | |
| 885 | 674218 | Evap.rate = Evap.monthlyEvap[0] / UCF(EVAPRATE); | |
| 886 | 674218 | break; | |
| 887 | |||
| 888 | 90 | case MONTHLY_EVAP: | |
| 889 | 90 | Evap.rate = Evap.monthlyEvap[mon-1] / UCF(EVAPRATE); | |
| 890 | 90 | break; | |
| 891 | |||
| 892 | 576 | case TIMESERIES_EVAP: | |
| 893 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 574 times.
|
576 | if ( theDate >= NextEvapDate ) |
| 894 | 2 | Evap.rate = NextEvapRate / UCF(EVAPRATE); | |
| 895 | 576 | break; | |
| 896 | |||
| 897 | 833 | case FILE_EVAP: | |
| 898 | 833 | Evap.rate = FileValue[EVAP] / UCF(EVAPRATE); | |
| 899 | 833 | Evap.rate *= Evap.panCoeff[mon-1]; | |
| 900 | 833 | break; | |
| 901 | |||
| 902 | 10759 | case TEMPERATURE_EVAP: | |
| 903 | 10759 | Evap.rate = FileValue[EVAP] / UCF(EVAPRATE); | |
| 904 | 10759 | break; | |
| 905 | |||
| 906 | ✗ | default: Evap.rate = 0.0; | |
| 907 | } | ||
| 908 | |||
| 909 | // --- apply climate change adjustment | ||
| 910 | 686476 | Evap.rate += Adjust.evap[mon-1]; | |
| 911 | |||
| 912 | // --- set soil recovery factor | ||
| 913 | 686476 | Evap.recoveryFactor = 1.0; | |
| 914 | 686476 | k = Evap.recoveryPattern; | |
| 915 |
3/4✓ Branch 0 taken 5760 times.
✓ Branch 1 taken 680716 times.
✓ Branch 2 taken 5760 times.
✗ Branch 3 not taken.
|
686476 | if ( k >= 0 && Pattern[k].type == MONTHLY_PATTERN ) |
| 916 | { | ||
| 917 | 5760 | Evap.recoveryFactor = Pattern[k].factor[mon-1]; | |
| 918 | } | ||
| 919 | 686476 | } | |
| 920 | |||
| 921 | //============================================================================= | ||
| 922 | |||
| 923 | 686476 | void setWind(DateTime theDate) | |
| 924 | // | ||
| 925 | // Input: theDate = simulation date | ||
| 926 | // Output: none | ||
| 927 | // Purpose: sets wind speed (mph) for a specified date. | ||
| 928 | // | ||
| 929 | { | ||
| 930 | int yr, mon, day; | ||
| 931 | |||
| 932 |
2/3✓ Branch 0 taken 686062 times.
✓ Branch 1 taken 414 times.
✗ Branch 2 not taken.
|
686476 | switch ( Wind.type ) |
| 933 | { | ||
| 934 | 686062 | case MONTHLY_WIND: | |
| 935 | 686062 | datetime_decodeDate(theDate, &yr, &mon, &day); | |
| 936 | 686062 | Wind.ws = Wind.aws[mon-1] / UCF(WINDSPEED); | |
| 937 | 686062 | break; | |
| 938 | |||
| 939 | 414 | case FILE_WIND: | |
| 940 | 414 | Wind.ws = FileValue[WIND]; | |
| 941 | 414 | break; | |
| 942 | |||
| 943 | ✗ | default: Wind.ws = 0.0; | |
| 944 | } | ||
| 945 | 686476 | } | |
| 946 | |||
| 947 | //============================================================================= | ||
| 948 | |||
| 949 | 52 | void updateTempTimes(int day) | |
| 950 | // | ||
| 951 | // Input: day = day of year | ||
| 952 | // Output: none | ||
| 953 | // Purpose: computes time of day when min/max temperatures occur. | ||
| 954 | // (min. temp occurs at sunrise, max. temp. at 3 hrs. < sunset) | ||
| 955 | // | ||
| 956 | { | ||
| 957 | double decl; // earth's declination | ||
| 958 | double hrang; // hour angle of sunrise/sunset | ||
| 959 | double arg; | ||
| 960 | |||
| 961 | 52 | decl = 0.40928*cos(0.017202*(172.0-day)); | |
| 962 | 52 | arg = -tan(decl)*Temp.tanAnglat; | |
| 963 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
|
52 | if ( arg <= -1.0 ) arg = PI; |
| 964 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
|
52 | else if ( arg >= 1.0 ) arg = 0.0; |
| 965 | 52 | else arg = acos(arg); | |
| 966 | 52 | hrang = 3.8197 * arg; | |
| 967 | 52 | Hrsr = 12.0 - hrang + Temp.dtlong; | |
| 968 | 52 | Hrss = 12.0 + hrang + Temp.dtlong - 3.0; | |
| 969 | 52 | Dhrdy = Hrsr - Hrss; | |
| 970 | 52 | Dydif = 24.0 + Hrsr - Hrss; | |
| 971 | 52 | Hrday = (Hrsr + Hrss) / 2.0; | |
| 972 | 52 | Tave = (Tmin + Tmax) / 2.0; | |
| 973 | 52 | Trng = (Tmax - Tmin) / 2.0; | |
| 974 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 41 times.
|
52 | if ( Temp.tmax == MISSING ) Trng1 = Tmax - Tmin; |
| 975 | 41 | else Trng1 = Temp.tmax - Tmin; | |
| 976 | 52 | Temp.tmax = Tmax; | |
| 977 | 52 | } | |
| 978 | |||
| 979 | //============================================================================= | ||
| 980 | |||
| 981 | 44 | double getTempEvap(int day, double tave, double trng) | |
| 982 | // | ||
| 983 | // Input: day = day of year | ||
| 984 | // tave = 7-day average temperature (deg F) | ||
| 985 | // trng = 7-day average daily temperature range (deg F) | ||
| 986 | // Output: returns evaporation rate in user's units (US:in/day, SI:mm/day) | ||
| 987 | // Purpose: uses Hargreaves method to compute daily evaporation rate | ||
| 988 | // from daily average temperatures and Julian day. | ||
| 989 | // | ||
| 990 | { | ||
| 991 | 44 | double a = 2.0*PI/365.0; | |
| 992 | 44 | double ta = (tave - 32.0)*5.0/9.0; //average temperature (deg C) | |
| 993 | 44 | double tr = trng*5.0/9.0; //temperature range (deg C) | |
| 994 | 44 | double lamda = 2.50 - 0.002361 * ta; //latent heat of vaporization | |
| 995 | 44 | double dr = 1.0 + 0.033*cos(a*day); //relative earth-sun distance | |
| 996 | 44 | double phi = Temp.anglat*2.0*PI/360.0; //latitude angle (rad) | |
| 997 | 44 | double del = 0.4093*sin(a*(284.+(double)day)); //solar declination angle (rad) | |
| 998 | 44 | double omega = acos(-tan(phi)*tan(del)); //sunset hour angle (rad) | |
| 999 | 44 | double ra = 37.6*dr* //extraterrestrial radiation | |
| 1000 | 44 | (omega*sin(phi)*sin(del) + | |
| 1001 | 44 | cos(phi)*cos(del)*sin(omega)); | |
| 1002 | 44 | double e = 0.0023*ra/lamda*sqrt(tr)*(ta+17.8); //evap. rate (mm/day) | |
| 1003 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
|
44 | if ( e < 0.0 ) e = 0.0; |
| 1004 |
1/2✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
|
44 | if ( UnitSystem == US ) e /= MMperINCH; //evap rate (in/day) |
| 1005 | 44 | return e; | |
| 1006 | } | ||
| 1007 | |||
| 1008 | //============================================================================= | ||
| 1009 | |||
| 1010 | 11 | int getFileFormat() | |
| 1011 | // | ||
| 1012 | // Input: none | ||
| 1013 | // Output: returns code number of climate file's format | ||
| 1014 | // Purpose: determines what format the climate file is in. | ||
| 1015 | // | ||
| 1016 | { | ||
| 1017 | 11 | char recdType[4] = ""; | |
| 1018 | 11 | char elemType[4] = ""; | |
| 1019 | 11 | char filler[5] = ""; | |
| 1020 | char staID[80]; | ||
| 1021 | char s[80]; | ||
| 1022 | char line[MAXLINE]; | ||
| 1023 | |||
| 1024 | int y, m, d, n; | ||
| 1025 | |||
| 1026 | // --- read first line of file | ||
| 1027 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
|
11 | if ( fgets(line, MAXLINE, Fclimate.file) == NULL ) return UNKNOWN_FORMAT; |
| 1028 | |||
| 1029 | // --- check for TD3200 format | ||
| 1030 | 11 | sstrncpy(recdType, line, 3); | |
| 1031 | 11 | sstrncpy(filler, &line[23], 4); | |
| 1032 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 10 times.
|
11 | if ( strcmp(recdType, "DLY") == 0 && |
| 1033 |
1/2✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
|
1 | strcmp(filler, "9999") == 0 ) return TD3200; |
| 1034 | |||
| 1035 | // --- check for DLY0204 format | ||
| 1036 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 9 times.
|
10 | if ( strlen(line) >= 233 ) |
| 1037 | { | ||
| 1038 | 1 | sstrncpy(elemType, &line[13], 3); | |
| 1039 | 1 | n = atoi(elemType); | |
| 1040 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
1 | if ( n == 1 || n == 2 || n == 151 ) return DLY0204; |
| 1041 | } | ||
| 1042 | |||
| 1043 | // --- check for USER_PREPARED format | ||
| 1044 | 9 | n = sscanf(line, "%s %d %d %d %s", staID, &y, &m, &d, s); | |
| 1045 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 2 times.
|
9 | if ( n == 5 ) return USER_PREPARED; |
| 1046 | |||
| 1047 | // --- check for GHCND format | ||
| 1048 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | if ( isGhcndFormat(line) ) return GHCND; |
| 1049 | |||
| 1050 | ✗ | return UNKNOWN_FORMAT; | |
| 1051 | } | ||
| 1052 | |||
| 1053 | //============================================================================= | ||
| 1054 | |||
| 1055 | 29515 | void readFileLine(int *y, int *m) | |
| 1056 | // | ||
| 1057 | // Input: none | ||
| 1058 | // Output: y = year | ||
| 1059 | // m = month | ||
| 1060 | // Purpose: reads year & month from next line of climate file. | ||
| 1061 | // | ||
| 1062 | { | ||
| 1063 | // --- read next line from climate data file | ||
| 1064 |
2/2✓ Branch 0 taken 29503 times.
✓ Branch 1 taken 29505 times.
|
88523 | while ( strlen(FileLine) == 0 ) |
| 1065 | { | ||
| 1066 |
2/2✓ Branch 1 taken 10 times.
✓ Branch 2 taken 29493 times.
|
29503 | if ( fgets(FileLine, MAXLINE, Fclimate.file) == NULL ) return; |
| 1067 |
1/2✓ Branch 0 taken 29493 times.
✗ Branch 1 not taken.
|
29493 | if ( FileLine[0] == '\n' ) FileLine[0] = '\0'; |
| 1068 | } | ||
| 1069 | |||
| 1070 | // --- parse year & month from line | ||
| 1071 |
4/5✓ Branch 0 taken 29455 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 41 times.
✗ Branch 4 not taken.
|
29505 | switch (FileFormat) |
| 1072 | { | ||
| 1073 | 29455 | case USER_PREPARED: readUserFileLine(y, m); break; | |
| 1074 | 5 | case TD3200: readTD3200FileLine(y,m); break; | |
| 1075 | 4 | case DLY0204: readDLY0204FileLine(y,m); break; | |
| 1076 | 41 | case GHCND: readGhcndFileLine(y,m); break; | |
| 1077 | } | ||
| 1078 | } | ||
| 1079 | |||
| 1080 | //============================================================================= | ||
| 1081 | |||
| 1082 | 29455 | void readUserFileLine(int* y, int* m) | |
| 1083 | // | ||
| 1084 | // Input: none | ||
| 1085 | // Output: y = year | ||
| 1086 | // m = month | ||
| 1087 | // Purpose: reads year & month from line of User-Prepared climate file. | ||
| 1088 | // | ||
| 1089 | { | ||
| 1090 | int n; | ||
| 1091 | char staID[80]; | ||
| 1092 | 29455 | n = sscanf(FileLine, "%s %d %d", staID, y, m); | |
| 1093 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29455 times.
|
29455 | if ( n < 3 ) |
| 1094 | { | ||
| 1095 | ✗ | report_writeErrorMsg(ERR_CLIMATE_FILE_READ, Fclimate.name); | |
| 1096 | } | ||
| 1097 | 29455 | } | |
| 1098 | |||
| 1099 | //============================================================================= | ||
| 1100 | |||
| 1101 | 5 | void readTD3200FileLine(int* y, int* m) | |
| 1102 | // | ||
| 1103 | // Input: none | ||
| 1104 | // Output: y = year | ||
| 1105 | // m = month | ||
| 1106 | // Purpose: reads year & month from line of TD-3200 climate file. | ||
| 1107 | // | ||
| 1108 | { | ||
| 1109 | 5 | char recdType[4] = ""; | |
| 1110 | 5 | char year[5] = ""; | |
| 1111 | 5 | char month[3] = ""; | |
| 1112 | |||
| 1113 | // --- check for minimum number of characters | ||
| 1114 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if ( strlen(FileLine) < 30 ) |
| 1115 | { | ||
| 1116 | ✗ | report_writeErrorMsg(ERR_CLIMATE_FILE_READ, Fclimate.name); | |
| 1117 | ✗ | return; | |
| 1118 | } | ||
| 1119 | |||
| 1120 | // --- check for proper type of record | ||
| 1121 | 5 | sstrncpy(recdType, FileLine, 3); | |
| 1122 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if ( strcmp(recdType, "DLY") != 0 ) |
| 1123 | { | ||
| 1124 | ✗ | report_writeErrorMsg(ERR_CLIMATE_FILE_READ, Fclimate.name); | |
| 1125 | ✗ | return; | |
| 1126 | } | ||
| 1127 | |||
| 1128 | // --- get record's date | ||
| 1129 | 5 | sstrncpy(year, &FileLine[17], 4); | |
| 1130 | 5 | sstrncpy(month, &FileLine[21], 2); | |
| 1131 | 5 | *y = atoi(year); | |
| 1132 | 5 | *m = atoi(month); | |
| 1133 | } | ||
| 1134 | |||
| 1135 | //============================================================================= | ||
| 1136 | |||
| 1137 | 4 | void readDLY0204FileLine(int* y, int* m) | |
| 1138 | // | ||
| 1139 | // Input: none | ||
| 1140 | // Output: y = year | ||
| 1141 | // m = month | ||
| 1142 | // Purpose: reads year & month from line of DLY02 or DLY04 climate file. | ||
| 1143 | // | ||
| 1144 | { | ||
| 1145 | 4 | char year[5] = ""; | |
| 1146 | 4 | char month[3] = ""; | |
| 1147 | |||
| 1148 | // --- check for minimum number of characters | ||
| 1149 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if ( strlen(FileLine) < 16 ) |
| 1150 | { | ||
| 1151 | ✗ | report_writeErrorMsg(ERR_CLIMATE_FILE_READ, Fclimate.name); | |
| 1152 | ✗ | return; | |
| 1153 | } | ||
| 1154 | |||
| 1155 | // --- get record's date | ||
| 1156 | 4 | sstrncpy(year, &FileLine[7], 4); | |
| 1157 | 4 | sstrncpy(month, &FileLine[11], 2); | |
| 1158 | 4 | *y = atoi(year); | |
| 1159 | 4 | *m = atoi(month); | |
| 1160 | } | ||
| 1161 | |||
| 1162 | //============================================================================= | ||
| 1163 | |||
| 1164 | 12 | void readFileValues() | |
| 1165 | // | ||
| 1166 | // Input: none | ||
| 1167 | // Output: none | ||
| 1168 | // Purpose: reads next month's worth of data from climate file. | ||
| 1169 | // | ||
| 1170 | { | ||
| 1171 | int i, j; | ||
| 1172 | int y, m; | ||
| 1173 | |||
| 1174 | // --- initialize FileData array to missing values | ||
| 1175 |
2/2✓ Branch 0 taken 48 times.
✓ Branch 1 taken 12 times.
|
60 | for ( i=0; i<MAXCLIMATEVARS; i++) |
| 1176 | { | ||
| 1177 |
2/2✓ Branch 0 taken 1536 times.
✓ Branch 1 taken 48 times.
|
1584 | for (j=0; j<MAXDAYSPERMONTH; j++) FileData[i][j] = MISSING; |
| 1178 | } | ||
| 1179 | |||
| 1180 |
1/2✓ Branch 0 taken 158 times.
✗ Branch 1 not taken.
|
158 | while ( !ErrorCode ) |
| 1181 | { | ||
| 1182 | // --- return when date on line is after current file date | ||
| 1183 |
2/2✓ Branch 1 taken 10 times.
✓ Branch 2 taken 148 times.
|
160 | if ( feof(Fclimate.file) ) return; |
| 1184 | 148 | readFileLine(&y, &m); | |
| 1185 |
4/4✓ Branch 0 taken 147 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 146 times.
|
148 | if ( y > FileYear || m > FileMonth ) return; |
| 1186 | |||
| 1187 | // --- parse climate values from file line | ||
| 1188 |
4/5✓ Branch 0 taken 98 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 39 times.
✗ Branch 4 not taken.
|
146 | switch (FileFormat) |
| 1189 | { | ||
| 1190 | 98 | case USER_PREPARED: parseUserFileLine(); break; | |
| 1191 | 5 | case TD3200: parseTD3200FileLine(); break; | |
| 1192 | 4 | case DLY0204: parseDLY0204FileLine(); break; | |
| 1193 | 39 | case GHCND: parseGhcndFileLine(); break; | |
| 1194 | } | ||
| 1195 | 146 | sstrncpy(FileLine, "", 0); | |
| 1196 | } | ||
| 1197 | } | ||
| 1198 | |||
| 1199 | //============================================================================= | ||
| 1200 | |||
| 1201 | 98 | void parseUserFileLine() | |
| 1202 | // | ||
| 1203 | // Input: none | ||
| 1204 | // Output: none | ||
| 1205 | // Purpose: parses climate variable values from a line of a user-prepared | ||
| 1206 | // climate file. | ||
| 1207 | // | ||
| 1208 | { | ||
| 1209 | int n; | ||
| 1210 | int y, m, d; | ||
| 1211 | char staID[80]; | ||
| 1212 | 98 | char s0[80] = ""; | |
| 1213 | 98 | char s1[80] = ""; | |
| 1214 | 98 | char s2[80] = ""; | |
| 1215 | 98 | char s3[80] = ""; | |
| 1216 | double x; | ||
| 1217 | |||
| 1218 | // --- read day, Tmax, Tmin, Evap, & Wind from file line | ||
| 1219 | 98 | n = sscanf(FileLine, "%s %d %d %d %s %s %s %s", | |
| 1220 | staID, &y, &m, &d, s0, s1, s2, s3); | ||
| 1221 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 92 times.
|
98 | if ( n < 4 ) return; |
| 1222 |
2/4✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 92 times.
|
92 | if ( d < 1 || d > 31 ) return; |
| 1223 | |||
| 1224 | // --- process TMAX | ||
| 1225 |
2/4✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 92 times.
✗ Branch 3 not taken.
|
92 | if ( strlen(s0) > 0 && *s0 != '*' ) |
| 1226 | { | ||
| 1227 | 92 | x = atof(s0); | |
| 1228 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 92 times.
|
92 | if ( UnitSystem == SI ) x = 9./5.*x + 32.0; |
| 1229 | 92 | FileData[TMAX][d] = x; | |
| 1230 | } | ||
| 1231 | |||
| 1232 | // --- process TMIN | ||
| 1233 |
2/4✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 92 times.
✗ Branch 3 not taken.
|
92 | if ( strlen(s1) > 0 && *s1 != '*' ) |
| 1234 | { | ||
| 1235 | 92 | x = atof(s1); | |
| 1236 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 92 times.
|
92 | if ( UnitSystem == SI ) x = 9./5.*x + 32.0; |
| 1237 | 92 | FileData[TMIN][d] = x; | |
| 1238 | } | ||
| 1239 | |||
| 1240 | // --- process EVAP | ||
| 1241 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
92 | if ( strlen(s2) > 0 && *s2 != '*' ) FileData[EVAP][d] = atof(s2); |
| 1242 | |||
| 1243 | // --- process WIND | ||
| 1244 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
92 | if ( strlen(s3) > 0 && *s3 != '*' ) FileData[WIND][d] = atof(s3); |
| 1245 | } | ||
| 1246 | |||
| 1247 | //============================================================================= | ||
| 1248 | |||
| 1249 | 5 | void parseTD3200FileLine() | |
| 1250 | // | ||
| 1251 | // Input: none | ||
| 1252 | // Output: none | ||
| 1253 | // Purpose: parses climate variable values from a line of a TD3200 file. | ||
| 1254 | // | ||
| 1255 | { | ||
| 1256 | int i; | ||
| 1257 | 5 | char param[5] = ""; | |
| 1258 | |||
| 1259 | // --- parse parameter name | ||
| 1260 | 5 | sstrncpy(param, &FileLine[11], 4); | |
| 1261 | |||
| 1262 | // --- see if parameter is temperature, evaporation or wind speed | ||
| 1263 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 5 times.
|
25 | for (i=0; i<MAXCLIMATEVARS; i++) |
| 1264 | { | ||
| 1265 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 17 times.
|
20 | if (strcmp(param, ClimateVarWords[i]) == 0 ) setTD3200FileValues(i); |
| 1266 | } | ||
| 1267 | 5 | } | |
| 1268 | |||
| 1269 | //============================================================================= | ||
| 1270 | |||
| 1271 | 3 | void setTD3200FileValues(int i) | |
| 1272 | // | ||
| 1273 | // Input: i = climate variable code | ||
| 1274 | // Output: none | ||
| 1275 | // Purpose: reads month worth of values for climate variable from TD-3200 file. | ||
| 1276 | // | ||
| 1277 | { | ||
| 1278 | 3 | char valCount[4] = ""; | |
| 1279 | 3 | char day[3] = ""; | |
| 1280 | 3 | char sign[2] = ""; | |
| 1281 | 3 | char value[6] = ""; | |
| 1282 | 3 | char flag2[2] = ""; | |
| 1283 | double x; | ||
| 1284 | int nValues; | ||
| 1285 | int j, k, d; | ||
| 1286 | |||
| 1287 | // --- parse number of days with data from cols. 27-29 of file line | ||
| 1288 | 3 | sstrncpy(valCount, &FileLine[27], 3); | |
| 1289 | 3 | nValues = atoi(valCount); | |
| 1290 | |||
| 1291 | // --- check for enough characters on line | ||
| 1292 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if ( (int)strlen(FileLine) >= 12*nValues + 30 ) |
| 1293 | { | ||
| 1294 | // --- for each day's value | ||
| 1295 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 3 times.
|
18 | for (j=0; j<nValues; j++) |
| 1296 | { | ||
| 1297 | // --- parse day, value & flag from file line | ||
| 1298 | 15 | k = 30 + j*12; | |
| 1299 | 15 | sstrncpy(day, &FileLine[k], 2); | |
| 1300 | 15 | sstrncpy(sign, &FileLine[k+4], 1); | |
| 1301 | 15 | sstrncpy(value, &FileLine[k+5], 5); | |
| 1302 | 15 | sstrncpy(flag2, &FileLine[k+11], 1); | |
| 1303 | |||
| 1304 | // --- if value is valid then store it in FileData array | ||
| 1305 | 15 | d = atoi(day); | |
| 1306 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
15 | if ( strcmp(value, "99999") != 0 |
| 1307 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
15 | && ( flag2[0] == '0' || flag2[0] == '1') |
| 1308 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
15 | && d > 0 |
| 1309 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
15 | && d <= 31 ) |
| 1310 | { | ||
| 1311 | // --- convert from string value to numerical value | ||
| 1312 | 15 | x = atof(value); | |
| 1313 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
|
15 | if ( sign[0] == '-' ) x = -x; |
| 1314 | |||
| 1315 | // --- convert evaporation from hundreths of inches | ||
| 1316 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 10 times.
|
15 | if ( i == EVAP ) |
| 1317 | { | ||
| 1318 | 5 | x /= 100.0; | |
| 1319 | |||
| 1320 | // --- convert to mm if using SI units | ||
| 1321 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if ( UnitSystem == SI ) x *= MMperINCH; |
| 1322 | } | ||
| 1323 | |||
| 1324 | // --- convert wind speed from miles/day to miles/hour | ||
| 1325 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
|
15 | if ( i == WIND ) x /= 24.0; |
| 1326 | |||
| 1327 | // --- store value | ||
| 1328 | 15 | FileData[i][d] = x; | |
| 1329 | } | ||
| 1330 | } | ||
| 1331 | } | ||
| 1332 | 3 | } | |
| 1333 | |||
| 1334 | //============================================================================= | ||
| 1335 | |||
| 1336 | 4 | void parseDLY0204FileLine() | |
| 1337 | // | ||
| 1338 | // Input: none | ||
| 1339 | // Output: none | ||
| 1340 | // Purpose: parses a month's worth of climate variable values from a line of | ||
| 1341 | // a DLY02 or DLY04 climate file. | ||
| 1342 | // | ||
| 1343 | { | ||
| 1344 | int j, k, p; | ||
| 1345 | 4 | char param[4] = ""; | |
| 1346 | 4 | char sign[2] = ""; | |
| 1347 | 4 | char value[6] = ""; | |
| 1348 | 4 | char code[2] = ""; | |
| 1349 | double x; | ||
| 1350 | |||
| 1351 | // --- parse parameter name | ||
| 1352 | 4 | sstrncpy(param, &FileLine[13], 3); | |
| 1353 | |||
| 1354 | // --- see if parameter is min or max temperature | ||
| 1355 | 4 | p = atoi(param); | |
| 1356 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3 times.
|
4 | if ( p == 1 ) p = TMAX; |
| 1357 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
|
3 | else if ( p == 2 ) p = TMIN; |
| 1358 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | else if ( p == 151 ) p = EVAP; |
| 1359 | 1 | else return; | |
| 1360 | |||
| 1361 | // --- check for 233 characters on line | ||
| 1362 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3 times.
|
4 | if ( strlen(FileLine) < 233 ) return; |
| 1363 | |||
| 1364 | // --- for each of 31 days | ||
| 1365 | 3 | k = 16; | |
| 1366 |
2/2✓ Branch 0 taken 93 times.
✓ Branch 1 taken 3 times.
|
96 | for (j=1; j<=31; j++) |
| 1367 | { | ||
| 1368 | // --- parse value & flag from file line | ||
| 1369 | 93 | sstrncpy(sign, &FileLine[k], 1); | |
| 1370 | 93 | sstrncpy(value, &FileLine[k+1], 5); | |
| 1371 | 93 | sstrncpy(code, &FileLine[k+6], 1); | |
| 1372 | 93 | k += 7; | |
| 1373 | |||
| 1374 | // --- if value is valid then store it in FileData array | ||
| 1375 | |||
| 1376 |
2/4✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 93 times.
✗ Branch 3 not taken.
|
93 | if ( strcmp(value, "99999") != 0 && strcmp(value, " ") != 0 ) |
| 1377 | { | ||
| 1378 |
2/3✓ Branch 0 taken 62 times.
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
|
93 | switch (p) |
| 1379 | { | ||
| 1380 | 62 | case TMAX: | |
| 1381 | case TMIN: | ||
| 1382 | // --- convert from integer tenths of a degree C to degrees F | ||
| 1383 | 62 | x = atof(value) / 10.0; | |
| 1384 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
|
62 | if ( sign[0] == '-' ) x = -x; |
| 1385 | 62 | x = 9./5.*x + 32.0; | |
| 1386 | 62 | break; | |
| 1387 | 31 | case EVAP: | |
| 1388 | // --- convert from 0.1 mm to inches or mm | ||
| 1389 | 31 | x = atof(value) / 10.0; | |
| 1390 |
1/2✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
|
31 | if ( UnitSystem == US ) x /= MMperINCH; |
| 1391 | 31 | break; | |
| 1392 | ✗ | default: return; | |
| 1393 | } | ||
| 1394 | 93 | FileData[p][j] = x; | |
| 1395 | } | ||
| 1396 | } | ||
| 1397 | } | ||
| 1398 | |||
| 1399 | //============================================================================= | ||
| 1400 | |||
| 1401 | 2 | int isGhcndFormat(char* line) | |
| 1402 | // | ||
| 1403 | // Input: line = first line of text from a climate file | ||
| 1404 | // Output: returns TRUE if climate file is in NCDC GHCN Daily format. | ||
| 1405 | // Purpose: Checks if a climate file is in the NCDC GHCN Daily format | ||
| 1406 | // and determines the position of each climate variable field. | ||
| 1407 | // | ||
| 1408 | { | ||
| 1409 | int i; | ||
| 1410 | char* ptr; | ||
| 1411 | |||
| 1412 | // --- find starting position of the DATE field | ||
| 1413 | 2 | ptr = strstr(line, "DATE"); | |
| 1414 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if ( ptr == NULL ) return FALSE; |
| 1415 | 2 | FileDateFieldPos = (int)(ptr - line); | |
| 1416 | |||
| 1417 | // --- initialize starting position of each data field | ||
| 1418 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
|
10 | for ( i = TMIN; i <= WIND; i++) FileFieldPos[i] = -1; |
| 1419 | |||
| 1420 | // --- find starting position of each climate variable's data field | ||
| 1421 | 2 | ptr = strstr(line, "TMIN"); | |
| 1422 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if ( ptr ) FileFieldPos[TMIN] = (int)(ptr - line); |
| 1423 | 2 | ptr = strstr(line, "TMAX"); | |
| 1424 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if ( ptr ) FileFieldPos[TMAX] = (int)(ptr - line); |
| 1425 | 2 | ptr = strstr(line, "EVAP"); | |
| 1426 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if ( ptr ) FileFieldPos[EVAP] = (int)(ptr - line); |
| 1427 | |||
| 1428 | // --- WIND can either be daily movement or average speed | ||
| 1429 | 2 | FileWindType = WDMV; | |
| 1430 | 2 | ptr = strstr(line, "WDMV"); | |
| 1431 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if ( ptr == NULL ) |
| 1432 | { | ||
| 1433 | 2 | FileWindType = AWND; | |
| 1434 | 2 | ptr = strstr(line, "AWND"); | |
| 1435 | } | ||
| 1436 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if ( ptr ) FileFieldPos[WIND] = (int)(ptr - line); |
| 1437 | |||
| 1438 | // --- check if at least one climate variable was found | ||
| 1439 |
2/4✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | for (i = TMIN; i <= WIND; i++) if (FileFieldPos[i] >= 0 ) return TRUE; |
| 1440 | ✗ | return FALSE; | |
| 1441 | } | ||
| 1442 | |||
| 1443 | //============================================================================= | ||
| 1444 | |||
| 1445 | 41 | void readGhcndFileLine(int* y, int* m) | |
| 1446 | // | ||
| 1447 | // Input: none | ||
| 1448 | // Output: y = year | ||
| 1449 | // m = month | ||
| 1450 | // Purpose: reads year & month from line of a NCDC GHCN Daily climate file. | ||
| 1451 | // | ||
| 1452 | { | ||
| 1453 | 41 | int n = sscanf(&FileLine[FileDateFieldPos], "%4d%2d", y, m); | |
| 1454 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 39 times.
|
41 | if ( n != 2 ) |
| 1455 | { | ||
| 1456 | 2 | *y = -99999; | |
| 1457 | 2 | *m = -99999; | |
| 1458 | } | ||
| 1459 | 41 | } | |
| 1460 | |||
| 1461 | //============================================================================= | ||
| 1462 | |||
| 1463 | 39 | void parseGhcndFileLine() | |
| 1464 | // | ||
| 1465 | // Input: none | ||
| 1466 | // Output: none | ||
| 1467 | // Purpose: parses a line of a NCDC GHCN Daily file for daily | ||
| 1468 | // values of max/min temperature, pan evaporation and | ||
| 1469 | // wind speed. | ||
| 1470 | // | ||
| 1471 | { | ||
| 1472 | int y, m, d, n, i; | ||
| 1473 | double v; | ||
| 1474 | |||
| 1475 | // --- parse day of month from date field | ||
| 1476 | 39 | n = sscanf(&FileLine[FileDateFieldPos], "%4d%2d%2d", &y, &m, &d); | |
| 1477 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 37 times.
|
39 | if ( n < 3 ) return; |
| 1478 |
2/4✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 37 times.
|
37 | if ( d < 1 || d > 31 ) return; |
| 1479 | |||
| 1480 | // --- parse climate variables | ||
| 1481 |
2/2✓ Branch 0 taken 148 times.
✓ Branch 1 taken 37 times.
|
185 | for (i = TMIN; i <= WIND; i++) |
| 1482 | { | ||
| 1483 |
2/2✓ Branch 0 taken 74 times.
✓ Branch 1 taken 74 times.
|
148 | if ( FileFieldPos[i] >= 0 ) |
| 1484 | { | ||
| 1485 |
1/2✓ Branch 0 taken 74 times.
✗ Branch 1 not taken.
|
74 | if ( sscanf(&FileLine[FileFieldPos[i]], "%8lf", &v) > 0 ) |
| 1486 | { | ||
| 1487 |
1/2✓ Branch 0 taken 74 times.
✗ Branch 1 not taken.
|
74 | if ( fabs(v) < 9999. ) |
| 1488 | 74 | FileData[i][d] = convertGhcndValue(i, v); | |
| 1489 | } | ||
| 1490 | } | ||
| 1491 | } | ||
| 1492 | } | ||
| 1493 | |||
| 1494 | //============================================================================= | ||
| 1495 | |||
| 1496 | 74 | double convertGhcndValue(int var, double v) | |
| 1497 | // | ||
| 1498 | // Input: var = climate variable code | ||
| 1499 | // v = climate variable value | ||
| 1500 | // Output: climate variable value in SWMM's internal units | ||
| 1501 | // Purpose: converts a climate variable value read from a NCDC GHCN Daily file | ||
| 1502 | // to SWMM's internal units. | ||
| 1503 | // | ||
| 1504 | { | ||
| 1505 |
1/4✓ Branch 0 taken 74 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
74 | switch (var) |
| 1506 | { | ||
| 1507 | 74 | case TMIN: | |
| 1508 | case TMAX: | ||
| 1509 |
1/3✓ Branch 0 taken 74 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
|
74 | switch (FileTempUnits) |
| 1510 | { | ||
| 1511 | 74 | case DEG_C10: // tenths deg. C ==> deg. F | |
| 1512 | 74 | return v / 10. * 9.0 / 5.0 + 32.0; | |
| 1513 | |||
| 1514 | ✗ | case DEG_C: // deg. C ==> deg. F | |
| 1515 | ✗ | return v * 9.0 / 5.0 + 32.0; | |
| 1516 | |||
| 1517 | ✗ | default: // deg. F | |
| 1518 | ✗ | return v; | |
| 1519 | } | ||
| 1520 | ✗ | case EVAP: | |
| 1521 | ✗ | switch (FileTempUnits) | |
| 1522 | { | ||
| 1523 | ✗ | case DEG_C10: // tenths mm ==> inches or mm | |
| 1524 | ✗ | v /= 10.; | |
| 1525 | ✗ | if (UnitSystem == US) v /= MMperINCH; | |
| 1526 | ✗ | return v; | |
| 1527 | |||
| 1528 | ✗ | case DEG_C: // mm ==> inches or mm | |
| 1529 | ✗ | if (UnitSystem == US) v /= MMperINCH; | |
| 1530 | ✗ | return v; | |
| 1531 | |||
| 1532 | ✗ | default: // inches ==> inches or mm | |
| 1533 | ✗ | if (UnitSystem == SI) v *= MMperINCH; | |
| 1534 | ✗ | return v; | |
| 1535 | } | ||
| 1536 | ✗ | case WIND: | |
| 1537 | ✗ | switch (FileTempUnits) | |
| 1538 | { | ||
| 1539 | ✗ | case DEG_C10: | |
| 1540 | // km/day ==> miles/hr | ||
| 1541 | ✗ | if (FileWindType == WDMV) | |
| 1542 | ✗ | return v * 0.62137 / 24.; | |
| 1543 | // tenths m/s ==> miles/hr | ||
| 1544 | else | ||
| 1545 | ✗ | return v / 10. / 1000. * 0.62137 * 3600.; | |
| 1546 | ✗ | case DEG_C: | |
| 1547 | // km/day ==> miles/hr | ||
| 1548 | ✗ | if (FileWindType == WDMV) | |
| 1549 | ✗ | return v * 0.62137 / 24.; | |
| 1550 | // m/s ==> miles/hr | ||
| 1551 | else | ||
| 1552 | ✗ | return v / 1000. * 0.62137 * 3600.; | |
| 1553 | |||
| 1554 | ✗ | default: | |
| 1555 | // miles ==> miles/hr | ||
| 1556 | ✗ | if (FileWindType == WDMV) | |
| 1557 | ✗ | return v / 24.; | |
| 1558 | // miles/hr | ||
| 1559 | else | ||
| 1560 | ✗ | return v; | |
| 1561 | } | ||
| 1562 | ✗ | default: | |
| 1563 | ✗ | return v; | |
| 1564 | } | ||
| 1565 | } | ||
| 1566 | |||
| 1567 | //============================================================================= | ||
| 1568 | |||
| 1569 | 44 | void updateTempMoveAve(double tmin, double tmax) | |
| 1570 | // | ||
| 1571 | // Input: tmin = minimum daily temperature (deg F) | ||
| 1572 | // tmax = maximum daily temperature (deg F) | ||
| 1573 | // Output: none | ||
| 1574 | // Purpose: updates moving averages of average daily temperature | ||
| 1575 | // and daily temperature range stored in structure Tma. | ||
| 1576 | // | ||
| 1577 | { | ||
| 1578 | double ta, // new day's average temperature (deg F) | ||
| 1579 | tr; // new day's temperature range (deg F) | ||
| 1580 | 44 | int kount = Tma.count; | |
| 1581 | 44 | double count = kount; | |
| 1582 | |||
| 1583 | // --- find ta and tr from new day's min and max temperature | ||
| 1584 | 44 | ta = (tmin + tmax) / 2.0; | |
| 1585 | 44 | tr = fabs(tmax - tmin); | |
| 1586 | |||
| 1587 | // --- if the array used to store previous days' temperatures is full | ||
| 1588 |
2/2✓ Branch 0 taken 25 times.
✓ Branch 1 taken 19 times.
|
44 | if ( kount == Tma.maxCount ) |
| 1589 | { | ||
| 1590 | // --- update the moving averages with the new day's value | ||
| 1591 | 25 | Tma.tAve = (Tma.tAve * count + ta - Tma.ta[Tma.front]) / count; | |
| 1592 | 25 | Tma.tRng = (Tma.tRng * count + tr - Tma.tr[Tma.front]) / count; | |
| 1593 | |||
| 1594 | // --- replace the values at the front of the moving average window | ||
| 1595 | 25 | Tma.ta[Tma.front] = ta; | |
| 1596 | 25 | Tma.tr[Tma.front] = tr; | |
| 1597 | |||
| 1598 | // --- move the front one position forward | ||
| 1599 | 25 | Tma.front++; | |
| 1600 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 22 times.
|
25 | if ( Tma.front == count ) Tma.front = 0; |
| 1601 | } | ||
| 1602 | |||
| 1603 | // --- array of previous day's values not full (at start of simulation) | ||
| 1604 | else | ||
| 1605 | { | ||
| 1606 | // --- find new moving averages by adding new values to previous ones | ||
| 1607 | 19 | Tma.tAve = (Tma.tAve * count + ta) / (count + 1); | |
| 1608 | 19 | Tma.tRng = (Tma.tRng * count + tr) / (count + 1); | |
| 1609 | |||
| 1610 | // --- save new day's values | ||
| 1611 | 19 | Tma.ta[Tma.front] = ta; | |
| 1612 | 19 | Tma.tr[Tma.front] = tr; | |
| 1613 | |||
| 1614 | // --- increment count and front of moving average window | ||
| 1615 | 19 | Tma.count++; | |
| 1616 | 19 | Tma.front++; | |
| 1617 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 18 times.
|
19 | if ( Tma.count == Tma.maxCount ) Tma.front = 0; |
| 1618 | } | ||
| 1619 | 44 | } | |
| 1620 |