Example #1
0
        /// <summary>
        /// Thomases the specified istart.
        /// </summary>
        /// <param name="istart">The istart.</param>
        /// <param name="n">The n.</param>
        /// <param name="a">a.</param>
        /// <param name="b">The b.</param>
        /// <param name="c">The c.</param>
        /// <param name="rhs">The RHS.</param>
        /// <param name="d">The d.</param>
        /// <param name="v">The v.</param>
        /// <param name="pondingData">The ponding data.</param>
        /// <param name="fail">if set to <c>true</c> [fail].</param>
        private void Thomas(int istart, int n, ref double[] a, ref double[] b, ref double[] c, ref double[] rhs, ref double[] d, ref double[] v, ref PondingData pondingData, out bool fail)
        {
            //     Short description:
            //     Thomas algorithm for solving tridiagonal system of eqns

            fail = true; // Indicate failure if we return early

            double piv = b[istart];
            if (istart == -1)
                piv = pondingData.b;
            if (piv == 0.0)
                return;
            if (istart == -1)
                pondingData.v = pondingData.rhs / piv;
            else
                v[istart] = rhs[istart] / piv;
            for (int i = istart + 1; i < istart + n; i++)
            {
                if (i == 0)
                    d[i] = pondingData.c / piv;
                else
                    d[i] = c[i - 1] / piv;
                piv = b[i] - a[i] * d[i];
                if (piv == 0.0)
                    return;
                if (i == 0)
                    v[i] = (rhs[i] - a[i] * pondingData.v) / piv;
                else
                    v[i] = (rhs[i] - a[i] * v[i - 1]) / piv;
            }

            for (int i = istart + n - 2; i >= istart; i--)
            {
                if (i == -1)
                    pondingData.v = pondingData.v - d[i + 1] * v[i + 1];
                else
                    v[i] = v[i] - d[i + 1] * v[i + 1];
            }

            fail = false;
        }
