Exemplo n.º 1
0
        /// <summary>
        /// This is the timestepping procedure
        /// </summary>
        /// <param name="TimeStep"></param>
        public void Update(DateTime NewTime)
        {
            CurrentTimeStep = NewTime.Subtract(CurrentTime);
            #region Sum of Sinks and sources

            //Sum the sources
            var          GWFlow       = GroundwaterBoundaries.Where(var => var.IsSource(CurrentTime)).Select(var => var.GetSourceWater(CurrentTime, CurrentTimeStep));
            var          SourceFlow   = Sources.Select(var => var.GetSourceWater(CurrentTime, CurrentTimeStep));
            IWaterPacket InFlow       = WaterMixer.Mix(GWFlow.Concat(SourceFlow));
            double       InflowVolume = 0;
            if (InFlow != null)
            {
                InflowVolume = InFlow.Volume;
            }

            //Sum the Evaporation boundaries
            double EvapoVolume = _evapoBoundaries.Sum(var => var.GetSinkVolume(CurrentTime, CurrentTimeStep));

            //Sum the sinks
            double SinkVolume = Sinks.Sum(var => var.GetSinkVolume(CurrentTime, CurrentTimeStep));
            //Add the sinking groundwater boundaries
            SinkVolume += GroundwaterBoundaries.Where(var => !var.IsSource(CurrentTime)).Sum(var => var.GetSinkVolume(CurrentTime, CurrentTimeStep));
            double sumSinkSources = InflowVolume - EvapoVolume - SinkVolume;

            //If we have no water from upstream but Inflow, remove water from inflow to fill stream
            if (sumSinkSources / Volume > 5)
            {
                AddWaterPacket(CurrentTime, NewTime, InFlow.Substract(sumSinkSources - Volume * 5));
                InflowVolume   = InFlow.Volume;
                sumSinkSources = InflowVolume - EvapoVolume - SinkVolume;
            }

            //Sort the incoming water an put in to queue
            PrePareIncomingWater();

            //Calculate the surplus
            WaterToRoute = _waterInStream.Sum(var => var.Volume) + InflowVolume - EvapoVolume - SinkVolume - Volume + _incomingWater.Sum(var => var.Volume);

            //If the loss is bigger than available water, reduce Evaporation and Sinks
            if (WaterToRoute + Volume < 0)
            {
                double reductionfactor = 1 + (WaterToRoute + Volume) / (EvapoVolume + SinkVolume);
                EvapoVolume *= reductionfactor;
                SinkVolume  *= reductionfactor;
                WaterToRoute = 0;
            }

            //Convert to rates
            double qu   = sumSinkSources / CurrentTimeStep.TotalSeconds / Volume;
            double qop  = _incomingWater.Sum(var => var.Volume) / CurrentTimeStep.TotalSeconds;
            double qout = qu * Volume + qop;

            //Create a mixer class
            Mixer M = new Mixer(InFlow, EvapoVolume, SinkVolume);

            #endregion

            #region Stored water
            //Send stored water out
            if (WaterToRoute > 0)
            {
                double OutflowTime = 0;

                //The volume that needs to flow out to meet the watertotroute
                double VToSend = WaterToRoute;
                if (qu != 0)
                {
                    VToSend = qout / qu * (1 - 1 / (Math.Exp(qu * CurrentTimeStep.TotalSeconds)));
                }
                //There is water in the stream that should be routed
                while (VToSend > 0 & _waterInStream.Count > 0)
                {
                    IWaterPacket IW;
                    //Mixing during flow towards end of stream
                    double dv = _waterInStream.Peek().Volume *(Math.Exp(qu * OutflowTime) - 1);

                    //Check if the entire water packet should flow out or it should be split
                    if (_waterInStream.Peek().Volume + dv < VToSend)
                    {
                        IW = _waterInStream.Dequeue();
                    }
                    else
                    {
                        IW = _waterInStream.Peek().Substract(VToSend);
                    }

                    //Update how mush water is yet to be routed
                    VToSend -= IW.Volume;
                    //Now do the mix
                    M.Mix(dv, IW);

                    //Calculate outflow time
                    double dt;
                    if (qu == 0)
                    {
                        dt = IW.Volume / qop;
                    }
                    else
                    {
                        dt = Math.Log(qout / (qout - qu * IW.Volume)) / qu;
                    }
                    //Mixing during outflow
                    M.Mix(qout * dt - IW.Volume, IW);

                    IW.MoveInTime(TimeSpan.FromSeconds(OutflowTime + dt / 2), IW.Volume / Depth);
                    SendWaterDownstream(IW);
                    OutflowTime += dt;
                }
            }

            //Now move the remaining packets to their final destination and time
            foreach (IWaterPacket IWP in _waterInStream)
            {
                if (qu != 0)
                {
                    M.Mix(IWP.Volume * (Math.Exp(qu * CurrentTimeStep.TotalSeconds) - 1), IWP);
                }
                IWP.MoveInTime(CurrentTimeStep, IWP.Volume / Depth);
            }
            #endregion

            #region Water packets traveling right through
            double inflowtime = 0;

            //No water in stream and incoming water. Just pass through
            if (_waterInStream.Count == 0 & _incomingWater.Count > 0)
            {
                //Calculate how much incoming water is required to fill stream volume;
                double VToStore = Volume;
                if (qu != 0)
                {
                    VToStore = qop / qu * Math.Log(Volume * qu / qop + 1);
                }

                //Now send water through
                double incomingVolume = _incomingWater.Sum(var => var.Volume);
                //Send packets through until the remaining will just fill the stream
                while (incomingVolume > VToStore + 1e-12 & _incomingWater.Count != 0)
                {
                    IWaterPacket WP;
                    if (incomingVolume - _incomingWater.Peek().Volume > VToStore)
                    {
                        WP = _incomingWater.Dequeue();
                    }
                    else
                    {
                        WP = _incomingWater.Peek().Substract(incomingVolume - VToStore);
                    }

                    incomingVolume -= WP.Volume;

                    //Keep track of inflow time.
                    inflowtime += WP.Volume / qop;
                    if (qu != 0)
                    {
                        double dvr = WP.Volume * qu * Volume / qop;
                        M.Mix(dvr, WP);
                    }

                    //Calculate the time a water package uses to travel through the stream
                    TimeSpan CurrentTravelTime;
                    if (qu != 0)
                    {
                        CurrentTravelTime = TimeSpan.FromSeconds(1 / qu * Math.Log(Volume * qu / qop + 1));
                    }
                    else
                    {
                        CurrentTravelTime = TimeSpan.FromSeconds(Volume / qout);
                    }

                    //Moves right through
                    WP.MoveInTime(CurrentTravelTime, WP.Volume / Depth);
                    SendWaterDownstream(WP);
                }
            }

            #endregion

            #region Fill the stream
            //The remaining incoming water is moved forward and stays in the stream.
            while (_incomingWater.Count > 0)
            {
                IWaterPacket WP = _incomingWater.Dequeue();

                double dt = WP.Volume / qop;
                inflowtime += dt;
                double dt2 = CurrentTimeStep.TotalSeconds - inflowtime; //How much of the timestep is left when this packet has moved in.

                if (qu != 0)
                {
                    //Volume change during inflow
                    double Vnew = qop * (Math.Exp(qu * dt) - 1) / qu;
                    //Volume change while in stream
                    Vnew *= Math.Exp(qu * dt2);
                    M.Mix(Vnew - WP.Volume, WP);
                }
                //The time is half the inflowtime + the time in stream
                WP.MoveInTime(TimeSpan.FromSeconds(dt2 + dt / 2), WP.Volume / Depth);
                _waterInStream.Enqueue(WP);
            }
            #endregion

            Output.Outflow.AddSiValue(CurrentTime, NewTime, WaterToRoute / CurrentTimeStep.TotalSeconds);
            CurrentTime = NewTime;

            StartofFlowperiod = CurrentTime;
        }