11/* ----------------------------------------------------------------
22 Raven Library Source Code
3- Copyright (c) 2008-2021 the Raven Development Team
3+ Copyright (c) 2008-2025 the Raven Development Team
44 ----------------------------------------------------------------*/
55#include " RavenInclude.h"
66#include " Model.h"
77
88/* ****************************************************************
99 Model Streamflow Assimilation Routines
1010*****************************************************************/
11- bool IsContinuousFlowObs2 (const CTimeSeriesABC *pObs,long SBID)
11+ bool IsContinuousFlowObs2 (const CTimeSeriesABC *pObs,long long SBID)
1212{
1313 // clears up terribly ugly repeated if statements
1414 if (pObs==NULL ) { return false ; }
@@ -25,19 +25,25 @@ void CModel::InitializeDataAssimilation(const optStruct &Options)
2525 // Initialize Streamflow assimilation
2626 if (Options.assimilate_flow )
2727 {
28- _aDAscale =new double [_nSubBasins];
29- _aDAlength =new double [_nSubBasins];
30- _aDAtimesince=new double [_nSubBasins];
31- _aDAoverride =new bool [_nSubBasins];
32- _aDAobsQ =new double [_nSubBasins];
33- _aDAlast =new double [_nSubBasins];
28+ _aDAscale =new double [_nSubBasins];
29+ _aDAscale_last=new double [_nSubBasins];
30+ _aDAQadjust =new double [_nSubBasins];
31+ _aDADrainSum =new double [_nSubBasins];
32+ _aDADownSum =new double [_nSubBasins];
33+ _aDAlength =new double [_nSubBasins];
34+ _aDAtimesince =new double [_nSubBasins];
35+ _aDAoverride =new bool [_nSubBasins];
36+ _aDAobsQ =new double [_nSubBasins];
3437 for (int p=0 ; p<_nSubBasins; p++) {
35- _aDAscale [p]=1.0 ;
36- _aDAlength [p]=0.0 ;
37- _aDAtimesince[p]=0.0 ;
38- _aDAoverride [p]=false ;
39- _aDAobsQ [p]=0.0 ;
40- _aDAlast [p]=1.0 ;
38+ _aDAscale [p]=1.0 ;
39+ _aDAscale_last[p]=1.0 ;
40+ _aDAQadjust [p]=0.0 ;
41+ _aDADrainSum [p]=0.0 ;
42+ _aDADownSum [p]=0.0 ;
43+ _aDAlength [p]=0.0 ;
44+ _aDAtimesince [p]=0.0 ;
45+ _aDAoverride [p]=false ;
46+ _aDAobsQ [p]=0.0 ;
4147 }
4248 int count=0 ;
4349 for (int p=0 ; p<_nSubBasins; p++) {
@@ -93,17 +99,20 @@ void CModel::AssimilationOverride(const int p,const optStruct& Options,const tim
9399 // Option B: instantaneous flow
94100 Qmod = _pSubBasins[p]->GetOutflowRate ();
95101 if (Qmod>PRETTY_SMALL) {
96- _aDAscale[p]=1.0 +alpha*((Qobs-Qmod)/Qmod);
102+ _aDAscale [p]=1.0 +alpha*((Qobs-Qmod)/Qmod); // if alpha = 1, Q=Qobs in observation basin
103+ _aDAQadjust[p]=alpha*(Qobs-Qmod);
97104 }
98105 else {
99- _aDAscale[p]=1.0 ;
106+ _aDAscale [p]=1.0 ;
107+ _aDAQadjust[p]=0.0 ;
100108 }
101- _aDAlast [p]=1.0 ;// no need to scale previous
109+ _aDAscale_last [p]=1.0 ;// no need to scale previous
102110 }
103111
104112 // actually scale flows
105113 // ---------------------------------------------------------------------
106- double mass_added=_pSubBasins[p]->ScaleAllFlows (_aDAscale[p]/_aDAlast[p],_aDAoverride[p],Options.timestep ,tt.model_time );
114+ double mass_added=_pSubBasins[p]->ScaleAllFlows (_aDAscale[p]/_aDAscale_last[p],_aDAoverride[p],Options.timestep ,tt.model_time );
115+ // double mass_added=_pSubBasins[p]->AdjustAllFlows(_aDAadjust[p],_aDAoverride[p],Options.timestep,tt.model_time);
107116 if (mass_added>0.0 ){_CumulInput +=mass_added/(_WatershedArea*M2_PER_KM2)*MM_PER_METER;}
108117 else {_CumulOutput-=mass_added/(_WatershedArea*M2_PER_KM2)*MM_PER_METER;}
109118
@@ -119,22 +128,24 @@ void CModel::AssimilationOverride(const int p,const optStruct& Options,const tim
119128//
120129void CModel::PrepareAssimilation (const optStruct &Options,const time_struct &tt)
121130{
122- if (!Options.assimilate_flow ) {return ;}
123- if (tt.model_time <(Options.assimilation_start + Options.timestep /2.0 )){return ;}// assimilates all data after assimilation date
131+ if (!Options.assimilate_flow ) {return ;}
132+ if (tt.model_time <(Options.assimilation_start - Options.timestep /2.0 )){return ;}// assimilates all data after or on assimilation date
124133
125134 int p,pdown;
126135 double Qobs;
127136 double t_observationsOFF=ALMOST_INF;// Only used for debugging - keep as ALMOST_INF otherwise
128137
129- int nn=(int )((tt.model_time +TIME_CORRECTION)/Options.timestep );// current timestep index
138+ int nn=(int )((tt.model_time +TIME_CORRECTION)/Options.timestep )+ 1 ;// end of timestep index
130139
131140 for (p=0 ; p<_nSubBasins; p++) {
132- _aDAlast[p]=_aDAscale[p];
141+ _aDAscale_last[p]=_aDAscale[p];
142+ _aDADrainSum[p]=0.0 ;
133143 }
134144
135145 for (int pp=_nSubBasins-1 ; pp>=0 ; pp--)// downstream to upstream
136146 {
137147 p=GetOrderedSubBasinIndex (pp);
148+ pdown = GetSubBasinByID (_pSubBasins[p]->GetDownstreamID ())->GetGlobalIndex ();
138149
139150 bool ObsExists=false ; // observation available in THIS basin
140151 // observations in this basin, determine scaling variables based upon blank/not blank
@@ -145,31 +156,45 @@ void CModel::PrepareAssimilation(const optStruct &Options,const time_struct &tt)
145156 {
146157 if (IsContinuousFlowObs2 (_pObservedTS[i],_pSubBasins[p]->GetID ()))// flow observation is available and linked to this subbasin
147158 {
148- Qobs = _pObservedTS[i]->GetSampledValue (nn+ 1 ); // +1: correction for period ending storage of hydrograph
159+ Qobs = _pObservedTS[i]->GetSampledValue (nn); // end of timestep flow
149160
150161 // bool fakeblank=((tt.model_time>30) && (tt.model_time<40)) || ((tt.model_time>45) && (tt.model_time<47));//TMP DEBUG
151162 // if (fakeblank){Qobs=RAV_BLANK_DATA;}
152163
153164 if ((Qobs!=RAV_BLANK_DATA) && (tt.model_time <t_observationsOFF))
154165 {
155166 // _aDAscale[p] calculated live in AssimilationOverride when up-to-date modelled flow available
167+ // same with _aDAQadjust[p]
156168 _aDAlength [p]=0.0 ;
157169 _aDAtimesince[p]=0.0 ;
158170 _aDAoverride [p]=true ;
159171 _aDAobsQ [p]=Qobs;
172+ _aDADrainSum [p]=0.0 ; // ??? maybe doesnt matter
173+ if (pdown != DOESNT_EXIST) {
174+ _aDADrainSum [pdown]+=_pSubBasins[p]->GetDrainageArea (); // DOES THIS HANDLE NESTING RIGHT?
175+ }
160176 }
161177 else
162178 { // found a blank or zero flow value
163- _aDAscale [p]=_aDAscale[p];
179+ _aDAscale [p]=_aDAscale[p];// same adjustment as before - scaling persists
180+ _aDAQadjust [p]=_aDAQadjust[p];// same adjustment as before - flow magnitude persists
164181 _aDAtimesince[p]+=Options.timestep ;
165182 _aDAlength [p]=0.0 ;
166183 _aDAoverride [p]=false ;
167184 _aDAobsQ [p]=0.0 ;
185+ if (pdown != DOESNT_EXIST) {
186+ _aDADrainSum[pdown] += _aDADrainSum[p];
187+ }
168188 }
169189 ObsExists=true ;
170190 break ; // avoids duplicate observations
171191 }
172192 }
193+ }
194+ else {
195+ if (pdown != DOESNT_EXIST) {
196+ _aDADrainSum[pdown] += _aDADrainSum[p];
197+ }
173198 }
174199 // no observations in this basin, get scaling from downstream
175200 // ----------------------------------------------------------------
@@ -178,25 +203,50 @@ void CModel::PrepareAssimilation(const optStruct &Options,const time_struct &tt)
178203 // if ((pdown!=DOESNT_EXIST) && (!_aDAoverride[pdown])){ //alternate - allow information to pass through reservoirs
179204 if ((pdown!=DOESNT_EXIST) && (_pSubBasins[p]->GetReservoir ()==NULL ) && (!_aDAoverride[pdown])) {
180205 _aDAscale [p]= _aDAscale [pdown];
206+ _aDAQadjust [p]= _aDAQadjust [pdown] * (_pSubBasins[p]->GetDrainageArea () / _pSubBasins[pdown]->GetDrainageArea ());
181207 _aDAlength [p]+=_pSubBasins [pdown]->GetReachLength ();
182208 _aDAtimesince [p]= _aDAtimesince[pdown];
183209 _aDAoverride [p]=false ;
184210 }
185211 else { // Nothing downstream or reservoir present in this basin, no assimilation
186212 _aDAscale [p]=1.0 ;
213+ _aDAQadjust [p]=0.0 ;
187214 _aDAlength [p]=0.0 ;
188215 _aDAtimesince[p]=0.0 ;
189216 _aDAoverride [p]=false ;
190217 }
191218 }
192219 }// end downstream to upstream
193220
221+ for (int pp=0 ;pp<_nSubBasins; pp++)// upstream to downstream
222+ {
223+ p=GetOrderedSubBasinIndex (pp);
224+ pdown = GetSubBasinByID (_pSubBasins[p]->GetDownstreamID ())->GetGlobalIndex ();
225+ if (_aDAoverride[p]) {
226+ _aDADrainSum[p]=_pSubBasins[p]->GetDrainageArea ();
227+ }
228+ else if (pdown!=DOESNT_EXIST){
229+ _aDADrainSum[p]=_aDADrainSum[pdown];
230+ }
231+ }
232+
233+
194234 // Apply time and space correction factors
195235 // ----------------------------------------------------------------
196236 double time_fact = _pGlobalParams->GetParams ()->assim_time_decay ;
197237 double distfact = _pGlobalParams->GetParams ()->assim_upstream_decay /M_PER_KM; // [1/km]->[1/m]
238+ double ECCCwt;
198239 for (p=0 ; p<_nSubBasins; p++)
199240 {
200- _aDAscale[p] =1.0 +(_aDAscale[p]-1.0 )*exp (-distfact*_aDAlength[p])*exp (-time_fact*_aDAtimesince[p]);
241+ _aDAscale [p] =1.0 +(_aDAscale[p]-1.0 )*exp (-distfact*_aDAlength[p])*exp (-time_fact*_aDAtimesince[p]);
242+ // _aDAQadjust[p] =_aDAQadjust[p]*exp(-distfact*_aDAlength[p])*exp(-time_fact*_aDAtimesince[p]);
243+
244+ if (_aDADrainSum[p]!=0.0 ){
245+ ECCCwt = (_pSubBasins[p]->GetDrainageArea () - _aDADrainSum[p])/(_aDADownSum[p]-_aDADrainSum[p]);
246+ }
247+ else {
248+ ECCCwt=1.0 ;
249+ }
250+ _aDAQadjust[p] = _aDAQadjust[p]*ECCCwt;
201251 }
202252}
0 commit comments