Example #2
0
        /// <summary>
        /// Gets the sol.
        /// </summary>
        /// <param name="solnum">The solnum.</param>
        /// <param name="a">a.</param>
        /// <param name="b">The b.</param>
        /// <param name="c">The c.</param>
        /// <param name="d">The d.</param>
        /// <param name="rhs">The RHS.</param>
        /// <param name="c1">The c1.</param>
        /// <param name="c2">The c2.</param>
        /// <param name="pondingData">The ponding data.</param>
        /// <param name="fail">if set to <c>true</c> [fail].</param>
        private void GetSol(int solnum, ref double[] a, ref double[] b, ref double[] c, ref double[] d, ref double[] rhs, ref double[] c1, ref double[] c2, ref PondingData pondingData, ref bool fail)
        {
            //     Short description:
            //     get and solve solute balance eqns

            //     Constant Values
            const int itmax = 20;
            const int constant_conc = 1;
            const int convection_only = 2;

            //     Determine type of solute BBC to use
            int solute_bbc;
            int j;
            double rslovr;
            bool nonlin = false;
            double wtime = 0.0, wtime1 = 0.0;

            if (ibbc == 1)
                // water table boundary condition
                solute_bbc = constant_conc;
            else if (((ibbc == 0) || (ibbc == 4)) && (q[n + 1] < 0))
                // you have a gradient with flow upward
                solute_bbc = constant_conc;
            else
                solute_bbc = convection_only;

            //    surface solute balance - assume evap. (g%res) comes from x0 store
            double rovr = roff + qbp;
            double rinf = q[0] + res;
            if (rinf > Math.Min(ersoil, ernode))
            {
                cslsur[solnum] = (rslon[solnum] + hold * cslsur[solnum] / _dt) / (rovr + rinf + _h / _dt);
                qsl[solnum][0] = rinf * cslsur[solnum];
                rslovr = rovr * cslsur[solnum];
                if (slsur[solnum] > 0.0)
                {
                    if (cslsur[solnum] < slsci[solnum])
                    {
                        if (slsur[solnum] > rinf * _dt * (slsci[solnum] - cslsur[solnum]))
                        {
                            qsl[solnum][0] = rinf * slsci[solnum];
                            slsur[solnum] = slsur[solnum] - rinf * _dt * (slsci[solnum] - cslsur[solnum]);
                        }
                        else
                        {
                            qsl[solnum][0] = rinf * cslsur[solnum] + slsur[solnum] / _dt;
                            slsur[solnum] = 0.0;
                        }
                    }
                    if (cslsur[solnum] < slscr[solnum])
                    {
                        if (slsur[solnum] > rovr * _dt * (slscr[solnum] - cslsur[solnum]))
                        {
                            rslovr = rovr * slscr[solnum];
                            slsur[solnum] = slsur[solnum] - rovr * _dt * (slscr[solnum] - cslsur[solnum]);
                        }
                        else
                        {
                            rslovr = rovr * cslsur[solnum] + slsur[solnum] / _dt;
                            slsur[solnum] = 0.0;
                        }
                        if (slsur[solnum] > _h * (slscr[solnum] - cslsur[solnum]))
                        {
                            slsur[solnum] = slsur[solnum] - _h * (slscr[solnum] - cslsur[solnum]);
                            cslsur[solnum] = slscr[solnum];
                        }
                        else
                        {
                            if (_h > 0.0)
                                cslsur[solnum] = cslsur[solnum] + slsur[solnum] / _h;
                            slsur[solnum] = 0.0;
                        }
                    }
                }
            }
            else
            {
                cslsur[solnum] = 0.0;
                qsl[solnum][0] = 0.0;
                rslovr = 0.0;
            }

            //     get eqn coeffs
            //     get production and storage components
            double thi;
            double exco1;
            //nh      call slprod
            for (int i = 0; i <= n; i++)
            {
                c1[i] = csl[solnum][i];
                thi = th[i];
                //nh         j=indxsl(solnum,i)
                j = i;
                nonlin = false;

                //Peter's CHANGE 21/10/98 to ensure zero exchange is treated as linear
                //         if (p%fip(solnum,j).eq.1.) then
                if ((MathUtilities.FloatsAreEqual(ex[solnum][j], 0.0)) || (MathUtilities.FloatsAreEqual(fip[solnum][j], 1.0)))
                {
                    //           linear exchange isotherm
                    c2[i] = 1.0;
                    exco1 = ex[solnum][j];
                }
                else
                {
                    //           nonlinear Freundlich exchange isotherm
                    nonlin = true;
                    c2[i] = 0.0;
                    if (c1[i] > 0.0)
                        c2[i] = Math.Pow(c1[i], fip[solnum][i] - 1.0);
                    //``````````````````````````````````````````````````````````````````````````````````````````````````
                    //RC         Changed by RCichota 30/jan/2010
                    exco1 = ex[solnum][j] * c2[i];
                    //            exco1=p%ex(solnum,j)*p%fip(solnum,j)*c2(i)    !<---old code
                    //
                }
                b[i] = (-(thi + exco1) / _dt) * dx[i] - qssof[i];
                //nh     1        apswim_slupf(1,solnum)*g%qex(i)-g%qssof(i)
                for (int crop = 0; crop < num_crops; crop++)
                    b[i] = b[i] - Slupf(crop, solnum) * qr[i][crop];
                //nh     1        p%slupf(solnum)*g%qex(i)
                rhs[i] = -(csl[solnum][i] * ((thold[i] + exco1) / _dt)) * dx[i];
                qsls[solnum][i] = -(csl[solnum][i] * (thold[i] + ex[solnum][j] * c2[i]) / _dt) * dx[i];
            }

            //     get dispersive and convective components
            //        use central diffs in time for convection, backward diffs for rest
            //        use central diffs in space, but for convection may need some
            //        upstream weighting to avoid instability

            for (int i = 1; i <= n; i++) // NOTE: staring from 1 is deliberate this time
            {
                if (!MathUtilities.FloatsAreEqual(x[i - 1], x[i]))
                {
                    double w1;
                    double thav = 0.5 * (th[i - 1] + th[i]);
                    double aq = Math.Abs(q[i]);
                    dc[solnum][i] = dcon[solnum] * Math.Pow(thav - SwimSoluteParameters.DTHC, SwimSoluteParameters.DTHP) +
                                    SwimSoluteParameters.Dis * Math.Pow(aq / thav, SwimSoluteParameters.Disp);
                    double dfac = thav * dc[solnum][i] / (x[i] - x[i - 1]);
                    if (slswt >= 0.5 && slswt <= 1.0)
                    {
                        //              use fixed space weighting on convection
                        w1 = MathUtilities.Sign(2.0 * slswt, q[i]);
                    }
                    else
                    {
                        //              use central diffs for convection if possible, else use
                        //                 just enough upstream weighting to avoid oscillation
                        //                 user may increase acceptable level for central diffs
                        //                 by setting p%slswt < -1
                        double accept = Math.Max(1.0, -slswt);
                        double wt = 0.0;
                        if (aq != 0.0)
                            wt = MathUtilities.Sign(Math.Max(0.0, 1.0 - 2.0 * accept * dfac / aq), q[i]);
                        w1 = 1.0 + wt;
                    }
                    double w2 = 2.0 - w1;

                    //Peter's CHANGE 21/10/98 to remove/restore Crank-Nicolson time weighting
                    //for convection
                    //            fq=.25*g%q(i)
                    //            fqc=fq*(w1*g%csl(solnum,i-1)+w2*g%csl(solnum,i))
                    //            wtime=0.25D0
                    //            wtime1=1.0D0
                    wtime = 0.5;
                    wtime1 = 0.0;
                    double fq = wtime * q[i];
                    double fqc = wtime1 * fq * (w1 * csl[solnum][i - 1] + w2 * csl[solnum][i]);

                    //           get convective component from old time level
                    qsl[solnum][i] = fqc;
                    b[i - 1] = b[i - 1] - dfac - fq * w1;
                    c[i - 1] = dfac - fq * w2;
                    a[i] = dfac + fq * w1;
                    b[i] = b[i] - dfac + fq * w2;
                    rhs[i - 1] = rhs[i - 1] + fqc;
                    rhs[i] = rhs[i] - fqc;
                }
            }

            //     allow for bypass flow
            qslbp[solnum] = 0.0;

            //     impose boundary conditions
            int k;
            if (itbc == 1)
            {
                //        constant concentration
                k = 1;
            }
            else
            {
                k = 0;
                rhs[0] = rhs[0] - qsl[solnum][0];
                if (rinf < -Math.Min(ersoil, ernode))
                {
                    b[0] = b[0] + 0.5 * rinf;
                    rhs[0] = rhs[0] - 0.5 * rinf * csl[solnum][0];
                }
            }

            int neq;
            if (solute_bbc == constant_conc)
            {
                //        constant concentration
                //nh
                csl[solnum][n] = cslgw[solnum];
                //nh
                rhs[n - 1] = rhs[n - 1] - c[n - 1] * csl[solnum][n];
                neq = n;
            }
            else
            {
                //        convection only
                b[n] = b[n] - 0.5 * q[n + 1];
                rhs[n] = rhs[n] + 0.5 * q[n + 1] * csl[solnum][n];
                neq = n + 1;
            }
            //     allow for two nodes at same depth
            j = 0;
            for (int i = 1; i <= n; i++)
            {
                if (!MathUtilities.FloatsAreEqual(x[i - 1], x[i]))
                {
                    j = j + 1;
                    a[j] = a[i];
                    b[j] = b[i];
                    rhs[j] = rhs[i];
                    c[j - 1] = c[i - 1];
                }
                else
                {
                    b[j] = b[j] + b[i];
                    rhs[j] = rhs[j] + rhs[i];
                }
            }
            //     save old g%csl(0),g%csl(p%n)
            double csl0 = csl[solnum][0];
            double csln = csl[solnum][n];
            neq = neq - (n - j);
            int itcnt = 0;
            //     solve for concentrations

            loop:
            //nh      call thomas(neq,0,a(k),b(k),c(k),rhs(k),dum,d(k),g%csl(solnum,k),
            //nh     :            dum,fail)
            double[] csltemp = new double[n + 1];
            for (int i = 0; i <= n; i++)
                csltemp[i] = csl[solnum][i];
            Thomas(k, neq, ref a, ref b, ref c, ref rhs, ref d, ref csltemp, ref pondingData, out fail);
            for (int i = 0; i <= n; i++)
                csl[solnum][i] = csltemp[i];
            // nh end subroutine
            itcnt++;
            slwork = slwork + neq;
            if (fail)
                return;
            j = k + neq - 1;
            if (solute_bbc == convection_only)
            {
                csl[solnum][n] = csl[solnum][j];
                j--;
            }
            for (int i = n - 1; i > 0; i--)
            {
                if (!MathUtilities.FloatsAreEqual(x[i], x[i + 1]))
                {
                    csl[solnum][i] = csl[solnum][j];
                    j--;
                }
                else
                {
                    csl[solnum][i] = csl[solnum][i + 1];
                }
            }

            if (nonlin)
            {
                //        test for convergence
                double dmax = 0.0;
                for (int i = 0; i <= n; i++)
                {
                    double dabs = Math.Abs(csl[solnum][i] - c1[i]);
                    if (dmax < dabs)
                        dmax = dabs;
                }
                if (dmax > slcerr)
                {
                    if (itcnt == itmax)
                    {
                        fail = true;
                        return;
                    }
                    //           keep iterating using Newton-Raphson technique
                    //           next c^fip for Freundlich isotherm is approximated as
                    //              cn^fip=c^fip+p%fip*c^(p%fip-1)*(cn-c)
                    //                    =p%fip*c^(p%fip-1)*cn+(1-p%fip)*c^fip
                    j = 0;
                    for (int i = 0; i <= n; i++)
                    {
                        if (!MathUtilities.FloatsAreEqual(x[i - 1], x[i]))
                        {
                            if (i > 0)
                                j++;
                        }
                        //cnh               kk=indxsl(solnum,i)
                        int kk = i;
                        if (!MathUtilities.FloatsAreEqual(fip[solnum][i], 1.0))
                        {
                            double cp = 0.0;
                            if (csl[solnum][i] > 0.0)
                                cp = Math.Pow(csl[solnum][i], fip[solnum][i] - 1.0);

                            //````````````````````````````````````````````````````````````````````````````````````````````````````````````````
                            //RC      Changed by RCichota (29/Jan/2010), original code is commented out
                            double d1 = cp - c2[i];
                            //                  d1=p%fip(solnum,kk)*(cp-c2(i))
                            //                  d2=(1.-p%fip(solnum,kk))
                            //     :              *(g%csl(solnum,i)*cp-c1(i)*c2(i))
                            c1[i] = csl[solnum][i];
                            c2[i] = cp;
                            b[j] = b[j] - (ex[solnum][kk] / _dt) * d1 * dx[i];
                            //                  rhs(j)=rhs(j)+(p%ex(solnum,kk)/g%dt
                            //     :                            -p%betaex(solnum,kk))
                            //     :                          *d2*p%dx(i)
                            //````````````````````````````````````````````````````````````````````````````````````````````````````````````````
                            // Changes in the calc of d1 are to agree with the calc of exco1 above (no need to multiply by p%fip
                            // If p%fip < 1, the unkown is Cw, and is only used in the calc of b. thus rhs is commented out.
                            //`
                        }
                    }
                    goto loop;
                }
            }

            //     get surface solute balance?
            if (rinf < -Math.Min(ersoil, ernode))
            {
                //        flow out of surface
                //CHANGES 6/11/98 to remove/restore Crank-Nicolson time weighting for convection
                //-----
                //         g%qsl(solnum,0)=.5*rinf*(csl0+g%csl(solnum,0))
                qsl[solnum][0] = 0.5 * rinf * (wtime1 * csl0 + 4.0 * wtime * csl[solnum][0]);

                double rslout = -qsl[solnum][0];
                if (slsur[solnum] > 0.0)
                {
                    //           allow for surface applied solute
                    if (csl[solnum][0] < slsci[solnum])
                    {
                        if (slsur[solnum] > -rinf * _dt * (slsci[solnum] - csl[solnum][0]))
                        {
                            rslout = -rinf * slsci[solnum];
                            slsur[solnum] = slsur[solnum] + rinf * _dt * (slsci[solnum] - csl[solnum][0]);
                        }
                        else
                        {
                            rslout = rslout + slsur[solnum] / _dt;
                            slsur[solnum] = 0.0;
                        }
                    }
                }
                //        get surface solute balance
                cslsur[solnum] = (rslon[solnum] + rslout + hold * cslsur[solnum] / _dt) / (rovr + _h / _dt);
                rslovr = rovr * cslsur[solnum];
            }

            rsloff[solnum] = rslovr - qslbp[solnum];
            //     get solute fluxes
            for (int i = 1; i <= n; i++)
            {
                if (!MathUtilities.FloatsAreEqual(x[i - 1], x[i]))
                {
                    double dfac = 0.5 * (th[i - 1] + th[i]) * dc[solnum][i] / (x[i] - x[i - 1]);
                    double aq = Math.Abs(q[i]);
                    double accept = Math.Max(1.0, -slswt);
                    double wt = 0.0;
                    if (aq != 0.0)
                        wt = MathUtilities.Sign(Math.Max(0.0, 1.0 - 2.0 * accept * dfac / aq), q[i]);
                    //Peter's CHANGES 21/10/98 to remove/restore Crank-Nicolson time weighting
                    //for convection
                    //            g%qsl(solnum,i)=g%qsl(solnum,i)
                    //     :                    +.25*g%q(i)*((1.+wt)*g%csl(solnum,i-1)
                    //     :                    +(1.-wt)*g%csl(solnum,i))
                    //     1                    +dfac*(g%csl(solnum,i-1)-g%csl(solnum,i))
                    qsl[solnum][i] = qsl[solnum][i] + wtime * q[i] * ((1.0 + wt) * csl[solnum][i - 1]
                                     + (1.0 - wt) * csl[solnum][i]) + dfac * (csl[solnum][i - 1] - csl[solnum][i]);
                }

            }
            for (int i = 2; i < n; i++)
            {
                if (MathUtilities.FloatsAreEqual(x[i - 1], x[i]))
                {
                    qsl[solnum][i] = (dx[i] * qsl[solnum][i - 1] + dx[i - 1] * qsl[solnum][i + 1]) / (dx[i - 1] + dx[i]);
                }
            }

            rslex[solnum] = 0.0;
            for (int i = 0; i <= n; i++)
            {
                //nh         j=indxsl(solnum,i)
                j = i;
                double cp = 1.0;
                if (!MathUtilities.FloatsAreEqual(fip[solnum][i], 1.0))
                {
                    cp = 0.0;
                    if (csl[solnum][i] > 0.0)
                        cp = Math.Pow(csl[solnum][i], fip[solnum][i] - 1.0);
                }
                cslt[solnum][i] = (th[i] + ex[solnum][j] * cp) * csl[solnum][i];

                for (int crop = 0; crop < num_crops; crop++)
                    rslex[solnum] += qr[i][crop] * csl[solnum][i] * Slupf(crop, solnum);

                qsls[solnum][i] += (csl[solnum][i] * (thold[i] + ex[solnum][j] * cp) / _dt) * dx[i];
            }

            if (solute_bbc == constant_conc)
            {
                //        constant concentration
                //nh         j=indxsl(solnum,p%n)
                j = n;
                qsl[solnum][n + 1] = qsl[solnum][n] - qsls[solnum][n] - qssof[n] * csl[solnum][n];
                //nh     :                  -g%qex(p%n)*g%csl(solnum,p%n)*p%slupf(solnum)
                //nh     :              -g%qex(p%n)*g%csl(solnum,p%n)*apswim_slupf(1,solnum)

                for (int crop = 0; crop < num_crops; crop++)
                    qsl[solnum][n + 1] -= qr[n][crop] * csl[solnum][n] * Slupf(crop, solnum);
            }
            else
            {
                //        convection only
                //CHANGES 6/11/98 to remove/restore Crank-Nicolson time weighting for convection
                //-----
                //         g%qsl(solnum,p%n+1)=.5*g%q(p%n+1)*(csln+g%csl(solnum,p%n))
                qsl[solnum][n + 1] = 0.5 * q[n + 1] * (wtime1 * csln + 4.0 * wtime * csl[solnum][n]);
            }
        }
