double exfil_getLoss(TExfil* exfil, double tStep, double depth, double area) // // Input: exfil = ptr. to a storage exfiltration object // tStep = time step (sec) // depth = water depth (ft) // area = surface area (ft2) // Output: returns exfiltration rate out of storage unit (cfs) // Purpose: computes rate of water exfiltrated from a storage node into // the soil beneath it. // { double exfilRate = 0.0; // --- find infiltration through bottom of unit if ( exfil->btmExfil->IMDmax == 0.0 ) { exfilRate = exfil->btmExfil->Ks * Adjust.hydconFactor; //(5.1.008) } else exfilRate = grnampt_getInfil(exfil->btmExfil, tStep, 0.0, depth); exfilRate *= exfil->btmArea; // --- find infiltration through sloped banks if ( depth > exfil->bankMinDepth ) { // --- get area of banks area = MIN(area, exfil->bankMaxArea) - exfil->btmArea; if ( area > 0.0 ) { // --- if infil. rate not a function of depth if ( exfil->btmExfil->IMDmax == 0.0 ) { exfilRate += area * exfil->btmExfil->Ks * Adjust.hydconFactor; //(5.1.008) } // --- infil. rate depends on depth above bank else { // --- case where water depth is above the point where the // storage curve no longer has increasing area with depth if ( depth > exfil->bankMaxDepth ) { depth = depth - exfil->bankMaxDepth + (exfil->bankMaxDepth - exfil->bankMinDepth) / 2.0; } // --- case where water depth is below top of bank else depth = (depth - exfil->bankMinDepth) / 2.0; // --- use Green-Ampt function for bank infiltration exfilRate += area * grnampt_getInfil(exfil->bankExfil, tStep, 0.0, depth); } } } return exfilRate; }
double infil_getInfil(int j, int m, double tstep, double rainfall, double runon, double depth) // // Input: j = subcatchment index // m = infiltration method code // tstep = runoff time step (sec) // rainfall = rainfall rate (ft/sec) // runon = runon rate from other sub-areas or subcatchments (ft/sec) // depth = depth of surface water on subcatchment (ft) // Output: returns infiltration rate (ft/sec) // Purpose: computes infiltration rate depending on infiltration method. // { switch (m) { case HORTON: return horton_getInfil(&HortInfil[j], tstep, rainfall+runon, depth); case MOD_HORTON: return modHorton_getInfil(&HortInfil[j], tstep, rainfall+runon, depth); case GREEN_AMPT: case MOD_GREEN_AMPT: //(5.1.010) return grnampt_getInfil(&GAInfil[j], tstep, rainfall+runon, depth, m); //(5.1.010) case CURVE_NUMBER: depth += runon / tstep; return curvenum_getInfil(&CNInfil[j], tstep, rainfall, depth); default: return 0.0; } }
double lidproc_getOutflow(TLidUnit* lidUnit, TLidProc* lidProc, double inflow, double rain, double evap, double infil, double maxInfil, double tStep, double* lidEvap, double* lidInfil) // // Purpose: computes runoff outflow from a single LID unit. // Input: theUnit = ptr. to specific LID unit being analyzed // theProc = ptr. to generic LID process of the LID unit // inflow = runoff rate captured by LID unit (ft/s) // rain = current rainfall on LID unit (ft/s) // evap = potential evaporation rate (ft/s) // infil = infiltration rate of native soil (ft/s) // maxInfil = max. infiltration rate to native soil (ft/s) // tStep = time step (sec) // Output: lidEvap = evaporation rate for LID unit (ft/s) // lidInfil = infiltration rate for LID unit (ft/s) // returns surface runoff rate from the LID unit (ft/s) // { int i; double x[MAX_STATE_VARS]; // layer moisture levels double xOld[MAX_STATE_VARS]; // work vector double xPrev[MAX_STATE_VARS]; // work vector double xMin[MAX_STATE_VARS]; // lower limit on moisture levels double xMax[MAX_STATE_VARS]; // upper limit on moisture levels double fOld[MAX_STATE_VARS]; // previously computed flux rates double f[MAX_STATE_VARS]; // newly computed flux rates double xTol[] = // convergence tolerance on moisture {STOPTOL, STOPTOL, STOPTOL}; // levels (ft, moisture fraction , ft) double omega = 0.0; // integration time weighting //(5.1.007) //... define a pointer to function that computes flux rates through the LID void (*fluxRates) (double *, double *) = NULL; //... save references to the LID process and LID unit theLidProc = lidProc; theLidUnit = lidUnit; //... save rain, evap, max. infil. & time step to shared variables Rainfall = rain; EvapRate = evap; MaxNativeInfil = maxInfil; Tstep = tStep; //... store current moisture levels in vector x x[SURF] = theLidUnit->surfaceDepth; x[SOIL] = theLidUnit->soilMoisture; x[STOR] = theLidUnit->storageDepth; //... initialize layer flux rates and moisture limits SurfaceInflow = inflow; SurfaceInfil = 0.0; SurfaceEvap = 0.0; SurfaceOutflow = 0.0; SoilEvap = 0.0; SoilPerc = 0.0; StorageInflow = 0.0; StorageInfil = 0.0; StorageEvap = 0.0; StorageDrain = 0.0; for (i = 0; i < MAX_STATE_VARS; i++) { f[i] = 0.0; fOld[i] = theLidUnit->oldFluxRates[i]; xMin[i] = 0.0; xMax[i] = BIG; Xold[i] = x[i]; } //... find Green-Ampt infiltration from surface layer if ( theLidUnit->soilInfil.Ks > 0.0 ) { SurfaceInfil = grnampt_getInfil(&theLidUnit->soilInfil, Tstep, SurfaceInflow, theLidUnit->surfaceDepth); } else SurfaceInfil = infil; //... set moisture limits for soil & storage layers if ( theLidProc->soil.thickness > 0.0 ) { xMin[SOIL] = theLidProc->soil.wiltPoint; xMax[SOIL] = theLidProc->soil.porosity; } if ( theLidProc->pavement.thickness > 0.0 ) { xMax[SOIL] = theLidProc->pavement.voidFrac; } if ( theLidProc->storage.thickness > 0.0 ) { xMax[STOR] = theLidProc->storage.thickness; } if ( theLidProc->lidType == GREEN_ROOF ) { xMax[STOR] = theLidProc->drainMat.thickness; } //... determine which flux rate function to use switch (theLidProc->lidType) { case BIO_CELL: case RAIN_GARDEN: fluxRates = &biocellFluxRates; break; case GREEN_ROOF: fluxRates = &greenRoofFluxRates; break; case INFIL_TRENCH: fluxRates = &trenchFluxRates; break; case POROUS_PAVEMENT: fluxRates = &pavementFluxRates; break; case RAIN_BARREL: fluxRates = &barrelFluxRates; break; case VEG_SWALE: fluxRates = &swaleFluxRates; omega = 0.5; //(5.1.007) break; default: return 0.0; } //... update moisture levels and flux rates over the time step i = modpuls_solve(MAX_STATE_VARS, x, xOld, xPrev, xMin, xMax, xTol, fOld, f, tStep, omega, fluxRates); //(5.1.007) /** For debugging only ******************************************** if (i == 0) { fprintf(Frpt.file, "\n WARNING 09: integration failed to converge at %s %s", theDate, theTime); fprintf(Frpt.file, "\n for LID %s placed in subcatchment %s.", theLidProc->ID, theSubcatch->ID); } *******************************************************************/ //... add any surface overflow to surface outflow if ( theLidProc->surface.canOverflow || theLidUnit->fullWidth == 0.0 ) { SurfaceOutflow += getSurfaceOverflowRate(&x[SURF]); } //... save updated results theLidUnit->surfaceDepth = x[SURF]; theLidUnit->soilMoisture = x[SOIL]; theLidUnit->storageDepth = x[STOR]; for (i = 0; i < MAX_STATE_VARS; i++) theLidUnit->oldFluxRates[i] = f[i]; //... assign values to LID unit evaporation & infiltration *lidEvap = SurfaceEvap + SoilEvap + StorageEvap; *lidInfil = StorageInfil; //... return total outflow (per unit area) from unit return SurfaceOutflow + StorageDrain; }
double storage_getLosses(int j, double tStep) // // Input: j = node index // tStep = time step (sec) // Output: returns volume of water evaporated & infiltrated (ft3) // Purpose: computes volume of water evaporated & infiltrated from a storage // node over a given time step. // { double depth; double area = 0.0; double area0 = 0.0; double evapRate; double evapLoss = 0.0; double infilLoss = 0.0; double totalLoss = 0.0; double maxLoss; TGrnAmpt* infil; // --- adjust evaporation rate for storage unit's evaporation potential evapRate = Evap.rate * Storage[Node[j].subIndex].fEvap; if ( evapRate > 0.0 ) { // --- find surface area available for evaporation depth = Node[j].oldDepth; if ( depth > FUDGE ) area += storage_getSurfArea(j, depth); depth = Node[j].newDepth; if ( depth > FUDGE ) area += storage_getSurfArea(j, depth); // --- compute evaporation loss over average area evapLoss = 0.5 * area * evapRate * tStep; } // --- compute infiltration loss infil = Storage[Node[j].subIndex].infil; if (infil) { // --- find average depth over time step depth = 0.5 * (Node[j].oldDepth + Node[j].newDepth); // --- get surface area at avg. depth and at bottom of unit area0 = storage_getSurfArea(j, 0.0); area = area0; if ( depth > FUDGE ) area = storage_getSurfArea(j, depth); if ( area > 0.0 ) { // --- get average depth assuming sloped sides depth = depth / 2.0 * (1.0 + area0/area); // --- compute infil. loss considering ponded depth infilLoss = grnampt_getInfil(infil, tStep, 0.0, depth) * area * tStep; } } // --- compute total loss totalLoss = evapLoss + infilLoss; maxLoss = 0.5 * (Node[j].oldVolume + Node[j].newVolume); if ( totalLoss > 0.0 ) { if ( totalLoss > maxLoss ) { evapLoss = (evapLoss / totalLoss) * maxLoss; totalLoss = maxLoss; } } Storage[Node[j].subIndex].evapLoss = evapLoss; Storage[Node[j].subIndex].losses = totalLoss; return totalLoss; }