void setNewLinkState(int j) // // Input: j = link index // Output: none // Purpose: updates state of link after current time step under // Steady Flow or Kinematic Wave flow routing // { int k; double a, y1, y2; Link[j].newDepth = 0.0; Link[j].newVolume = 0.0; if ( Link[j].type == CONDUIT ) { // --- find avg. depth from entry/exit conditions k = Link[j].subIndex; a = 0.5 * (Conduit[k].a1 + Conduit[k].a2); Link[j].newVolume = a * link_getLength(j) * Conduit[k].barrels; y1 = xsect_getYofA(&Link[j].xsect, Conduit[k].a1); y2 = xsect_getYofA(&Link[j].xsect, Conduit[k].a2); Link[j].newDepth = 0.5 * (y1 + y2); // --- update depths at end nodes updateNodeDepth(Link[j].node1, y1 + Link[j].offset1); updateNodeDepth(Link[j].node2, y2 + Link[j].offset2); // --- check if capacity limited if ( Conduit[k].a1 >= Link[j].xsect.aFull ) Conduit[k].capacityLimited = TRUE; else Conduit[k].capacityLimited = FALSE; } }
void initLinks() // // Input: none // Output: none // Purpose: sets initial upstream/downstream conditions in links. // // Note: initNodes() must have been called first to properly // initialize each node's crown elevation. // { int i; // link index int j; // node index int k; // conduit or pump index double z; // crown elev. (ft) // --- examine each link for ( i = 0; i < Nobjects[LINK]; i++ ) { // --- examine each conduit if ( Link[i].type == CONDUIT ) { // --- assign initial flow to both ends of conduit k = Link[i].subIndex; Conduit[k].q1 = Link[i].newFlow / Conduit[k].barrels; Conduit[k].q2 = Conduit[k].q1; Conduit[k].q1Old = Conduit[k].q1; Conduit[k].q2Old = Conduit[k].q2; // --- find areas based on initial flow depth Conduit[k].a1 = xsect_getAofY(&Link[i].xsect, Link[i].newDepth); Conduit[k].a2 = Conduit[k].a1; // --- compute initial volume from area Link[i].newVolume = Conduit[k].a1 * link_getLength(i) * Conduit[k].barrels; Link[i].oldVolume = Link[i].newVolume; } // --- update crown elev. of nodes at either end j = Link[i].node1; z = Node[j].invertElev + Link[i].offset1 + Link[i].xsect.yFull; Node[j].crownElev = MAX(Node[j].crownElev, z); j = Link[i].node2; z = Node[j].invertElev + Link[i].offset2 + Link[i].xsect.yFull; Node[j].crownElev = MAX(Node[j].crownElev, z); } }
double getLinkStep(double tMin, int *minLink) // // Input: tMin = critical time step found so far (sec) // Output: minLink = index of link with critical time step; // returns critical time step (sec) // Purpose: finds critical time step for conduits based on Courant criterion. // { int i; // link index int k; // conduit index double q; // conduit flow (cfs) double t; // time step (sec) double tLink = tMin; // critical link time step (sec) // --- examine each conduit link for ( i = 0; i < Nobjects[LINK]; i++ ) { if ( Link[i].type == CONDUIT ) { // --- skip conduits with negligible flow, area or Fr k = Link[i].subIndex; q = fabs(Link[i].newFlow) / Conduit[k].barrels; if ( q <= 0.05 * Link[i].qFull || Conduit[k].a1 <= FUDGE || Link[i].froude <= 0.01 ) continue; // --- compute time step to satisfy Courant condition t = Link[i].newVolume / Conduit[k].barrels / q; t = t * Conduit[k].modLength / link_getLength(i); t = t * Link[i].froude / (1.0 + Link[i].froude) * CourantFactor; // --- update critical link time step if ( t < tLink ) { tLink = t; *minLink = i; } } } return tLink; }
void setNewLinkState(Project* project, int j) // // Input: j = link index // Output: none // Purpose: updates state of link after current time step under // Steady Flow or Kinematic Wave flow routing // { int k; double a, y1, y2; project->Link[j].newDepth = 0.0; project->Link[j].newVolume = 0.0; if ( project->Link[j].type == CONDUIT ) { // --- find avg. depth from entry/exit conditions k = project->Link[j].subIndex; a = 0.5 * (project->Conduit[k].a1 + project->Conduit[k].a2); project->Link[j].newVolume = a * link_getLength(project, j) * project->Conduit[k].barrels; y1 = xsect_getYofA(project, &project->Link[j].xsect, project->Conduit[k].a1); y2 = xsect_getYofA(project, &project->Link[j].xsect, project->Conduit[k].a2); project->Link[j].newDepth = 0.5 * (y1 + y2); // --- update depths at end nodes updateNodeDepth(project, project->Link[j].node1, y1 + project->Link[j].offset1); updateNodeDepth(project, project->Link[j].node2, y2 + project->Link[j].offset2); // --- check if capacity limited if ( project->Conduit[k].a1 >= project->Link[j].xsect.aFull ) { project->Conduit[k].capacityLimited = TRUE; project->Conduit[k].fullState = ALL_FULL; //(5.1.008) } else { project->Conduit[k].capacityLimited = FALSE; project->Conduit[k].fullState = 0; //(5.1.008) } } }
void initLinks(Project* project, int routingModel) // // Input: none // Output: none // Purpose: sets initial upstream/downstream conditions in links. // { int i; // link index int k; // conduit or pump index // --- examine each link for ( i = 0; i < project->Nobjects[LINK]; i++ ) { if ( routingModel == SF) project->Link[i].newFlow = 0.0; // --- otherwise if link is a conduit else if ( project->Link[i].type == CONDUIT ) { // --- assign initial flow to both ends of conduit k = project->Link[i].subIndex; project->Conduit[k].q1 = project->Link[i].newFlow / project->Conduit[k].barrels; project->Conduit[k].q2 = project->Conduit[k].q1; // --- find areas based on initial flow depth project->Conduit[k].a1 = xsect_getAofY(project, &project->Link[i].xsect, project->Link[i].newDepth); project->Conduit[k].a2 = project->Conduit[k].a1; // --- compute initial volume from area { project->Link[i].newVolume = project->Conduit[k].a1 * link_getLength(project, i) * project->Conduit[k].barrels; } project->Link[i].oldVolume = project->Link[i].newVolume; } } }
int kinwave_execute(int j, double* qinflow, double* qoutflow, double tStep) // // Input: j = link index // qinflow = inflow at current time (cfs) // tStep = time step (sec) // Output: qoutflow = outflow at current time (cfs), // returns number of iterations used // Purpose: finds outflow over time step tStep given flow entering a // conduit using Kinematic Wave flow routing. // // t // | qin, ain |-------------------| qout, aout // | | Flow ---> | // |----> x q1, a1 |-------------------| q2, a2 // // { int k; int result = 1; double dxdt, dq; double ain, aout; double qin, qout; double a1, a2, q1, q2; // --- no routing for non-conduit link (*qoutflow) = (*qinflow); if ( Link[j].type != CONDUIT ) return result; // --- no routing for dummy xsection if ( Link[j].xsect.type == DUMMY ) return result; // --- assign module-level variables pXsect = &Link[j].xsect; Qfull = Link[j].qFull; Afull = Link[j].xsect.aFull; k = Link[j].subIndex; Beta1 = Conduit[k].beta / Qfull; // --- normalize flows and areas q1 = Conduit[k].q1 / Qfull; q2 = Conduit[k].q2 / Qfull; a1 = Conduit[k].a1 / Afull; a2 = Conduit[k].a2 / Afull; qin = (*qinflow) / Conduit[k].barrels / Qfull; // --- use full area when inlet flow >= full flow //(5.0.012 - LR) if ( qin >= 1.0 ) ain = 1.0; //(5.0.012 - LR) // --- get normalized inlet area corresponding to inlet flow else ain = xsect_getAofS(pXsect, qin/Beta1) / Afull; // --- check for no flow if ( qin <= TINY && q2 <= TINY ) { qout = 0.0; aout = 0.0; } // --- otherwise solve finite difference form of continuity eqn. else { // --- compute constant factors dxdt = link_getLength(j) / tStep * Afull / Qfull; dq = q2 - q1; C1 = dxdt * WT / WX; C2 = (1.0 - WT) * (ain - a1); C2 = C2 - WT * a2; C2 = C2 * dxdt / WX; C2 = C2 + (1.0 - WX) / WX * dq - qin; // --- starting guess for aout is value from previous time step aout = a2; // --- solve continuity equation for aout result = solveContinuity(qin, ain, &aout); // --- report error if continuity eqn. not solved if ( result == -1 ) { report_writeErrorMsg(ERR_KINWAVE, Link[j].ID); return 1; } if ( result <= 0 ) result = 1; // --- compute normalized outlet flow from outlet area qout = Beta1 * xsect_getSofA(pXsect, aout*Afull); if ( qin > 1.0 ) qin = 1.0; } // --- save new flows and areas Conduit[k].q1 = qin * Qfull; Conduit[k].a1 = ain * Afull; Conduit[k].q2 = qout * Qfull; Conduit[k].a2 = aout * Afull; (*qinflow) = Conduit[k].q1 * Conduit[k].barrels; (*qoutflow) = Conduit[k].q2 * Conduit[k].barrels; return result; }