Example #3
0
        /// <summary>
        /// Solves the specified itlim.
        /// </summary>
        /// <param name="itlim">The itlim.</param>
        /// <param name="fail">if set to <c>true</c> [fail].</param>
        private void Solve(int itlim, ref bool fail)
        {
            //     Short description:
            //     solves for this time step

            int it = 0;
            double wpold = _wp;
            int iroots = 0;
            // loop until solved or too many iterations or Thomas algorithm fails
            int i1;
            int i2;
            double[] a = new double[n + 1];
            double[] b = new double[n + 1];
            double[] c = new double[n + 1];
            double[] d = new double[n + 1];
            double[] rhs = new double[n + 1];
            double[] dp = new double[n + 1];
            double[] vbp = new double[n + 1];
            PondingData pondingData = new PondingData();

            do
            {
                it++;
                //        get balance eqns
                // LOOK OUT. THE FORTRAN CODE USED ARRAY INDICES STARTING AT -1
                Baleq(it, ref iroots, ref slos, ref csl, out i1, out i2, ref a, ref b, ref c, ref rhs, ref pondingData);
                //   test for convergence to soln
                // nh hey - wpf has no arguments !
                // nh         _wp = wpf(n, _dx, th)
                _wp = Wpf();

                double balerr = ron - roff - q[n + 1] - rex - res + rssf - (_h - hold + _wp - wpold) / _dt;
                double err = 0.0;
                for (int i = i1; i <= i2; i++)
                {
                    double aerr = Math.Abs(rhs[i]);
                    if (err < aerr)
                        err = aerr;
                }

                // switch off iteration for root extraction if err small enough
                if (err < errex * rex && iroots == 0)
                    iroots = 1;
                if (Math.Abs(balerr) < ersoil && err < ernode)
                    fail = false;
                else
                {
                    int neq = i2 - i1 + 1;
                    Thomas(i1, neq, ref a, ref b, ref c, ref rhs, ref d, ref dp, ref pondingData, out fail);
                    _work += neq;
                    //nh            if(fail)go to 90
                    if (fail)
                    {
                        //nh               call warning_error(Err_internal,
                        //nh     :            'swim will reduce timestep to solve water movement')
                        summary.WriteMessage(this, "swim will reduce timestep to avoid error in water balance");
                        break;
                    }

                    fail = true;
                    //           limit step size_of for soil nodesn
                    int i0 = Math.Max(i1, 0);
                    for (int i = i0; i <= i2; i++)
                    {
                        if (dp[i] > dppl)
                            dp[i] = dppl;
                        if (dp[i] < -dpnl)
                            dp[i] = -dpnl;
                    }
                    //           update solution
                    int j = i0;
                    for (int i = i0; i <= i2; i++)
                    {
                        _p[j] += dp[i];
                        if (j > 0 && j < n - 1)
                        {
                            if (MathUtilities.FloatsAreEqual(x[j], x[j + 1]))
                            {
                                j++;
                                _p[j] = _p[j - 1];
                            }
                        }
                        j++;
                    }
                    if (i1 == -1)
                        _h = Math.Max(0.0, _h + pondingData.v);
                    //_h = Math.Max(0.0, _h + dp[-1]);
                }
            }
            while (fail && it < itlim);

            if (fail)
            {
                summary.WriteMessage(this, clock.Today.ToString("dd-mmm-yyyy"));
                summary.WriteMessage(this, "Maximum iterations reached - swim will reduce timestep");
            }

            //     solve for solute movement
            else
            {
                for (int solnum = 0; solnum < num_solutes; solnum++)
                {
                    GetSol(solnum, ref a, ref b, ref c, ref d, ref rhs, ref dp, ref vbp, ref pondingData, ref fail);
                    if (fail)
                    {
                        summary.WriteMessage(this, "swim will reduce timestep to solve solute movement");
                        break;
                    }
                }
            }
        }
