Classwork 10 Solving Streeter-Phelps model for the Oglio River upstream of lake Iseo The ValleCamonica watershed will be used as a geographical background for studying the Oxygen content when sewage is released within the Oglio river in Ponte di Legno (red circle)
Why classwork 10? The thermal balance of a lake, as studied in the previous classwork, is an important prerequisite both for the comprehension of its hydrodynamics and of its water quality. Due to the scarcity of Oxygen within its hypolimnion, a main issue is the amount of nutrients delivered to the lake from the upstream catchment as well as the oxygen content of the entering discharge. In this classwork we shall implement and solve the Streeter- Phelps model for the main tributary of the lake in correspondence of steady state effluents and discharge in order to provide some insights on these and others related questions. The content of classwork 10 The layout for solving the Streeter-Phelps model is provided by the geometrical description of the Oglio river as provided by the file valcamonica_80m_reach.xyz, that has been automatically derived by the Digital Elevation Model of the watershed and whose format is as follows sez. x y z[m] s[m] A(s) S b q T [ C] L 0 [mg/l] O [mg/l] [km 2 ] [mc/s] 1 1615980 5123660 1227.7 0 107.44 0.0538 12 8 0 10.66 2 1615820 5123660 1219.1 160 114.76 0.0466 1 15 120 4 3 1615660 5123500 1210 386.27 114.81 0.0378 0 0 0 0 4 1615500 5123500 1204.4 546.27 115.40 0.0196 0 0 0 0 5 1615500 5123340 1203.7 706.27 115.89 0.0042 0 0 0 0 396 1584140 5072620 186.9 74665.43 1444.7 0.001 0 0 0 0 This table provides the x,y,z coordinates of the Oglio river, along with the drained area (A(s)) at each station s along the river. Q,T, L 0 and O are the discharge, Temperature, Organic Content and Oxygen of effluent entering the river at each cross section. The first line contains this information for the inlet of the whole stretch so that if no effluent is entering the river, this set of information will be different from 0 only for the first line. This format allows sufficient flexibility to represent any river and any combination of discharges/pollutants. This set of information must be complemented with information regarding the hydraulic geometry of the river. Within the header of the program you will find the width of the cross section (that is supposed to be rectangular) at section 1 and section 396 that corresponds to Ponte di Legno and Pisogne respectively. In the middle, the width is interpolated linearly and the water depth is computed as the normal depth by solving the Chezy equation. To this purpose the local slope is provided as Sb above and the Stcickler coefficient is given in the header of the program. The program propagates discharge and pollutant along the set of reaches corresponding to the sections numbered in the first column, solving the equations reach by reach. In other words, the program starts form section1 and solves the equations x1 kr U L( ) = L0e (1) x1 x1 x1 k kr ka ka d L0 U U U D( ) = ( e e ) + D0e k a kr as far as section 2, using the velocity and the water depth at cross-section 1. There it checks for inflow from possible lateral effluents (in apex in following symbols) or tributaries, and computes the averaged T, O and L in in Ti 1Qi 1 + Ti qi T ( xi ) = in (2) Qi 1 + qi the Oxygen Oi and the amount of oxidizable matter Li must be averaged after having applied the Streeter Phelps model, so that
in in OiQi + Oi qi O( xi ) = in (3) Qi 1 + qi Then the Oxygen at saturation, Os, must be computed using the averaged temperature and eventually the local value of D can be obtained as Di= Os Oi. These values of L and D can be used to compute Di+1 and Li+1 by solving (1) and so on in an iterative fashion as far as the outlet. If one keeps track of the time, as t= t + x/u(x), one can also get a time evolution of L and D. All these data are saved within vectors and then saved on a file for following check and visualization. The student is asked to complete the main loop (yellow cells in the following) by programming the core of the cell-updating algorithm END. leggi_profilo; apri_file_scrittura; time[1] := 0; FOR i := 1 TO num_reaches-1 DO END scrivi_soluzione_su_file; chiudi_programma; As well as to fill in the averaging procedure FUNCTION calcola_t_equivalente(i:integer):real; FUNCTION calcola_o_equivalente(i:integer):real; FUNCTION calcola_l_equivalente(i:integer):real; These functions will be used within the above mentioned loop. After implementing the missing part of the code, the user is asked to validate the code. This can be accomplished by applying it to a channel of uniform properties, with a constant Ka value, that can be specifically selected by selecting ka_constant = TRUE; k_a ; in the CONST declaration of the main program. To this purpose one can use the file valcamonica_80m_rett.xyz where the slope is constant by setting B_min = B_max also placed in the CONST declaration. Final targets of Classwork 10 Solving a classical model in the field of water quality and applying it to the Oglio basin, that is the primary culprit of the pollution of Iseo lake. Suggested readings A very well written and instructive reading about this and other topics on water quality is provided by Chapters 19-22 of the book
S. Chapra, Surface Water Quality Modeling, Mc Graw Hill, 1997 Data Visualization and Program Data can be visualized using a matlab script, that in its basic form might be as the following out = importdata('reach_data.out, ' ', 1); time = out.data(:,10); O = out.data(:,12); D = out.data(:,14); L = out.data(:,15); figure plot(time,o,'b'); hold on plot(time,d,'g'); plot(time,l,'r'); xlabel('time (s)') ylabel('concentration (mg/l)') legend('oxygen','deficit','l') The following Figures have been obtained when at Ponte di Legno the discharge is characterized by q [mc/s] T [ C] L0[mg/L] O [mg/l] 12 8 0 10.66 whilst in the following cell the effluent is characterized by 1 15 150 4 The first Figure is related to a rectangular cross section with slope of 0.001 m/m.the other parameters are the following B_min = 15; B_max = 15; // width of the rectangular cross-section at Pisogne KStrickler = 10; // roughness coefficient considered constant kd = 4/86400; // da day^-1 a s^-1; tasso di decomposizione della materia organica ks = 0.25/86400; //da day^-1 a s^-1; tasso di sedimentazione kr = ks +kd; ka_constant = TRUE; k_a = 0.5/86400; The second Figure has been obtained with the same parameters but using the file valcamonica_80m_reach.xyz and considering a reareation rate that depends on local U and Y according to the O Connor-Dobbins formula. Figure 1 Figure 2
program Streeter_Phelps; {$R *.RES} Const // dati generali per l'alveo B_min = 10; // width of the rectangular cross-section at Ponte di Legno B_max = 10; // width of the rectangular cross-section at Pisogne KStrickler = 15; // roughness coefficient considered constant kd = 4/86400; // da day^-1 a s^-1; tasso di decomposizione della materia organica ks = 0.25/86400; //da day^-1 a s^-1; tasso di sedimentazione kr = ks +kd; ka_constant = TRUE; k_a = 0.5/86400; eps = 1e-06; max_n_elementi = 10000; // massimo numero di discretizzazioni del fiume // INPUT-OUTPUT // two files containing Oglio river: valcamonica_80m_rett.xyz, with a constant slope, to be used with B_min=B_max for // comparison with analytical solution // valcamonica_80m_reach.xyz, with real slope, to be used with B_min = 10 m and B_max = 40 m // for testing the role of geometrical variations on the process namein1 = 'valcamonica_80m_rett.xyz'; // river profile nameout1 = 'reach_data.out'; TYPE realarraynp = ARRAY [1..max_n_elementi] OF real; VAR z_bed,x_bed,drained_a,slope_bed,q,y_normal,u,l_organic,d,o_sat, T,O,L,T_in,Q_in,L_in,O_in,B,time : realarraynp; i,j,k,dummyi,num_reaches : INTEGER; dummyr, L_reach,deltaX,kka : real; arch1,arch3 : TEXT; FUNCTION pt(a,potenza:real):real; {calcola a elevato ad una potenza} IF a = 0 THEN pt := 0 ELSE IF a > 0 THEN pt := exp(potenza*ln(a)) ELSE IF odd(round(potenza)) THEN pt := -exp(potenza*ln(abs(a))) ELSE pt := exp(potenza*ln(abs(a))); FUNCTION Area(B,h:REAL):REAL; Area := B*h; FUNCTION Perimetro(B,h:REAL):REAL; Perimetro := B+2*h; FUNCTION Raggio_idraulico(B,h:REAL):REAL;
Raggio_idraulico := Area(B,h)/Perimetro(B,h); FUNCTION Q_Chezy(Ks,Slope_bed,B,h:REAL):REAL; Q_Chezy := ks*area(b,h)*pt(raggio_idraulico(b,h),2/3)*sqrt(slope_bed); FUNCTION F(Q,Ks,Slope_bed,B,h:REAL):REAL; F := Q- Q_Chezy(Ks,Slope_bed,B,h); FUNCTION df_dh(q,ks,slope_bed,b,h:real):real; CONST dh = 1e-08; df_dh :=( F(Q,Ks,Slope_bed,B,h+dh) -F(Q,Ks,Slope_bed,B,h) )/dh; FUNCTION find_normal_depth(q,ks,slope_bed,b,x0 :real):real; CONST tolf = 1e-08; tolx = 1e-08; n_max_ntrial = 100; VAR xx : ARRAY[1..2] OF REAL; ntrial : INTEGER; continua : BOOLEAN; xx[1] := x0; ntrial := 0; CONTINUA := TRUE; WHILE continua DO ntrial := ntrial +1; xx[2] := xx[1] -F(Q,Ks,Slope_bed,B,xx[1])/dF_dh(Q,Ks,Slope_bed,B,xx[1]); IF ABS(F(Q,Ks,Slope_bed,B,xx[2])) <= tolf THEN continua := FALSE; IF ABS(xx[2] -xx[1]) <= tolx THEN continua := FALSE; IF ntrial > n_max_ntrial THEN continua := FALSE; writeln('maximum numeber of iterations exceeded!'); xx[1] := xx[2]; find_normal_depth := xx[2]; FUNCTION O_saturazione(T:real):extended; {Os is a function of several variables, most noticeably of T. In fresh water at 1 ATM, this relationship holds (APHA, 1992)} T := T +273.15; O_saturazione :=EXP(-139.34411+1.575701*1e05/(T)-6.642308*1e07/(sqr(T))+1.2438*1e10/((T)*sqr(T))- 8.621949*1e11/(sqr(sqr(T))));
FUNCTION ka(u,y:real):real; // reareation rate according to O Connor-Dobbins formula, with U [m/s], Y [m] // ka = da [day^-1] a [s^-1] IF ka_constant THEN ka := k_a ELSE ka := 3.93*sqrt(U)/pt(Y,1.5)/86400; ; FUNCTION calcola_t_equivalente(i:integer):real; FUNCTION calcola_o_equivalente(i:integer):real; FUNCTION calcola_l_equivalente(i:integer):real; //////////////////////////////////////////// INIZIALIZZAZIONI /////////////// PROCEDURE leggi_profilo; VAR first_tentative_y,channel_width : real; writeln('reading file ',namein1); ASSIGN(arch3,namein1); RESET(arch3); FOR i := 1 TO 1 DO readln(arch3); i := 0; WHILE NOT EOF(arch3) DO i := i+1; // data regarding the river and (Q_in[i],T_in[i],L_in[i],O_in[i]) the effluent into the river // If i = 1 then the effluent is the river itself readln(arch3,dummyi,dummyr,dummyr,z_bed[i],x_bed[i],drained_a[i],slope_bed[i],q_in[i],t_in[i],l_in[i],o_in[i]); IF i = 1 THEN Q[1] :=Q_in[i]; T[i] :=T_in[i]; L[i] :=L_in[i]; O[i] :=O_in[i]; END // we increment the discharge according to the drained area ELSE Q[i] := Q[i-1] +Q_in[i]; CLOSE(arch3); num_reaches := i; // maximum length of the river L_reach := x_bed[num_reaches]; FOR i := 1 TO num_reaches DO channel_width := B_min + (B_max -B_min)/L_reach*x_bed[i]; B[i] := channel_width;
first_tentative_y := 1; Y_normal[i] := find_normal_depth(q[i],kstrickler,slope_bed[i],channel_width,first_tentative_y); U[i] := Q[i]/(channel_width*Y_normal[i]); PROCEDURE apri_file_scrittura; writeln('scrittura dati su file :',nameout1); ASSIGN(arch1,nameout1); REWRITE(arch1); writeln('press a key to continue'); readln; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////// EDITING E FORMATTAZIONE //////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// PROCEDURE scrivi_soluzione_su_file; writeln(arch1,' # z s A(s) Sb Q U Y B time T O O_sat D L'); FOR i := 1 TO num_reaches DO writeln(i:4,q[i]:6:2,u[i]:6:2,y_normal[i]:6:2); write(arch1,i:4,' ',z_bed[i]:6:2,' ',x_bed[i]:6:2,' ',drained_a[i]:6:2,' ',slope_bed[i]:6:5,' ',Q[i]:6:2,' ',U[i]:6:2,' ',Y_normal[i]:6:2,' ',B[i]:6:2); writeln(arch1,' ',time[i]:8:1,' ',T[i]:6:2,' ',O[i]:6:2,' ',O_sat[i]:6:2,' ',D[i]:6:2,' ',L[i]:6:2); PROCEDURE chiudi_programma; writeln('programma terminato'); readln; CLOSE(arch1); leggi_profilo; apri_file_scrittura; time[1] := 0; FOR i := 1 TO num_reaches-1 DO scrivi_soluzione_su_file; chiudi_programma; END.