@@ -304,7 +304,7 @@ double CEnthalpyModel::GetEnergyLossesFromLake(const int p, double &Q_sens, doub
304304 }
305305
306306 Q_sens =hstar* A_avg * (temp_air -0.5 *(T_new+T_old));
307- Q_cond =kdiff * A_avg * (0.5 *(Ts_new+Ts_old)- 0.5 *(T_new+T_old));
307+ Q_cond =ksed * A_avg * (0.5 *(Ts_new+Ts_old)- 0.5 *(T_new+T_old));
308308 Q_sw_in =(SW )*A_avg;
309309 Q_lw_in =(LW_in )*A_avg;
310310 Q_lw_out=(LW )*A_avg;
@@ -314,7 +314,7 @@ double CEnthalpyModel::GetEnergyLossesFromLake(const int p, double &Q_sens, doub
314314 return -(Q_sens + Q_cond + Q_sw_in + Q_lw_in + Q_lw_out + Q_lat + Q_rain) * tstep; // [MJ]
315315}
316316// ////////////////////////////////////////////////////////////////
317- // / \brief Calculates Res_mass (total reservoir energ ) and ResSedMass (total sediment energy) in basin with reservoir at end of time step
317+ // / \brief Calculates Res_mass (total reservoir energy ) and ResSedMass (total sediment energy) in basin with reservoir at end of time step
318318// /
319319// / \param p [in] subbasin index
320320// / \param aMout_new[] [in] Array of energy outflows at downstream end of each segment at end of current timestep [MJ/d] [size: nsegs]
@@ -324,7 +324,7 @@ double CEnthalpyModel::GetEnergyLossesFromLake(const int p, double &Q_sens, doub
324324// / \param tt [in] Time structure
325325//
326326void CEnthalpyModel::RouteMassInReservoir (const int p, // SB index
327- const double *aMout_new, // [MJ/d][size: nsegs ]
327+ const double *aMout_new, // [MJ/d][size: nsegs]
328328 double &Res_mass, // [MJ]
329329 double &ResSedMass, // [MJ]
330330 const optStruct &Options,
@@ -334,28 +334,30 @@ void CEnthalpyModel::RouteMassInReservoir (const int p, //
334334 CReservoir* pRes = _pModel->GetSubBasin (p)->GetReservoir ();
335335 int nSegments = _pModel->GetSubBasin (p)->GetNumSegments ();
336336
337- double V_new=pRes->GetStorage ();
338- double V_old=pRes->GetOldStorage ();
339- double Q_new=pRes->GetOutflowRate ()*SEC_PER_DAY;
340- double Q_old=pRes->GetOldOutflowRate ()*SEC_PER_DAY;
341- double A_new=pRes->GetSurfaceArea ();
342- double A_old=pRes->GetOldSurfaceArea ();
343- double A_avg=0.5 *(A_new+A_old);
344- double tstep=Options.timestep ;
345-
337+ double V_new=pRes->GetStorage ();
338+ double V_old=pRes->GetOldStorage ();
339+
346340 if ((V_old<=0.0 ) || (V_new<=0.0 )) { Res_mass=ResSedMass=0.0 ; return ;} // handles dried out reservoir/lake
347341
348- double V_h_new = pRes->GetHypolimnionStorage ();
349- double V_h_old = pRes->GetOldHypolimnionStorage ();
342+ double V_h_new = pRes->GetHypolimnionStorage ();
343+ double V_h_old = pRes->GetOldHypolimnionStorage ();
350344 double V_e_new = V_new-V_h_new;
351345 double V_e_old = V_old-V_h_old;
352- double Q_dn_new = 0 ;
353- double Q_dn_old = 0 ;
354- double Q_up_new = 0 ;
355- double Q_up_old = 0 ;
346+ double Q_new=pRes->GetOutflowRate ()*SEC_PER_DAY;
347+ double Q_old=pRes->GetOldOutflowRate ()*SEC_PER_DAY;
348+ double A_new=pRes->GetSurfaceArea ();
349+ double A_old=pRes->GetOldSurfaceArea ();
350+ double A_avg=0.5 *(A_new+A_old);
356351 double A_h_new = pRes->GetMixingArea ();
357352 double A_h_old = pRes->GetOldMixingArea ();
358353 double A_h_avg = 0.5 * (A_h_new + A_h_old);
354+
355+ double Q_dn_new = 0.0 ;
356+ double Q_dn_old = 0.0 ;
357+ double Q_up_new = 0.0 ;
358+ double Q_up_old = 0.0 ;
359+
360+ double tstep=Options.timestep ;
359361
360362 CHydroUnit* pHRU=_pModel->GetHydroUnit (pRes->GetHRUIndex ());
361363
@@ -367,13 +369,10 @@ void CEnthalpyModel::RouteMassInReservoir (const int p, //
367369 double hstar (0 );
368370 if (pHRU!=NULL ) { // otherwise only simulate advective mixing+ rain input
369371 Acorr =pHRU->GetArea ()*M2_PER_KM2/A_avg; // handles the fact that GetAET() returns mm/d normalized by HRU area, not actual area
370-
371372 temp_air =pHRU->GetForcingFunctions ()->temp_ave ; // [C]
372373 SW =pHRU->GetForcingFunctions ()->SW_radia_net ; // [MJ/m2/d]
373374 LW_in =pHRU->GetForcingFunctions ()->LW_incoming ; // [MJ/m2/d]
374-
375375 LW =-STEFAN_BOLTZ*EMISS_WATER*pow (T_old+ZERO_CELSIUS,4 ); // [MJ/m2/d] //TMP DEBUG -time-lagged - should include in the N-R formulation
376-
377376 AET =pRes->GetAET ()*Acorr/ MM_PER_METER ; // [m/d]
378377// Vsed =pRes->GetLakebedThickness() * pHRU->GetArea()*M2_PER_KM2;
379378 hstar =pRes->GetLakeConvectionCoeff (); // [MJ/m2/d/K]
@@ -382,7 +381,7 @@ void CEnthalpyModel::RouteMassInReservoir (const int p, //
382381
383382 double Ts_old=ConvertVolumetricEnthalpyToTemperature (_aMsed_last[p]/V_h_old);
384383
385-
384+ // water density in each layer as a function of water temperature (Chapra, 2008)
386385 double dens_e,dens_h,vdiff,kdiff;
387386
388387 double a0= 999.842594 ;
@@ -395,23 +394,24 @@ void CEnthalpyModel::RouteMassInReservoir (const int p, //
395394 dens_e = a0+a1*T_old + a2*pow (T_old,2 ) + a3*pow (T_old,3 ) +a4*pow (T_old,4 )+a5*pow (T_old,5 );
396395 dens_h = a0+a1*Ts_old + a2*pow (Ts_old,2 ) + a3*pow (Ts_old,3 ) +a4*pow (Ts_old,4 )+a5*pow (Ts_old,5 );
397396
398- double N2;
399-
400- if (dens_e<dens_h){
401- N2 = -1 *GRAVITY/dens_e*(dens_e-dens_h)/1.0 ;
397+ // Brunt-Väisälä frequency at thermocline depth (assume dz = 1.0m) (Jennings et al. 2012)
398+ double N2= 1.0e-12 ; // value when dens_e >= dens_h
399+ if (dens_e<dens_h){
400+ N2 = -1 *GRAVITY/dens_e*(dens_e-dens_h)/1.0 ;
402401 }
403- double m = -0.65 ;
404- double b = -3.1 ;
405402
403+ // Vertical convection coefficient as function of N2 (Quay et el. 1980; Figure 10)
404+ double m = -0.65 ;
405+ double b = -3.1 ;
406406 vdiff = pow (10 ,m*log10 (N2)+b)/100 /100 ;
407407 kdiff = vdiff*HCP_WATER*SEC_PER_DAY;
408408
409-
410- // N-R solution of Crank-nicolson problem as set of two non-linear algebraic equations
409+ // N-R solution of Crank-nicolson problem as set of two non-linear algebraic equations [A][E]=[B]
411410 // -----------------------------------------------------------------------------------------
412- // dE /dt= Qh_in- Qh_out+(Rnet*A+P*hrain*A) -ET*rho*LH*A+ k*(Tair-T(E))*A+ksed*(Tsed-T(E))*A // [MJ/d]
413- // dEsed /dt= -ksed*(Tsed-T(E))*A // [MJ/d]
411+ // dEe /dt = Qh_in - Qh_out - Qdn*Ee/Ve + Qup*Eh/Vh + As*(Rnet+Phrain -ET*rho*LH) + As* k*(Tair-Te) + Ah*kdiff*(Th-Te) // [MJ/d]
412+ // dEh /dt = Qdn*Ee/Ve – Qup*Eh/Vh + Ah*kdiff*(Te-Th) // [MJ/d]
414413 //
414+
415415 // Allocate memory
416416 double *B=new double [2 ];
417417 double *R=new double [2 ];
@@ -424,20 +424,24 @@ double b = -3.1;
424424 for (int j = 0 ; j < 2 ; j++) {A[i][j]=J[i][j]=0.0 ;}
425425 }
426426
427+ // constants matrix
427428 B[0 ] = 0.5 *(aMout_new[nSegments-1 ]+_aMout[p][nSegments-1 ])*tstep; // inflow [MJ]
429+ B[0 ]+=(1.0 -0.5 *tstep/V_e_old*Q_old)*_aMres_last[p]; // outflow [MJ]
428430 B[0 ]+=_aMresRain[p]*tstep; // rainfall inputs [MJ]
429- B[0 ]+= A_avg *(SW+LW_in)*tstep; // net incoming radiation [MJ]
430- B[0 ]+= A_avg *(LW )*tstep; // net outgoing radiation [MJ]
431- B[0 ]-= A_avg *(AET*DENSITY_WATER*LH_VAPOR)*tstep; // latent heat [MJ]
432- B[0 ]+= A_avg *(hstar*temp_air)*tstep; // sensible heat exhange [MJ]
433-
434- B[0 ]+=(1.0 -0.5 *tstep/V_old*Q_old)*_aMres_last[p];
435- B[0 ]-=0.5 *tstep*hstar*A_old*T_old;
436- B[0 ]-=0.5 *tstep*kdiff *A_avg*T_old;
437- B[0 ]+=0.5 *tstep*kdiff *A_avg*Ts_old;
438-
439- B[1 ] =0.5 *tstep*kdiff *A_avg*T_old;
440- B[1 ]+=_aMsed_last[p]-0.5 *tstep*kdiff *A_avg*Ts_old;
431+ B[0 ]+= A_avg*(SW+LW_in)*tstep; // net incoming radiation [MJ]
432+ B[0 ]+= A_avg*(LW )*tstep; // net outgoing radiation [MJ]
433+ B[0 ]-= A_avg*(AET*DENSITY_WATER*LH_VAPOR)*tstep; // latent heat [MJ]
434+ B[0 ]+= A_avg*hstar*(temp_air-0.5 *T_old)*tstep; // sensible heat exhange [MJ]
435+ // B[0]-=0.5*tstep*hstar*A_avg*T_old; // sensible heat exchange [MJ]
436+ B[0 ]+=0.5 *tstep*kdiff*A_h_avg*(Ts_old-T_old); // diffusive heat exchange [MJ]
437+ // B[0]-=0.5*tstep*kdiff*A_h_avg*T_old; // diffusive heat exchange [MJ]
438+ B[0 ]+=(1.0 -0.5 *tstep/V_e_old*Q_dn_old)*_aMres_last[p]; // downward advection [MJ]
439+ B[0 ]+=0.5 *tstep/V_h_old*Q_up_old*_aMsed_last[p]; // upward advection [MJ]
440+
441+ B[1 ] =0.5 *tstep*kdiff*A_h_avg*(T_old-Ts_old); // diffusive heat exchange [MJ]
442+ // B[1]-=0.5*tstep*kdiff*A_h_avg*Ts_old;
443+ B[1 ]+=0.5 *tstep/V_e_old*Q_dn_old*_aMres_last[p]; // downward advection [MJ]
444+ B[1 ]+=(1 -0.5 *tstep/V_h_old*Q_up_old)*_aMsed_last[p]; // upward advection [MJ]
441445
442446 int iter=0 ;
443447 double change=ALMOST_INF;
@@ -452,35 +456,35 @@ double b = -3.1;
452456 T_guess = ConvertVolumetricEnthalpyToTemperature (E_guess/V_e_new);
453457 Ts_guess= ConvertVolumetricEnthalpyToTemperature (Es_guess/V_h_new);
454458
455- A[0 ][0 ] = 1.0 + 0.5 * tstep * Q_new/V_new ;
456- A[1 ][1 ] = 1.0 ;
457- A[1 ][0 ] = 0.0 ;
458- A[0 ][1 ] = 0.0 ;
459+ A[0 ][0 ] = 1.0 + 0.5 * tstep*( Q_new + Q_dn_new)/V_e_new ;
460+ A[1 ][1 ] = 1.0 + 0.5 *tstep*Q_up_new/V_h_new ;
461+ A[1 ][0 ] = 0.5 *tstep*Q_dn_new/V_e_new ;
462+ A[0 ][1 ] = 0.5 *tstep*Q_up_new/V_h_new ;
459463 if (E_guess!=0.0 ){
460- A[0 ][0 ]+= 0.5 *tstep*hstar*A_new* T_guess/E_guess;
461- A[0 ][0 ]+= 0.5 *tstep*kdiff *A_avg *T_guess/E_guess; // [MJ/m2/d/K]*[m2]/[MJ/m3/K] = [m3/d]
462- A[1 ][0 ]-= 0.5 *tstep*kdiff *A_avg *T_guess/E_guess;
464+ A[0 ][0 ]+= 0.5 *tstep*hstar*A_avg* T_guess/E_guess;
465+ A[0 ][0 ]+= 0.5 *tstep*kdiff*A_h_avg *T_guess/E_guess; // [MJ/m2/d/K]*[m2]/[MJ/m3/K] = [m3/d]
466+ A[1 ][0 ]-= 0.5 *tstep*kdiff*A_h_avg *T_guess/E_guess;
463467 }
464468 if (Es_guess!=0.0 ){
465- A[0 ][1 ]-= 0.5 *tstep*kdiff *A_avg *Ts_guess/Es_guess;
466- A[1 ][1 ]+= 0.5 *tstep*kdiff *A_avg *Ts_guess/Es_guess;
469+ A[0 ][1 ]-= 0.5 *tstep*kdiff*A_h_avg *Ts_guess/Es_guess;
470+ A[1 ][1 ]+= 0.5 *tstep*kdiff*A_h_avg *Ts_guess/Es_guess;
467471 }
468472
469473 // J_ij=A_ij+E*dA_ij/dE = Jacobian
470- J[0 ][0 ]=A[0 ][0 ]+0.5 *tstep*(hstar*A_new +kdiff*A_avg ) * (TemperatureEnthalpyDerivative (E_guess /V_e_new)/ V_e_new);
471- J[0 ][1 ]=A[0 ][1 ]- 0.5 *tstep*( kdiff*A_avg ) * (TemperatureEnthalpyDerivative (Es_guess/V_h_new )/ V_h_new );
472- J[1 ][0 ]=A[1 ][0 ]-0.5 *tstep*( kdiff*A_avg ) * (TemperatureEnthalpyDerivative (E_guess /V_e_new)/ V_e_new);
473- J[1 ][1 ]=A[1 ][1 ]+0.5 *tstep*( kdiff*A_avg ) * (TemperatureEnthalpyDerivative (Es_guess/V_h_new )/ V_h_new );
474+ J[0 ][0 ]=A[0 ][0 ]+0.5 *tstep*(hstar*A_avg +kdiff*A_h_avg ) * (TemperatureEnthalpyDerivative (E_guess /V_e_new)/V_e_new);
475+ J[0 ][1 ]=A[0 ][1 ]+ 0.5 *tstep*( kdiff*A_h_avg ) * (TemperatureEnthalpyDerivative (Es_guess/V_h_new)/ V_h_new);
476+ J[1 ][0 ]=A[1 ][0 ]-0.5 *tstep*( kdiff*A_h_avg ) * (TemperatureEnthalpyDerivative (E_guess /V_e_new)/V_e_new);
477+ J[1 ][1 ]=A[1 ][1 ]+0.5 *tstep*( kdiff*A_h_avg ) * (TemperatureEnthalpyDerivative (Es_guess/V_h_new)/ V_h_new);
474478
475- R[0 ] = -A[0 ][0 ]*E_guess-A[0 ][1 ]*Es_guess+B[0 ];
476- R[1 ] = -A[1 ][0 ]*E_guess-A[1 ][1 ]*Es_guess+B[1 ];
479+ R[0 ] = -A[0 ][0 ]*E_guess-A[0 ][1 ]*Es_guess+B[0 ];
480+ R[1 ] = -A[1 ][0 ]*E_guess-A[1 ][1 ]*Es_guess+B[1 ];
477481
478482 // invert 2x2 matrix analytically
479483 double den= (J[0 ][1 ]*J[1 ][0 ]-J[0 ][0 ]*J[1 ][1 ]);
480484 dE = (J[0 ][1 ]*R[1 ]-J[1 ][1 ]*R[0 ])/den;
481485 dEs = (J[1 ][0 ]*R[0 ]-J[0 ][0 ]*R[1 ])/den;
482486
483- change =sqrt (dE*dE+dEs*dEs)/ V_e_new / HCP_WATER; // convert to approx temp difference (for >0C water)
487+ change =sqrt (dE*dE+dEs*dEs)/V_new/ HCP_WATER; // convert to approx temp difference (for >0C water)
484488
485489 E_guess +=dE;
486490 Es_guess +=dEs;
@@ -494,6 +498,7 @@ double b = -3.1;
494498 delete [] R;
495499 for (int i=0 ;i<2 ;i++){delete [] A[i]; delete[] J[i]; } delete[] A; delete[] J;
496500}
501+
497502// ////////////////////////////////////////////////////////////////
498503// / \brief returns total energy lost from subbasin reach over current time step [MJ]
499504// / \param p [in] subbasin index
0 commit comments