Example #4
0
        /// <summary>
        /// Baleqs the specified it.
        /// </summary>
        /// <param name="it">It.</param>
        /// <param name="iroots">The iroots.</param>
        /// <param name="tslos">The tslos.</param>
        /// <param name="tcsl">The TCSL.</param>
        /// <param name="ibegin">The ibegin.</param>
        /// <param name="iend">The iend.</param>
        /// <param name="a">a.</param>
        /// <param name="b">The b.</param>
        /// <param name="c">The c.</param>
        /// <param name="rhs">The RHS.</param>
        /// <param name="pondingData">The ponding data.</param>
        private void Baleq(int it, ref int iroots, ref double[] tslos, ref double[][] tcsl, out int ibegin, out int iend, ref double[] a, ref double[] b, ref double[] c, ref double[] rhs, ref PondingData pondingData)
        {
            //     Short Description:
            //     gets coefficient matrix and rhs for Newton soln of balance eqns
            //
            //     Some variables had the same name as some global variables and so
            //     these were renamed (by prefixing with g%t - for temp)
            //     this include p%isol, g%csl, p%slos

            const double hcon = 7.0e-7;
            const double hair = 0.5;

            double[] psip = new double[n + 1];
            double[] psipp = new double[n + 1];
            double[] thp = new double[n + 1];
            double[] hkp = new double[n + 1];
            double[] qsp = new double[n + 1];
            double[] qp1 = new double[n + 2];
            double[] qp2 = new double[n + 2];
            double[] psios = new double[n + 1];
            double[,] qexp = new double[3, n + 1];
            double[] qdrain = new double[n + 1];
            double[] qdrainpsi = new double[n + 1];
            double[] qssofp = new double[n + 1];
            double v1;

            //   initialise for first iteration
            if (it == 1)
            {
                ifirst = 0;
                ilast = n;
                if (itbc == 2 && hold > 0.0)
                    ifirst = -1;
                if (ibbc == 0)
                    gr = bbc_value;

                if (ibbc == 1)
                {
                    _psi[n] = bbc_value;
                    _p[n] = Pf(_psi[n]);
                }
            }

            //   get soil water variables and their derivatives
            for (int i = 0; i <= n; i++)
                Watvar(i, _p[i], out _psi[i], out psip[i], out psipp[i], out th[i], ref thp[i], out hk[i], ref hkp[i]);

            //   check boundary potls
            if (itbc == 0 && isbc == 0 && _psi[0] > 0.0)
            {
                //        infinite conductance and no ponding allowed
                _psi[0] = 0.0;
                _p[0] = Pf(_psi[0]);
                Watvar(0, _p[0], out v1, out psip[0], out psipp[0], out th[0], ref thp[0], out hk[0], ref hkp[0]);
            }
            if (ibbc == 3 && _psi[n] > bbc_value)
            {
                //        seepage at bottom boundary
                _psi[n] = bbc_value;
                _p[n] = Pf(_psi[n]);
                Watvar(n, _p[n], out v1, out psip[n], out psipp[n], out th[n], ref thp[n], out hk[n], ref hkp[n]);
            }

            //   get fluxes between nodes
            double absgf = Math.Abs(gf);
            double w1, w2;
            double deltap;
            double deltax;
            double skd;
            double hkdp1;
            double hkdp2;
            double hsoil;
            for (int i = 1; i <= n; i++)
            {
                if (!MathUtilities.FloatsAreEqual(x[i - 1], x[i]))
                {
                    deltax = x[i] - x[i - 1];
                    deltap = _p[i] - _p[i - 1];
                    double hkd1 = hk[i - 1] * psip[i - 1];
                    double hkd2 = hk[i] * psip[i];
                    hkdp1 = hk[i - 1] * psipp[i - 1] + hkp[i - 1] * psip[i - 1];
                    hkdp2 = hk[i] * psipp[i] + hkp[i] * psip[i];
                    skd = hkd1 + hkd2;
                    if (swt >= 0.5 && swt <= 1.0)
                    {
                        //              use fixed space weighting on gravity flow
                        w1 = MathUtilities.Sign(2.0 * swt, gf);
                    }
                    else
                    {
                        //              use central diffs for gravity flow if possible, else use
                        //                 just enough upstream weighting to avoid instability
                        //                 user may increase acceptable level for central diffs
                        //                 by setting p%swt < -1
                        double accept = Math.Max(1.0, -swt);
                        double wt = 0.0;
                        //               if(absgf.ne.0..and.hkp(i).ne.0.)then
                        double gfhkp = gf * hkp[i];
                        if (gfhkp != 0.0)
                        {
                            if (it == 1)
                            {
                                //                     value=1.-accept*(skd+(g%p(i)-g%p(i-1))*hkdp2)/(absgf*deltax*hkp(i))
                                double value = 1.0 - accept * (skd) / (Math.Abs(gfhkp) * deltax);
                                //                     value=min(1d0,value)
                                swta[i] = MathUtilities.Sign(Math.Max(0.0, value), gfhkp);
                            }
                            wt = swta[i];
                        }

                        w1 = 1.0 + wt;
                    }
                    w2 = 2.0 - w1;

                    if ((w1 > 2.0) || (w1 < 0.0))
                        IssueWarning("bad space weighting factor");

                    q[i] = -0.5 * (skd * deltap / deltax - gf * (w1 * hk[i - 1] + w2 * hk[i]));
                    qp1[i] = -0.5 * ((hkdp1 * deltap - skd) / deltax - gf * w1 * hkp[i - 1]);
                    qp2[i] = -0.5 * ((hkdp2 * deltap + skd) / deltax - gf * w2 * hkp[i]);

                    _swf[i] = w1;
                }
            }
            //   get fluxes to storage
            for (int i = 0; i <= n; i++)
            {
                qs[i] = (th[i] - thold[i]) * dx[i] / _dt;
                qsp[i] = thp[i] * dx[i] / _dt;
            }
            //   get uptake fluxes to roots if still in iterations
            if (iroots < 2)
            {
                for (int i = 0; i <= n; i++)
                {
                    psios[i] = _psi[i];
                    for (int solnum = 0; solnum < num_solutes; solnum++)
                        psios[i] = psios[i] - tslos[solnum] * tcsl[solnum][i];
                }
                Uptake(ref psios, ref hk, ref psip, ref hkp, ref qex, ref qexp);
            }
            rex = 0.0;
            for (int i = 0; i <= n; i++)
                rex = rex + qex[i];

            //   NIH  get subsurface fluxes
            Drain(out qdrain, out qdrainpsi);

            rssf = 0.0;
            for (int i = 0; i <= n; i++)
            {
                qssif[i] = SubSurfaceInFlow[i] / 10.0 / 24.0; // assumes mm and daily timestep - need something better !!!!
                qssof[i] = qdrain[i]; // Add outflow calc here later
                qssofp[i] = qdrainpsi[i] * psip[i];
                rssf += qssif[i] - qssof[i];
            }

            //   get soil surface fluxes, taking account of top boundary condition
            //
            double respsi;
            double roffd;
            if (itbc == 0)
            {
                //       infinite conductance
                ifirst = 0;
                if (_psi[0] < 0.0)
                {
                    hsoil = Math.Max(hair, Math.Exp(hcon * _psi[0]));
                    res = resp * (hsoil - hair) / (1.0 - hair);
                    respsi = resp * hcon * hsoil / (1.0 - hair);
                }
                else
                {
                    res = resp;
                    respsi = 0.0;
                }

                if (isbc == 0)
                {
                    //           no ponding allowed
                    _h = 0.0;
                    double q0 = ron - res + hold / _dt;

                    if (_psi[0] < 0.0 || q0 < qs[0] + qex[0] + q[1] - qssif[0] + qssof[0])
                    {
                        q[0] = q0;
                        qp2[0] = -respsi * psip[0];
                        roff = 0.0;
                        roffd = 0.0;
                    }
                    else
                    {
                        //              const zero potl
                        ifirst = 1;
                        q[0] = qs[0] + qex[0] + q[1] - qssif[0] + qssof[0];
                        roff = q0 - q[0];
                        roffd = -qp2[1];
                    }
                }
                else
                {
                    //           runoff zero or given by a function
                    if (_psi[0] < 0.0)
                    {
                        _h = 0.0;
                        roff = 0.0;
                        q[0] = ron - res + hold / _dt;
                        qp2[0] = -respsi * psip[0];
                    }
                    else
                    {
                        _h = _psi[0];
                        roff = 0.0;
                        roffd = 0.0;
                        if (isbc == 2)
                            Runoff(t, _h, out roff, out roffd);
                        q[0] = ron - roff - res - (_h - hold) / _dt;
                        qp2[0] = (-roffd - respsi - 1.0 / _dt) * psip[0];
                    }
                }
            }

            if (itbc == 1)
            {
                //       const potl
                ifirst = 1;
                if (_psi[0] < 0.0)
                {
                    hsoil = Math.Exp(hcon * _psi[0]);
                    res = resp * (hsoil - hair) / (1.0 - hair);
                }
                else
                {
                    res = resp;
                }
                _h = Math.Max(_psi[0], 0.0);
                q[0] = qs[0] + qex[0] + q[1] - qssif[0] + qssof[0];
                //        flow to source of potl treated as "runoff" (but no bypass flow)
                roff = ron - res - (_h - hold) / _dt - q[0];
            }
            else if (itbc == 2)
            {
                //       conductance given by a function
                double g_, gh;
                double q0 = ron - resp + hold / _dt;
                if (isbc == 0)
                {
                    //           no ponding allowed
                    ifirst = 0;
                    _h = 0.0;
                    SCond(t, _h, out g_, out gh);

                    if (q0 > -g_ * _psi[0])
                    {
                        res = resp;
                        respsi = 0.0;
                        q[0] = -g_ * _psi[0];
                        qp2[0] = -g_ * psip[0];
                        roff = q0 - q[0];
                        roffd = -qp2[0];
                    }
                    else
                    {
                        hsoil = Math.Exp(hcon * _psi[0]);
                        res = resp * (hsoil - hair) / (1.0 - hair);
                        respsi = resp * hcon * hsoil / (1.0 - hair);
                        q0 = ron - res + hold / _dt;
                        q[0] = q0;
                        qp2[0] = -respsi * psip[0];
                        roff = 0.0;
                    }
                }
                else
                {
                    //           runoff zero or given by a function
                    SCond(t, _h, out g_, out gh);
                    if (q0 > -g_ * _psi[0])
                    {
                        //              initialise _h if necessary
                        if (ifirst == 0)
                            _h = Math.Max(_psi[0], 0.0);
                        ifirst = -1;
                        res = resp;
                        roff = 0.0;
                        roffd = 0.0;
                        if (isbc == 2 && _h > 0.0)
                            Runoff(t, _h, out roff, out roffd);
                        q[0] = g_ * (_h - _psi[0]);
                        qp1[0] = g_ + gh * (_h - _psi[0]);
                        qp2[0] = -g_ * psip[0];
                        // WE MAY NEED TO HANDLE THE -1 INDICES SOMEHOW (though I'm not sure they are ever used)
                        pondingData.rhs = -(ron - roff - res - q[0] - (_h - hold) / _dt);
                        pondingData.b = -roffd - qp1[0] - 1.0 / _dt;
                        pondingData.c = -qp2[0];
                        //rhs[-1] = -(ron - roff - res - q[0] - (_h - hold) / _dt);
                        //b[-1] = -roffd - qp1[0] - 1.0 / _dt;
                        //c[-1] = -qp2[0];
                    }
                    else
                    {
                        ifirst = 0;
                        _h = 0.0;
                        roff = 0.0;
                        hsoil = Math.Exp(hcon * _psi[0]);
                        res = resp * (hsoil - hair) / (1.0 - hair);
                        respsi = resp * hcon * hsoil / (1.0 - hair);
                        q[0] = ron - res + hold / _dt;
                        qp2[0] = -respsi * psip[0];
                    }
                }
            }
            //     bypass flow?
            qbp = 0.0;
            //qbpd = 0.0;
            //double qbpp = 0.0;
            //double qbps = 0.0;
            //double qbpsp = 0.0;

            //   bottom boundary condition
            if (ibbc == 0)
            {
                //       zero matric potl gradient
                q[n + 1] = (gf + gr) * hk[n];
                qp1[n + 1] = (gf + gr) * hkp[n];
            }
            else if (ibbc == 1)
            {
                //       const potl
                ilast = n - 1;
                q[n + 1] = q[n] - qs[n] - qex[n] + qssif[n] - qssof[n];
            }
            else if (ibbc == 2)
            {
                //       zero flux
                q[n + 1] = 0.0;
                qp1[n + 1] = 0.0;
            }
            else if (ibbc == 3)
            {
                //       seepage
                //nh added to allow seepage to user potential at bbc
                if (_psi[n] >= bbc_value)
                {
                    q[n + 1] = q[n] - qs[n] - qex[n] + qssif[n] - qssof[n];
                    if (q[n + 1] >= 0.0)
                    {
                        ilast = n - 1;
                        //qbpd = 0.0;
                    }
                    else
                    {
                        ilast = n;
                    }
                }
                if (ilast == n)
                {
                    q[n + 1] = 0.0;
                    qp1[n + 1] = 0.0;
                }
            }
            else if (ibbc == 4)
            {
                //       flux calculated according to head difference from water table
                double headdiff = _psi[n] - x[n] + bbc_value / 10.0;
                q[n + 1] = headdiff * water_table_conductance;
                qp1[n + 1] = psip[n] * water_table_conductance;
            }
            //    get Newton-Raphson equations
            int i1 = Math.Max(ifirst, 0);
            int k = i1 - 1;
            bool xidif = true;
            for (int i = i1; i <= ilast; i++)
            {
                //        allow for two nodes at same depth
                bool xipdif = true;
                if (xidif)
                {
                    k = k + 1;
                    int j = i + 1;
                    //           j is next different node, k is equation
                    if (i > 0 && i < n - 1)
                    {
                        if (MathUtilities.FloatsAreEqual(x[i], x[i + 1]))
                        {
                            xipdif = false;
                            j = i + 2;
                            q[i + 1] = ((x[j] - x[i]) * q[i] + (x[i] - x[i - 1]) * q[j]) / (x[j] - x[i - 1]);
                        }
                    }
                    rhs[k] = -(q[i] - q[j]);
                    a[k] = qp1[i];
                    b[k] = qp2[i] - qp1[j];
                    c[k] = -qp2[j];
                }
                rhs[k] = rhs[k] + qs[i] + qex[i] - qssif[i] + qssof[i];
                b[k] = b[k] - qsp[i] - qssofp[i];

                if (iroots == 0)
                {
                    //            a(k)=a(k)-qexp(1,i)
                    b[k] = b[k] - qexp[1, i];
                    //            c(k)=c(k)-qexp(3,i)
                }
                else
                {
                    iroots = 2;
                }
                xidif = xipdif;
            }

            ibegin = ifirst;
            iend = k;
        }