Exemple #1
0
        /// <summary>
        /// Solves the ADE from time ti to tf. Diffusion of solute ignored - dispersion
        /// coeff = dispersivity* abs(pore water velocity).
        /// </summary>
        /// <param name="ti">start time (h).</param>
        /// <param name="tf">finish time.</param>
        /// <param name="thi">initial layer water contents.</param>
        /// <param name="thf">initial layer water contents.</param>
        /// <param name="dwexs">water extracted from layers over period ti to tf.</param>
        /// <param name="win">water in at top of profile.</param>
        /// <param name="cin">solute concn in win.</param>
        /// <param name="n">no. of soil layers.</param>
        /// <param name="ns">no. of solutes.</param>
        /// <param name="nex">no. of water extraction streams.</param>
        /// <param name="dx">layer thicknesses.</param>
        /// <param name="jt">layer soil type numbers for solute.</param>
        /// <param name="dsmmax">max change in sm of any layer to aim for each time step; controls time step size.</param>
        /// <param name="sm">layer masses of solute per cc.</param>
        /// <param name="sdrn">cumulative solute drainage.</param>
        /// <param name="nssteps">cumulative no. of time steps for ADE soln.</param>
        /// <param name="c"></param>
        /// <param name="sex">cumulative solute extractions in water extraction streams.</param>
        /// <param name="extraction">bool indicating if solute extraction is enabled.</param>
        /// <param name="solProps">Solute properties.</param>
        private static void Solute(double ti, double tf, double[] thi, double[] thf, double[,] dwexs, double win,
                                   double[] cin, int n, int ns, int nex, double[] dx, int[] jt, double dsmmax,
                                   ref double[,] sm, ref double[] sdrn, ref int[] nssteps, ref double[,] c, ref double[,,] sex, bool extraction, SolProps solProps)
        {
            int itmax = 20; //max iterations for finding c from sm
            double eps = 0.00001; // for stopping
            int i, it, j, k;
            double dc, dm, dmax, dt=0, f=0, fc=0, r, rsig, rsigdt, sig, sigdt, t, tfin, th, v1, v2;
            double[] dz = new double[n - 1 + 1];
            double[] coef1 = new double[n - 1 + 1];
            double[] coef2 = new double[n - 1 + 1];
            double[] csm = new double[n + 1];
            double[] tht = new double[n + 1];
            double[] dwex = new double[n + 1];
            double[] qsex = new double[n + 1];
            double[] qsexd = new double[n + 1];
            double[,] qsexs = new double[nex + 1, n + 1];
            double[,] qsexsd = new double[nex + 1, n + 1];
            double[] aa = new double[n + 1]; // these are 0 based
            double[] bb = new double[n + 1];
            double[] cc = new double[n + 1];
            double[] dd = new double[n + 1];
            double[] dy = new double[n + 1];
            double[] ee = new double[n + 1];
            double[] q = new double[n + 1];
            double[] qw = new double[n + 1];
            double[] qya = new double[n + 1];
            double[] qyb = new double[n + 1];

            sig = 0.5;
            rsig = 1.0 / sig;
            tfin = tf;
            for (int x = 2; x <= n; x++)
                dz[x - 1] = 0.5 * (dx[x - 1] + dx[x]);
            //get average water fluxes
            //dwex = sum(dwexs, 2) !total changes in sink water extraction since last call
            if (dwexs.GetLength(0) > 0 && dwexs.GetLength(1) > 0)
            {
                Matrix<double> dwexsM = Matrix<double>.Build.DenseOfArray(dwexs);
                for (int x = 0; x < dwexs.GetLength(1); x++)
                {
                    dwex[x] = MathUtilities.Sum(dwexsM.Row(x));
                }
            }

            r = 1.0 / (tf - ti);
            qw[0] = r * win;

            // Compounding errors due to float/double issues start here. May or may not be a problem.
            tht = MathUtilities.Multiply_Value(MathUtilities.Subtract(thf, thi), r);
            for (i = 1; i <= n; i++)
                qw[i] = qw[i - 1] - dx[i] * tht[i] - r * dwex[i];

            //get constant coefficients
            for (i = 1; i <= n - 1; i++)
            {
                v1 = 0.5 * qw[i];
                v2 = 0.5 * (solProps.dis[jt[i]] + solProps.dis[jt[i + 1]]) * Math.Abs(qw[i]) / dz[i];
                coef1[i] = v1 + v2;
                coef2[i] = v1 - v2;
            }

            for (j = 1; j <= ns; j++)
            {
                t = ti;
                if (qw[0] > 0.0)
                    q[0] = qw[0] * cin[j];
                else
                    q[0] = 0.0;

                qyb[0] = 0.0;

                while (t < tfin)
                //get fluxes
                {
                    for (i = 1; i <= n; i++)
                    {
                        //get c and csm = dc / dsm(with theta constant)
                        k = jt[i];
                        th = thi[i] + (t - ti) * tht[i];
                        if (solProps.isotype[j, k] == "no" || sm[j, i] < 0.0) //handle sm < 0 here
                        {
                            csm[i] = 1.0 / th;
                            c[j, i] = csm[i] * sm[j, i];
                        }
                        else if (solProps.isotype[j, k] == "li")
                        {
                            csm[i] = 1.0 / (th + solProps.bd[k] * solProps.isopar[k, j].ElementAt(1));
                            c[j, i] = csm[i] * sm[j, i];
                        }
                        else
                        {
                            for (it = 1; it <= itmax; it++) //get c from sm using Newton's method and bisection
                            {
                                if (c[j, i] < 0.0)
                                    c[j, i] = 0.0; //c and sm are >= 0
                                solProps.Isosub(solProps.isotype[j, k], c[j, i], dsmmax, ref solProps.isopar[j,k], out f, out fc);
                                csm[i] = 1.0 / (th + solProps.bd[k] * fc);
                                dm = sm[j, i] - (solProps.bd[k] * f + th * c[j, i]);
                                dc = dm * csm[i];
                                if (sm[j, i] >= 0.0 && c[j, i] + dc < 0.0)
                                    c[j, i] = 0.5 * c[j, i];
                                else
                                    c[j, i] = c[j, i] + dc;
                                if (Math.Abs(dm) < eps * (sm[j, i] + 10.0 * dsmmax))
                                    break;
                                if (it == itmax)
                                {
                                    Console.WriteLine("solute: too many iterations getting c");
                                    Environment.Exit(1);
                                }
                            }
                        }
                    }

                    for (int x = 1; x <= n - 1; x++)
                    {
                        q[x] = coef1[x] * c[j, x] + coef2[x] * c[j, x + 1];
                        qya[x] = coef1[x] * csm[x];
                        qyb[x] = coef2[x] * csm[x + 1];
                    }
                    q[n] = qw[n] * c[j, n];
                    qya[n] = qw[n] * csm[n];
                    //get time step
                    double[] absQ = new double[n+1];
                    for (int x = 1; x <= n; x++)
                        absQ[x] = Math.Abs(q[x] - q[x - 1] / dx[x]);

                    dmax = MathUtilities.Max(absQ);
                    if (dmax == 0.0)
                        dt = tfin - t;
                    else if (dmax < 0.0)
                    {
                        Console.WriteLine("solute: errors in fluxes prevent continuation");
                        Environment.Exit(1);
                    }
                    else
                        dt = dsmmax / dmax;

                    if (t + 1.1 * dt > tfin)
                    {
                        dt = tfin - t;
                        t = tfin;
                    }
                    else
                        t = t + dt;

                    sigdt = sig * dt;
                    rsigdt = 1.0 / sigdt;
                    //adjust q for change in theta
                    for (int x = 1; x <= n - 1; x++)
                        q[x] = q[x] - sigdt * (qya[x] * tht[x] * c[j, x] + qyb[x] * tht[x + 1] * c[j, x]);
                    q[n] = q[n] - sigdt * qya[n] * tht[n] * c[j, n];

                    //get and solve eqns
                    for (int x = 2; x <= n; x++)
                        aa[x] = qya[x - 1];

                    for (int x = 1; x <= n - 1; x++)
                        cc[x] = -qyb[x];

                    Matrix<double> qsexsM = Matrix<double>.Build.DenseOfArray(qsexs);
                    Matrix<double> qsexsdM = Matrix<double>.Build.DenseOfArray(qsexsd);

                    if (sex.GetLength(0) > 1)  //get extraction
                    {
                        double[] ctemp = new double[n + 1];
                        for (int x = 1; x <= n; x++)
                            ctemp[x] = c[j, x];

                        qsexs = qsexsM.ToArray();
                        qsexsd = qsexsdM.ToArray();
                        sink.Ssinks(t, ti, tf, j, dwexs, ctemp, ref qsexs, ref qsexsd);
                        qsex = qsexsM.ColumnSums().ToArray();
                        qsexd = qsexsdM.ColumnSums().ToArray();
                        for (int x = 1; x <= n; x++)
                        {
                            bb[x] = qyb[x - 1] - qya[x] - qsexd[x] * csm[x] - dx[x] * rsigdt;
                            dd[x] = -(q[x - 1] - q[x] - qsex[x]) * rsig;
                        }
                    }
                    else
                    {
                        for (int x = 1; x <= n; x++)
                        {
                            bb[x] = qyb[x - 1] - qya[x] - dx[x] * rsigdt;
                            dd[x] = -(q[x - 1] - q[x]) * rsig;
                        }
                    }

                    Tri(1, n, aa, ref bb, cc, dd, ref ee, ref dy);
                    //update unknowns
                    Matrix<double> smM = Matrix<double>.Build.DenseOfArray(sm);
                    qsexsM = Matrix<double>.Build.DenseOfArray(qsexs);
                    qsexsdM = Matrix<double>.Build.DenseOfArray(qsexsd);
                    sdrn[j] = sdrn[j] + (q[n] + sig * qya[n] * dy[n]) * dt;
                    smM.SetRow(j, smM.Row(j) + Vector<double>.Build.DenseOfArray(dy.Slice(1, n)));
                    sm = smM.ToArray();
                    if (sex.GetLength(0) > 1) // need to test, this will need to be transposed.
                    {
                        Matrix<double> sexM = Matrix<double>.Build.Dense(sex.GetLength(0), sex.GetLength(1));
                        for (int x = 0; x < sex.GetLength(0); x++)
                            for (int y = 0; y < sex.GetLength(1); y++)
                                sexM[x, y] = sex[x, y, j];

                        Vector<double> dysub = Vector<double>.Build.DenseOfArray(dy.Slice(1, n));
                        for (i = 1; i <= nex; i++)
                            sexM.SetRow(i, sexM.Row(i) + (qsexsM.Row(i) + sig * qsexsdM.Row(i) * Vector<double>.Build.DenseOfArray(csm) * dysub) * dt);

                        for (int x = 0; x < sex.GetLength(0); x++)
                            for (int y = 0; y < sex.GetLength(1); y++)
                                sex[j, y, x] = sexM[y, x];
                    }
                    nssteps[j] = nssteps[j] + 1;
                }
            }
        }
Exemple #2
0
        /// <summary>
        /// Solves the equation of continuity from time ts to tfin.
        /// </summary>
        /// <param name="sol">solute properties.</param>
        /// <param name="sdata">soil data.</param>
        /// <param name="ts">start time (h).</param>
        /// <param name="tfin">finish time.</param>
        /// <param name="qprec">precipitation (or water input) rate (fluxes are in cm/h).</param>
        /// <param name="qevap">potl evaporation rate from soil surface.</param>
        /// <param name="nsol">no. of solutes.</param>
        /// <param name="nex">no. of water extraction streams.</param>
        /// <param name="h0">surface head, equal to depth of surface pond.</param>
        /// <param name="S">degree of saturation ("effective satn") of layers.</param>
        /// <param name="evap">cumulative evaporation from soil surface (cm, not initialised).</param>
        /// <param name="runoff">cumulative runoff.</param>
        /// <param name="infil">cumulative net infiltration (time integral of flux across surface).</param>
        /// <param name="drn">cumulative net drainage (time integral of flux across bottom).</param>
        /// <param name="nsteps">cumulative no. of time steps for RE soln.</param>
        /// <param name="jt">layer soil type numbers for solute.</param>
        /// <param name="cin">solute concns in water input (user's units/cc).</param>
        /// <param name="c0">solute concns in surface pond.</param>
        /// <param name="sm">solute (mass) concns in layers.</param>
        /// <param name="soff">cumulative solute runoff (user's units).</param>
        /// <param name="sinfil">cumulative solute infiltration.</param>
        /// <param name="sdrn">cumulative solute drainage.</param>
        /// <param name="nssteps">cumulative no. of time steps for ADE soln.</param>
        /// <param name="wex">cumulative water extractions from layers.</param>
        /// <param name="sex">cumulative solute extractions from layers.</param>
        public static void Solve(SolProps sol, SoilData sdata, double ts, double tfin, double qprec, double qevap, int nsol, int nex,
                          ref double h0, ref double[] S, ref double evap, ref double runoff, ref double infil, ref double drn, ref int nsteps, int[] jt, double[] cin,
                          ref double[] c0, ref double[,] sm, ref double[] soff, ref double[] sinfil, ref double[] sdrn, ref int[] nssteps, ref double[,] wex, ref double[,,] sex)
        {
            // Since S.Length is one greater in Fortran to account for the 0 based index, this line is included
            // so that the +1 nomenclature can be kept below to avoid (further) confusion.
            int sLength = S.Length - 1;

            bool again, extraction, initpond, maxpond;
            int i, iflux, ih0, iok, isatbot, itmp, ns, nsat, nsatlast, nsteps0;
            int[] isat = new int[sLength + 1];
            double accel, dmax, dt, dwinfil, dwoff, fac, infili, qpme, qprec1, rsig, rsigdt, sig, t, ti, win, xtblbot;
            double[] dSdt = new double[sLength + 1];
            double[] h = new double[sLength + 1];
            double[] xtbl = new double[sLength + 1];
            double[] thi = new double[sLength + 1];
            double[] thf = new double[sLength + 1];
            double[] qwex = new double[sLength + 1];
            double[] qwexd = new double[sLength + 1];
            double[,] dwexs = new double[sLength + 1, nex + 1];
            double[,] qwexs = new double[sLength + 1, nex + 1];
            double[,] qwexsd = new double[sLength + 1, nex + 1];
            double[] aa = new double[sLength+1]; // these are 0 based
            double[] bb = new double[sLength+1];
            double[] cc = new double[sLength + 1];
            double[] dd = new double[sLength + 1];
            double[] dy = new double[sLength + 1];
            double[] ee = new double[sLength + 1];
            double[] q = new double[sLength + 1];
            double[] qya = new double[sLength + 1];
            double[] qyb = new double[sLength + 1];
            double[] cav = new double[nsol + 1];
            double[] sinfili = new double[nsol + 1];
            double[,] c;
            sd = sdata;
            c = new double[nsol + 1, sd.n + 1];
            ISink sink = new SinkDripperDrain(); // Sink type can be changed here.
            /*
             ! The saturation status of a layer is stored as 0 or 1 in isat since S may be
             ! >1 (because of previous overshoot) when a layer desaturates. Fluxes at the
             ! beginning of a time step and their partial derivs wrt S or h of upper and
             ! lower layers or boundaries are stored in q, qya and qyb.
            */
            isatbot = 0;
            xtblbot = 0;
            dt = 0.0;
            fac = 0.0;
            initpond = false;
            extraction = false;
            dwoff = 0.0;
            ti = 0.0;
            infili = 0.0;

            if (nex > 0)
                extraction = true;

            if (sLength != sd.n)
            {
                Console.WriteLine("solve: Size of S differs from table data.");
                Environment.Exit(1);
            }

            //-----set up for boundary conditions
            if (botbc == "constant head") // !h at bottom bdry specified
                if (hbot < sd.he[sd.n])
                {
                    isatbot = 0;
                    xtblbot = Sbot;
                }
                else
                {
                    isatbot = 1;
                    xtblbot = hbot - sd.he[sd.n];
                }
            //-----end set up for boundary conditions
            //-----initialise
            t = ts;
            nsteps0 = nsteps;
            nsat = 0;
            //initialise saturated regions
            for (int x = 1; x < sLength; x++)
                if (S[x] >= 1.0)
                {
                    isat[x] = 1;
                    h[x] = sd.he[x];
                }
                else
                {
                    isat[x] = 0;
                    h[x] = sd.he[x] - 1.0;
                }

            if (nsol > 0)
            {
                //set solute info
                thi = MathUtilities.Multiply(sd.ths, S); //initial th
                dwexs.Populate2D(0); //initial water extracted from layers

                ti = t;
                infili = infil;
                sinfili = sinfil;
                double c0Temp = c0[1];
                if (h0 > 0 && cin.Any(x => x != c0Temp)) // count(c0 /= cin) > 0)
                    initpond = true; //initial pond with different solute concn
                else
                    initpond = false;
                c.Populate2D(0); //temp storage for soln concns
            }
            //-----end initialise
            //-----solve until tfin

            while (t < tfin)
            {
                //-----take next time step
                for (iflux = 1; iflux <= 2; iflux++) //sometimes need twice to adjust h at satn
                {
                    nsatlast = nsat; // for detecting onset of profile saturation
                    nsat = isat.Sum();
                    sig = 0.5;
                    if (nsat != 0)
                        sig = 1.0; //time weighting sigma
                    rsig = 1.0 / sig;
                    //-----get fluxes and derivs
                    //     get table entries
                    for (int x = 1; x < isat.Length; x++)
                        if (isat[x] == 0)
                            xtbl[x] = S[x];
                        else
                            xtbl[x] = h[x] - sd.he[x];

                    //get surface flux
                    qpme = qprec - qevap; //input rate at saturation
                    qprec1 = qprec; //may change qprec1 to maintain pond if required
                    if (h[1] <= 0 && h0 <= 0 && nsat < sd.n) //no ponding
                    {
                        ns = 1; //start index for eqns
                        sd.GetQ(0, new [] {0, 0, isat[1] }, new [] {0, 0, xtbl[1] }, out q[0], out qya[0], out qyb[0]);
                        if (q[0] < qpme)
                        {
                            q[0] = qpme;
                            qyb[0] = 0;
                        }
                        maxpond = false;
                    }
                    else //ponding
                    {
                        ns = 0;
                        sd.GetQ(0, new [] {0, 1, isat[1] }, new [] {0, h0 - sd.he[1], xtbl[1] }, out q[0], out qya[0], out qyb[0]);
                        if (h0 >= h0max && qpme > q[0])
                        {
                            maxpond = true;
                            ns = 1;
                        }
                        else
                            maxpond = false;
                    }

                    //get profile fluxes
                    for (i = 1; i <= sd.n - 1; i++)
                        sd.GetQ(i, new [] {0, isat[i], isat[i + 1] }, new [] {0, xtbl[i], xtbl[i + 1] }, out q[i], out qya[i], out qyb[i]);

                    //get bottom flux
                    switch (botbc)
                    {
                        case "constant head":
                            sd.GetQ(sd.n, new [] {0, isat[sd.n], isatbot }, new [] {0, xtbl[sd.n], xtblbot }, out q[sd.n], out qya[sd.n], out qyb[sd.n]);
                            break;
                        case "0.0 flux":
                            q[sd.n] = 0;
                            qya[sd.n] = 0;
                            break;
                        case "free drainage":
                            sd.GetK(sd.n, isat[sd.n], xtbl[sd.n], out q[sd.n], out qya[sd.n]);
                            break;
                        case "seepage":
                            if (h[sd.n] <= -0.5 * sd.dx[sd.n])
                            {
                                q[sd.n] = 0;
                                qya[sd.n] = 0;
                            }
                            else
                                sd.GetQ(sd.n, new int[] {0, isat[sd.n], 1 }, new double[] {0, xtbl[sd.n], -sd.he[sd.n] }, out q[sd.n], out qya[sd.n], out qyb[sd.n]);
                            break;
                        default:
                            Console.Out.WriteLine("solve: illegal bottom boundary condn");
                            Environment.Exit(1);
                            break;
                    }

                    if (extraction) //get rate of extraction
                    {
                        sink.Wsinks(t, isat, xtbl, sd.he, ref qwexs, ref qwexsd);

                        for (int x = 1; x < qwexs.GetLength(1); x++)
                        {
                            qwex[x] = Matrix<double>.Build.DenseOfArray(qwexs).Column(x).Sum();
                            qwexd[x] = Matrix<double>.Build.DenseOfArray(qwexsd).Column(x).Sum();
                        }
                    }
                    again = false; //flag for recalcn of fluxes
                //-----end get fluxes and derivs
                //----estimate time step dt
                    dmax = 0;
                    dSdt = MathUtilities.CreateArrayOfValues(0, dSdt.Length);
                    for (int x = 1; x <= sd.n; x++)
                    {
                        if (isat[x] == 0)
                            dSdt[x] = Math.Abs(q[x] - q[x - 1] + (extraction ? qwex[x] : 0)) / (sd.ths[x] * sd.dx[x]);
                    }

                    dmax = MathUtilities.Max(dSdt); //Max derivative | dS / dt |
                    if (dmax > 0)
                    {
                        dt = dSmax / dmax;
                        // if pond going adjust dt
                        if (h0 > 0 && (q[0] - qpme) * dt > h0)
                            dt = (h0 - 0.5 * h0min) / (q[0] - qpme);
                    }
                    else //steady state flow
                    {
                        if (qpme >= q[sd.n]) //step to finish -but what if extraction varies with time ???
                            dt = tfin - t;
                        else
                            dt = -(h0 - 0.5 * h0min) / (qpme - q[sd.n]); //pond going so adjust dt
                    }

                    if (dt > dtmax)
                        dt = dtmax; //user's limit
               // if initial step, improve h where S>= 1
               if (nsteps == nsteps0 && nsat > 0 && iflux == 1)
                    {
                        again = true;
                        dt = 1.0e-20 * (tfin - ts);
                    }
                    if (nsat == sd.n && nsatlast < sd.n && iflux == 1)
                    {
                        //profile has just become saturated so adjust h values
                        again = true;
                        dt = 1.0e-20 * (tfin - ts);
                    }
                    if (t + 1.1 * dt > tfin) //step to finish
                    {
                        dt = tfin - t;
                        t = tfin;
                    }
                    else
                        t = t + dt; //tentative update
                    //-----end estimate time step dt
                    //-----get and solve eqns
                    rsigdt = 1.0 / (sig * dt);
                    //aa, bb, cc and dd hold coeffs and rhs of tridiag eqn set
                    for (int x = ns; x <= sd.n - 1; x++)
                    {
                        aa[x + 1] = qya[x];
                        cc[x] = -qyb[x];
                    }
                    if (extraction)
                        for (int x = 1; x <= sd.n; x++)
                            dd[x] = -(q[x - 1] - q[x] - qwex[x]) * rsig;
                    else
                        for (int x = 1; x <= sd.n; x++)
                            dd[x] = -(q[x - 1] - q[x]) * rsig;

                    iok = 0; //flag for time step test
                    itmp = 0; //counter to abort if not getting solution
                    while (iok == 0) //keep reducing time step until all ok
                    {
                        itmp = itmp + 1;
                        accel = 1.0 - 0.05 * Math.Min(10, Math.Max(0, itmp - 4)); //acceleration
                        if (itmp > 20)
                        {
                            Console.Out.WriteLine("solve: too many iterations of equation solution");
                            Environment.Exit(1);
                        }
                        if (ns < 1)
                        {
                            bb[0] = -qya[0] - rsigdt;
                            dd[0] = -(qpme - q[0]) * rsig;
                        }

                        for (int x = 1; x <= sd.n; x++)
                        {
                            bb[x] = qyb[x - 1] - qya[x];
                            if (isat[x]==0)
                                bb[x] -= sd.ths[x] * sd.dx[x] * rsigdt;

                            if (extraction)
                                bb[x] -= qwexd[x];
                        }

                        Tri(ns, sd.n, aa, ref bb, cc, dd, ref ee, ref dy);
                        //dy contains dS or, for sat layers, h values
                        iok = 1;
                        if (!again)
                        {
                            //check if time step ok, if not then set fac to make it less
                            iok = 1;
                            for (i = 1; i <= sd.n; i++)
                            {
                                if (isat[i] == 0) //check change in S
                                {
                                    if (Math.Abs(dy[i]) > dSfac * dSmax)
                                    {
                                        fac = Math.Max(0.5, accel * Math.Abs(dSmax / dy[i]));
                                        iok = 0;
                                        break;
                                    }
                                    if (-dy[i] > dSmaxr * S[i])
                                    {
                                        fac = Math.Max(0.5, accel * dSmaxr * S[i] / (-dSfac * dy[i]));
                                        iok = 0;
                                        break;
                                    }
                                    if (S[i] < 1.0 && S[i] + dy[i] > Smax)
                                    {
                                        fac = accel * (0.5 * (1.0 + Smax) - S[i]) / dy[i];
                                        iok = 0;
                                        break;
                                    }
                                    if (S[i] >= 1.0 && dy[i] > 0.5 * (Smax - 1.0))
                                    {
                                        fac = 0.25 * (Smax - 1.0) / dy[i]; iok = 0;
                                        break;
                                    }
                                }
                            }
                            if (iok == 1 && ns < 1 && h0 < h0max && h0 + dy[0] > h0max + dh0max)
                            {
                                //start of runoff
                                fac = (h0max + 0.5 * dh0max - h0) / dy[0];
                                iok = 0;
                            }
                            if (iok == 1 && ns < 1 && h0 > 0.0 && h0 + dy[0] < h0min)
                            {
                                //pond going
                                fac = -(h0 - 0.5 * h0min) / dy[0];
                                iok = 0;
                            }
                            if (iok == 0) //reduce time step
                            {
                                t = t - dt;
                                dt = fac * dt;
                                t = t + dt;
                                rsigdt = 1.0 / (sig * dt);
                                nless = nless + 1; //count step size reductions
                            }
                            if (isat[1] != 0 && iflux == 1 && h[1] < 0.0 && h[1] + dy[1] > 0.0)
                            {
                                //incipient ponding - adjust state of saturated regions
                                t = t - dt;
                                dt = 1.0e-20 * (tfin - ts);
                                rsigdt = 1.0 / (sig * dt);
                                again = true;
                                iok = 0;
                            }
                        }
                    } //end while

                    //-----end get and solve eqns
                    //-----update unknowns
                    ih0 = 0;
                    if (!again)
                    {
                        dwoff = 0.0;
                        if (ns < 1)
                        {
                            h0 = h0 + dy[0];
                            if (h0 < 0.0 && dy[0] < 0.0)
                                ih0 = 1; //pond g1.0

                            evap = evap + qevap * dt;
                            //note that fluxes required are q at sigma of time step
                            dwinfil = (q[0] + sig * (qya[0] * dy[0] + qyb[0] * dy[1])) * dt;
                        }
                        else
                        {
                            dwinfil = (q[0] + sig * qyb[0] * dy[1]) * dt;
                            if (maxpond)
                            {
                                evap = evap + qevap * dt;
                                if (qprec > qprecmax) // set input to maintain pond
                                {
                                    qpme = q[0] + sig * qyb[0] * dy[1];
                                    qprec1 = qpme + qevap;
                                    dwoff = 0.0;
                                }
                                else
                                {
                                    dwoff = qpme * dt - dwinfil;
                                }
                                runoff = runoff + dwoff;
                            }
                            else
                                evap = evap + qprec1 * dt - dwinfil;
                        }
                        infil = infil + dwinfil;
                        if (nsol > 0) //get surface solute balance
                        {
                            if (initpond) //pond concn != cin
                            {
                                if (h0 > 0.0)
                                {
                                    if (ns == 1) // if max pond depth
                                        dy[0] = 0.0;
                                    for (int x = 1; x < cav.Length; x++)
                                        cav[x] = ((2.0 * h0 - dy[0]) * c0[x] + qprec1 * dt * cin[x]) / (2.0 * h0 + dwoff + dwinfil);
                                    for (int x = 1; x < c0.Length; x++)
                                        c0[x] = 2.0 * cav[x] - c0[x]; //This needs to be tested from FORTRAN; no example in original code.
                                }
                                else
                                {
                                    for (int x = 1; x < cav.Length; x++)
                                        cav[x] = ((h0 - dy[0]) * c0[x] + qprec1 * dt * cin[x]) / (dwoff + dwinfil);
                                    initpond = false; //pond gone
                                    c0 = cin; // for output if any pond at end
                                }
                                soff = MathUtilities.Add(soff, MathUtilities.Multiply_Value(cav, dwoff));
                                sinfil = MathUtilities.Add(sinfil, MathUtilities.Multiply_Value(cav, dwinfil));
                            }
                            else
                            {
                                soff = MathUtilities.Add(soff, MathUtilities.Multiply_Value(cav, dwoff));
                                sinfil = MathUtilities.Add(sinfil, MathUtilities.Multiply_Value(cin, qprec1 * dt - dwoff));
                            }
                        }

                        // There's a condition based on botbc in the FORTRAN here, but both paths
                        // resolve to the same equation.
                        drn = drn + (q[sd.n] + sig * qya[sd.n] * dy[sd.n]) * dt;

                        if (extraction)
                        {
                            Matrix<double> dwexsM = Matrix<double>.Build.DenseOfArray(dwexs);
                            Matrix<double> qwexsM = Matrix<double>.Build.DenseOfArray(qwexs);
                            Matrix<double> qwexsdM = Matrix<double>.Build.DenseOfArray(qwexsd);
                            Matrix<double> wexM = Matrix<double>.Build.DenseOfArray(wex);
                            Vector<double> wexV;
                            Vector<double> dwexsV;
                            Vector<double> qwexsV;
                            Vector<double> qwexsdV;
                            if (nsol > 0)
                            {
                                //dwexs = dwexs + (qwexs + sig * qwexsd * spread(dy(1:n), 2, nex)) * dt
                                for (i = 1; i <= nex; i++)
                                {
                                    dwexsV = dwexsM.Column(i);
                                    qwexsV = qwexsM.Column(i);
                                    qwexsdV = qwexsdM.Column(i);
                                    dwexsV = dwexsV + (qwexsV + sig * qwexsdV * Vector<double>.Build.DenseOfArray(dy.Slice(1, sd.n))) * dt;
                                    dwexsM.Column(i, dwexsV);
                                }
                                dwexs = dwexsM.ToArray();
                            }

                            //wex = wex + (qwexs + sig * qwexsd * spread(dy(1:n), 2, nex)) * dt
                            if (wex.GetLength(0) > 1) // analog for if (present(wex))
                            {
                                for (i = 1; i <= nex; i++)
                                {
                                    qwexsV = qwexsM.Column(i);
                                    qwexsdV = qwexsdM.Column(i);
                                    wexV = wexM.Column(i);
                                    wexV = wexV + (qwexsV + sig * qwexsdV * Vector<double>.Build.DenseOfArray(dy.Slice(1, sd.n))) * dt;
                                    wexM.Column(i, wexV);
                                }
                                wex = wexM.ToArray();
                            }
                        }
                    }

                    for (i = 1; i <= sd.n; i++)
                    {
                        if (isat[i] == 0)
                        {
                            if (!again)
                            {
                                S[i] = S[i] + dy[i];
                                if (S[i] > 1.0 && dy[i] > 0.0) //saturation of layer
                                {
                                    isat[i] = 1;
                                    h[i] = sd.he[i];
                                }
                            }
                        }
                        else
                        {
                            h[i] = h[i] + dy[i];
                            if (i == 1 && ih0 != 0 && h[i] >= sd.he[i]) h[i] = sd.he[i] - 1.0; //pond gone
                            if (h[i] < sd.he[i])  //desaturation of layer
                            {
                                isat[i] = 0;
                                h[i] = sd.he[i];
                            }
                        }
                    }
                    //-----end update unknowns
                    if (!again)
                        break;
                }

                if (dt <= dtmin)
                {
                    Console.WriteLine("solve: time step = " + dt);
                    Environment.Exit(1);
                }
                //-----end take next time step
                //remove negative h0 (optional)
                if (h0 < 0.0 && isat[1] == 0)
                {
                    infil = infil + h0;
                    S[1] = S[1] + h0 / (sd.ths[1] * sd.dx[1]);
                    h0 = 0.0;
                }
                nsteps = nsteps + 1;
                //solve for solute transport if required
                if (nwsteps * (nsteps / nwsteps) == nsteps)
                {
                    // This is function getsolute() in FORTRAN. Inline here as it uses a huge number of vars and is only used once.
                    if (nsol > 0 && t > ti)
                    {
                        thf = MathUtilities.Multiply(sd.ths, S); //final th before call
                        win = infil - infili; //water in at top over time interval
                        cav = MathUtilities.Divide_Value(MathUtilities.Subtract(sinfil, sinfili), win); //average concn in win
                        Solute(ti, t, thi, thf, dwexs, win, cav, sd.n, nsol, nex, sd.dx, jt, dsmmax, ref sm, ref sdrn, ref nssteps, ref c, ref sex, extraction, sol);
                        ti = t;
                        thi = thf;
                        dwexs.Populate2D(0);
                        infili = infil;
                        sinfili = sinfil; // for next interval
                    }
                }
            }
            //-----end solve until tfin
            //finalise solute transport if required
        }
Exemple #3
0
        public static List<object> TestSolute(double ti, double tf, double[] thi, double[] thf, double[,] dwexs, double win,
                                   double[] cin, int n, int ns, int nex, double[] dx, int[] jt, double dsmmax,
                                   double[,] sm, double[] sdrn, int[] nssteps, double[,] c, double[,,] sex, bool extraction, SolProps solProps)
        {
            List<object> ret = new List<object>();
            Solute(ti, tf, thi, thf, dwexs, win, cin, n, ns, nex, dx, jt, dsmmax, ref sm, ref sdrn, ref nssteps, ref c, ref sex, extraction, solProps);

            ret.Add(sm);
            ret.Add(sdrn);
            ret.Add(nssteps);
            ret.Add(c);

            return ret;
        }
Exemple #4
0
        public static List <object> TestSolute(double ti, double tf, double[] thi, double[] thf, double[,] dwexs, double win,
                                               double[] cin, int n, int ns, int nex, double[] dx, int[] jt, double dsmmax,
                                               double[,] sm, double[] sdrn, int[] nssteps, double[,] c, double[,,] sex, bool extraction, SolProps solProps)
        {
            List <object> ret = new List <object>();

            Solute(ti, tf, thi, thf, dwexs, win, cin, n, ns, nex, dx, jt, dsmmax, ref sm, ref sdrn, ref nssteps, ref c, ref sex, extraction, solProps);

            ret.Add(sm);
            ret.Add(sdrn);
            ret.Add(nssteps);
            ret.Add(c);

            return(ret);
        }
Exemple #5
0
        /// <summary>
        /// Solves the ADE from time ti to tf. Diffusion of solute ignored - dispersion
        /// coeff = dispersivity* abs(pore water velocity).
        /// </summary>
        /// <param name="ti">start time (h).</param>
        /// <param name="tf">finish time.</param>
        /// <param name="thi">initial layer water contents.</param>
        /// <param name="thf">initial layer water contents.</param>
        /// <param name="dwexs">water extracted from layers over period ti to tf.</param>
        /// <param name="win">water in at top of profile.</param>
        /// <param name="cin">solute concn in win.</param>
        /// <param name="n">no. of soil layers.</param>
        /// <param name="ns">no. of solutes.</param>
        /// <param name="nex">no. of water extraction streams.</param>
        /// <param name="dx">layer thicknesses.</param>
        /// <param name="jt">layer soil type numbers for solute.</param>
        /// <param name="dsmmax">max change in sm of any layer to aim for each time step; controls time step size.</param>
        /// <param name="sm">layer masses of solute per cc.</param>
        /// <param name="sdrn">cumulative solute drainage.</param>
        /// <param name="nssteps">cumulative no. of time steps for ADE soln.</param>
        /// <param name="c"></param>
        /// <param name="sex">cumulative solute extractions in water extraction streams.</param>
        /// <param name="extraction">bool indicating if solute extraction is enabled.</param>
        /// <param name="solProps">Solute properties.</param>
        private static void Solute(double ti, double tf, double[] thi, double[] thf, double[,] dwexs, double win,
                                   double[] cin, int n, int ns, int nex, double[] dx, int[] jt, double dsmmax,
                                   ref double[,] sm, ref double[] sdrn, ref int[] nssteps, ref double[,] c, ref double[,,] sex, bool extraction, SolProps solProps)
        {
            int    itmax = 20;    //max iterations for finding c from sm
            double eps = 0.00001; // for stopping
            int    i, it, j, k;
            double dc, dm, dmax, dt = 0, f = 0, fc = 0, r, rsig, rsigdt, sig, sigdt, t, tfin, th, v1, v2;

            double[] dz    = new double[n - 1 + 1];
            double[] coef1 = new double[n - 1 + 1];
            double[] coef2 = new double[n - 1 + 1];
            double[] csm   = new double[n + 1];
            double[] tht   = new double[n + 1];
            double[] dwex  = new double[n + 1];
            double[] qsex  = new double[n + 1];
            double[] qsexd = new double[n + 1];
            double[,] qsexs  = new double[nex + 1, n + 1];
            double[,] qsexsd = new double[nex + 1, n + 1];
            double[] aa  = new double[n + 1]; // these are 0 based
            double[] bb  = new double[n + 1];
            double[] cc  = new double[n + 1];
            double[] dd  = new double[n + 1];
            double[] dy  = new double[n + 1];
            double[] ee  = new double[n + 1];
            double[] q   = new double[n + 1];
            double[] qw  = new double[n + 1];
            double[] qya = new double[n + 1];
            double[] qyb = new double[n + 1];

            sig  = 0.5;
            rsig = 1.0 / sig;
            tfin = tf;
            for (int x = 2; x <= n; x++)
            {
                dz[x - 1] = 0.5 * (dx[x - 1] + dx[x]);
            }
            //get average water fluxes
            //dwex = sum(dwexs, 2) !total changes in sink water extraction since last call
            if (dwexs.GetLength(0) > 0 && dwexs.GetLength(1) > 0)
            {
                Matrix <double> dwexsM = Matrix <double> .Build.DenseOfArray(dwexs);

                for (int x = 0; x < dwexs.GetLength(1); x++)
                {
                    dwex[x] = MathUtilities.Sum(dwexsM.Row(x));
                }
            }

            r     = 1.0 / (tf - ti);
            qw[0] = r * win;

            // Compounding errors due to float/double issues start here. May or may not be a problem.
            tht = MathUtilities.Multiply_Value(MathUtilities.Subtract(thf, thi), r);
            for (i = 1; i <= n; i++)
            {
                qw[i] = qw[i - 1] - dx[i] * tht[i] - r * dwex[i];
            }

            //get constant coefficients
            for (i = 1; i <= n - 1; i++)
            {
                v1       = 0.5 * qw[i];
                v2       = 0.5 * (solProps.dis[jt[i]] + solProps.dis[jt[i + 1]]) * Math.Abs(qw[i]) / dz[i];
                coef1[i] = v1 + v2;
                coef2[i] = v1 - v2;
            }

            for (j = 1; j <= ns; j++)
            {
                t = ti;
                if (qw[0] > 0.0)
                {
                    q[0] = qw[0] * cin[j];
                }
                else
                {
                    q[0] = 0.0;
                }

                qyb[0] = 0.0;

                while (t < tfin)
                //get fluxes
                {
                    for (i = 1; i <= n; i++)
                    {
                        //get c and csm = dc / dsm(with theta constant)
                        k  = jt[i];
                        th = thi[i] + (t - ti) * tht[i];
                        if (solProps.isotype[j, k] == "no" || sm[j, i] < 0.0) //handle sm < 0 here
                        {
                            csm[i]  = 1.0 / th;
                            c[j, i] = csm[i] * sm[j, i];
                        }
                        else if (solProps.isotype[j, k] == "li")
                        {
                            csm[i]  = 1.0 / (th + solProps.bd[k] * solProps.isopar[k, j].ElementAt(1));
                            c[j, i] = csm[i] * sm[j, i];
                        }
                        else
                        {
                            for (it = 1; it <= itmax; it++) //get c from sm using Newton's method and bisection
                            {
                                if (c[j, i] < 0.0)
                                {
                                    c[j, i] = 0.0; //c and sm are >= 0
                                }
                                solProps.Isosub(solProps.isotype[j, k], c[j, i], dsmmax, ref solProps.isopar[j, k], out f, out fc);
                                csm[i] = 1.0 / (th + solProps.bd[k] * fc);
                                dm     = sm[j, i] - (solProps.bd[k] * f + th * c[j, i]);
                                dc     = dm * csm[i];
                                if (sm[j, i] >= 0.0 && c[j, i] + dc < 0.0)
                                {
                                    c[j, i] = 0.5 * c[j, i];
                                }
                                else
                                {
                                    c[j, i] = c[j, i] + dc;
                                }
                                if (Math.Abs(dm) < eps * (sm[j, i] + 10.0 * dsmmax))
                                {
                                    break;
                                }
                                if (it == itmax)
                                {
                                    Console.WriteLine("solute: too many iterations getting c");
                                    Environment.Exit(1);
                                }
                            }
                        }
                    }

                    for (int x = 1; x <= n - 1; x++)
                    {
                        q[x]   = coef1[x] * c[j, x] + coef2[x] * c[j, x + 1];
                        qya[x] = coef1[x] * csm[x];
                        qyb[x] = coef2[x] * csm[x + 1];
                    }
                    q[n]   = qw[n] * c[j, n];
                    qya[n] = qw[n] * csm[n];
                    //get time step
                    double[] absQ = new double[n + 1];
                    for (int x = 1; x <= n; x++)
                    {
                        absQ[x] = Math.Abs(q[x] - q[x - 1] / dx[x]);
                    }

                    dmax = MathUtilities.Max(absQ);
                    if (dmax == 0.0)
                    {
                        dt = tfin - t;
                    }
                    else if (dmax < 0.0)
                    {
                        Console.WriteLine("solute: errors in fluxes prevent continuation");
                        Environment.Exit(1);
                    }
                    else
                    {
                        dt = dsmmax / dmax;
                    }

                    if (t + 1.1 * dt > tfin)
                    {
                        dt = tfin - t;
                        t  = tfin;
                    }
                    else
                    {
                        t = t + dt;
                    }

                    sigdt  = sig * dt;
                    rsigdt = 1.0 / sigdt;
                    //adjust q for change in theta
                    for (int x = 1; x <= n - 1; x++)
                    {
                        q[x] = q[x] - sigdt * (qya[x] * tht[x] * c[j, x] + qyb[x] * tht[x + 1] * c[j, x]);
                    }
                    q[n] = q[n] - sigdt * qya[n] * tht[n] * c[j, n];

                    //get and solve eqns
                    for (int x = 2; x <= n; x++)
                    {
                        aa[x] = qya[x - 1];
                    }

                    for (int x = 1; x <= n - 1; x++)
                    {
                        cc[x] = -qyb[x];
                    }

                    Matrix <double> qsexsM = Matrix <double> .Build.DenseOfArray(qsexs);

                    Matrix <double> qsexsdM = Matrix <double> .Build.DenseOfArray(qsexsd);

                    if (sex.GetLength(0) > 1)  //get extraction
                    {
                        double[] ctemp = new double[n + 1];
                        for (int x = 1; x <= n; x++)
                        {
                            ctemp[x] = c[j, x];
                        }

                        qsexs  = qsexsM.ToArray();
                        qsexsd = qsexsdM.ToArray();
                        sink.Ssinks(t, ti, tf, j, dwexs, ctemp, ref qsexs, ref qsexsd);
                        qsex  = qsexsM.ColumnSums().ToArray();
                        qsexd = qsexsdM.ColumnSums().ToArray();
                        for (int x = 1; x <= n; x++)
                        {
                            bb[x] = qyb[x - 1] - qya[x] - qsexd[x] * csm[x] - dx[x] * rsigdt;
                            dd[x] = -(q[x - 1] - q[x] - qsex[x]) * rsig;
                        }
                    }
                    else
                    {
                        for (int x = 1; x <= n; x++)
                        {
                            bb[x] = qyb[x - 1] - qya[x] - dx[x] * rsigdt;
                            dd[x] = -(q[x - 1] - q[x]) * rsig;
                        }
                    }

                    Tri(1, n, aa, ref bb, cc, dd, ref ee, ref dy);
                    //update unknowns
                    Matrix <double> smM = Matrix <double> .Build.DenseOfArray(sm);

                    qsexsM = Matrix <double> .Build.DenseOfArray(qsexs);

                    qsexsdM = Matrix <double> .Build.DenseOfArray(qsexsd);

                    sdrn[j] = sdrn[j] + (q[n] + sig * qya[n] * dy[n]) * dt;
                    smM.SetRow(j, smM.Row(j) + Vector <double> .Build.DenseOfArray(dy.Slice(1, n)));
                    sm = smM.ToArray();
                    if (sex.GetLength(0) > 1) // need to test, this will need to be transposed.
                    {
                        Matrix <double> sexM = Matrix <double> .Build.Dense(sex.GetLength(0), sex.GetLength(1));

                        for (int x = 0; x < sex.GetLength(0); x++)
                        {
                            for (int y = 0; y < sex.GetLength(1); y++)
                            {
                                sexM[x, y] = sex[x, y, j];
                            }
                        }

                        Vector <double> dysub = Vector <double> .Build.DenseOfArray(dy.Slice(1, n));

                        for (i = 1; i <= nex; i++)
                        {
                            sexM.SetRow(i, sexM.Row(i) + (qsexsM.Row(i) + sig * qsexsdM.Row(i) * Vector <double> .Build.DenseOfArray(csm) * dysub) * dt);
                        }

                        for (int x = 0; x < sex.GetLength(0); x++)
                        {
                            for (int y = 0; y < sex.GetLength(1); y++)
                            {
                                sex[j, y, x] = sexM[y, x];
                            }
                        }
                    }
                    nssteps[j] = nssteps[j] + 1;
                }
            }
        }
Exemple #6
0
        static double[,] isopar;                          //2 params (nt,2)

        //public static void Setup()
        public static void Main(string[] args)
        {
            Soil.SoilProperties = new Dictionary <string, SoilProps>();
            Fluxes.FluxTables   = new Dictionary <string, FluxTable>();
            Program.GenerateFlux();

            c0      = new double[ns + 1];
            cin     = new double[ns + 1];
            h       = new double[n + 1];
            S       = new double[n + 1];
            isotype = new string[nt + 1];
            isopar  = new double[nt + 1, 2 + 1];
            soff    = new double[ns + 1];
            sdrn    = new double[ns + 1];
            sinfil  = new double[ns + 1];
            SoilData sd = new SoilData();

            Flow.sink = new SinkDripperDrain(); //set the type of sink to use
            jt        = new int[n + 1];
            nssteps   = new int[ns + 1];
            sidx      = new int[n + 1];
            sm        = new double[ns + 1, n + 1];
            //set a list of soil layers(cm).
            x = new double[] { 0, 10.0, 20.0, 30.0, 40.0, 60.0, 80.0, 100.0, 120.0, 160.0, 200.0 };
            for (int c = 1; c <= n; c++)
            {
                sidx[c] = c < 5 ? 103 : 109; //soil ident of layers
            }
            //set required soil hydraulic params
            sd.GetTables(n, sidx, x);
            SolProps solProps = new SolProps(nt, ns);

            bd  = new double[] { 0, 1.3, 1.3 };
            dis = new double[] { 0, 20.0, 20.0 };
            //set isotherm type and params for solute 2 here
            isotype[1]   = "Fr";
            isotype[2]   = "La";
            isopar[1, 1] = 1.0;
            isopar[1, 2] = 1.0;
            isopar[2, 1] = 0.5;
            isopar[2, 2] = 0.01;
            Matrix <double> isoparM = Matrix <double> .Build.DenseOfArray(isopar);

            for (j = 1; j <= nt; j++) //set params
            {
                solProps.Solpar(j, bd[j], dis[j]);
                //set isotherm type and params
                solProps.Setiso(j, 2, isotype[j], isoparM.Column(j).ToArray());
            }
            //initialise for run
            ts = 0.0;          //start time
                               //dSmax controls time step.Use 0.05 for a fast but fairly accurate solution.
                               //Use 0.001 to get many steps to test execution time per step.
            Flow.dSmax = 0.01; //0.01 ensures very good accuracy
            for (int c = 1; c <= n; c++)
            {
                jt[c] = c < 5 ? 1 : 2;      //!4 layers of type 1, rest of type2
            }
            h0 = 0.0;                       //pond depth initially zero
            h1 = -1000.0;
            h2 = -400.0;                    //initial matric heads
            double Sh = 0;                  //not used for this call but required as C# does not have 'present' operator

            sd.Sofh(h1, 1, out S1, out Sh); //solve uses degree of satn
            sd.Sofh(h2, 5, out S2, out Sh);
            for (int c = 1; c <= n; c++)
            {
                S[c] = c < 5 ? S1 : S2;
            }
            wpi    = MathUtilities.Sum(MathUtilities.Multiply(MathUtilities.Multiply(sd.ths, S), sd.dx)); //water in profile initially
            nsteps = 0;                                                                                   //no.of time steps for water soln(cumulative)
            win    = 0.0;                                                                                 //water input(total precip)
            evap   = 0.0;
            runoff = 0.0;
            infil  = 0.0;
            drn    = 0.0;
            for (int col = 1; col < sm.GetLength(0); col++)
            {
                sm[col, 1] = 1000.0 / sd.dx[1];
            }
            //initial solute concn(mass units per cc of soil)
            //solute in profile initially
            spi          = new [] { 1000.0, 1000.0 };
            Flow.dsmmax  = 0.1 * sm[1, 1]; //solute stepsize control param
            Flow.nwsteps = 10;
            MathUtilities.Zero(c0);
            MathUtilities.Zero(cin); //no solute input
            nssteps.Populate(0);     //no.of time steps for solute soln(cumulative)
            MathUtilities.Zero(soff);
            MathUtilities.Zero(sinfil);
            MathUtilities.Zero(sdrn);
            qprec          = 1.0;              //precip at 1 cm / h for first 24 h
            ti             = ts;
            qevap          = 0.05;             // potential evap rate from soil surface
            double[,] wex  = new double[1, 1]; //unused option params in FORTRAN... must be a better way of doing this
            double[,,] sex = new double[1, 1, 1];

            //timer here in FORTRAN, this basically runs the solution for 100 days
            for (j = 1; j <= 100; j++)
            {
                tf = ti + 24.0;
                Flow.Solve(solProps, sd, ti, tf, qprec, qevap, ns, Flow.sink.nex, ref h0, ref S, ref evap, ref runoff, ref infil, ref drn, ref nsteps, jt, cin, ref c0, ref sm, ref soff, ref sinfil, ref sdrn, ref nssteps, ref wex, ref sex);
                win = win + qprec * (tf - ti);
                if (j == 1)
                {
                    ti = tf;
                }
                qprec = 0.0;
            }
            win = win + qprec * (tf - ti);
            wp  = MathUtilities.Sum(MathUtilities.Multiply(MathUtilities.Multiply(sd.ths, S), sd.dx)); //!water in profile
            double hS = 0;                                                                             //hS is not used used here, but is a required parameter

            for (j = 1; j <= n; j++)
            {
                sd.hofS(S[j], j, out h[j], out hS);
            }
        }
Exemple #7
0
        /// <summary>
        /// Solves the equation of continuity from time ts to tfin.
        /// </summary>
        /// <param name="sol">solute properties.</param>
        /// <param name="sdata">soil data.</param>
        /// <param name="ts">start time (h).</param>
        /// <param name="tfin">finish time.</param>
        /// <param name="qprec">precipitation (or water input) rate (fluxes are in cm/h).</param>
        /// <param name="qevap">potl evaporation rate from soil surface.</param>
        /// <param name="nsol">no. of solutes.</param>
        /// <param name="nex">no. of water extraction streams.</param>
        /// <param name="h0">surface head, equal to depth of surface pond.</param>
        /// <param name="S">degree of saturation ("effective satn") of layers.</param>
        /// <param name="evap">cumulative evaporation from soil surface (cm, not initialised).</param>
        /// <param name="runoff">cumulative runoff.</param>
        /// <param name="infil">cumulative net infiltration (time integral of flux across surface).</param>
        /// <param name="drn">cumulative net drainage (time integral of flux across bottom).</param>
        /// <param name="nsteps">cumulative no. of time steps for RE soln.</param>
        /// <param name="jt">layer soil type numbers for solute.</param>
        /// <param name="cin">solute concns in water input (user's units/cc).</param>
        /// <param name="c0">solute concns in surface pond.</param>
        /// <param name="sm">solute (mass) concns in layers.</param>
        /// <param name="soff">cumulative solute runoff (user's units).</param>
        /// <param name="sinfil">cumulative solute infiltration.</param>
        /// <param name="sdrn">cumulative solute drainage.</param>
        /// <param name="nssteps">cumulative no. of time steps for ADE soln.</param>
        /// <param name="wex">cumulative water extractions from layers.</param>
        /// <param name="sex">cumulative solute extractions from layers.</param>
        public static void Solve(SolProps sol, SoilData sdata, double ts, double tfin, double qprec, double qevap, int nsol, int nex,
                                 ref double h0, ref double[] S, ref double evap, ref double runoff, ref double infil, ref double drn, ref int nsteps, int[] jt, double[] cin,
                                 ref double[] c0, ref double[,] sm, ref double[] soff, ref double[] sinfil, ref double[] sdrn, ref int[] nssteps, ref double[,] wex, ref double[,,] sex)
        {
            // Since S.Length is one greater in Fortran to account for the 0 based index, this line is included
            // so that the +1 nomenclature can be kept below to avoid (further) confusion.
            int sLength = S.Length - 1;

            bool again, extraction, initpond, maxpond;
            int  i, iflux, ih0, iok, isatbot, itmp, ns, nsat, nsatlast, nsteps0;

            int[]  isat = new int[sLength + 1];
            double accel, dmax, dt, dwinfil, dwoff, fac, infili, qpme, qprec1, rsig, rsigdt, sig, t, ti, win, xtblbot;

            double[] dSdt  = new double[sLength + 1];
            double[] h     = new double[sLength + 1];
            double[] xtbl  = new double[sLength + 1];
            double[] thi   = new double[sLength + 1];
            double[] thf   = new double[sLength + 1];
            double[] qwex  = new double[sLength + 1];
            double[] qwexd = new double[sLength + 1];
            double[,] dwexs  = new double[sLength + 1, nex + 1];
            double[,] qwexs  = new double[sLength + 1, nex + 1];
            double[,] qwexsd = new double[sLength + 1, nex + 1];
            double[] aa      = new double[sLength + 1]; // these are 0 based
            double[] bb      = new double[sLength + 1];
            double[] cc      = new double[sLength + 1];
            double[] dd      = new double[sLength + 1];
            double[] dy      = new double[sLength + 1];
            double[] ee      = new double[sLength + 1];
            double[] q       = new double[sLength + 1];
            double[] qya     = new double[sLength + 1];
            double[] qyb     = new double[sLength + 1];
            double[] cav     = new double[nsol + 1];
            double[] sinfili = new double[nsol + 1];
            double[,] c;
            sd = sdata;
            c  = new double[nsol + 1, sd.n + 1];
            ISink sink = new SinkDripperDrain(); // Sink type can be changed here.

            /*
             * ! The saturation status of a layer is stored as 0 or 1 in isat since S may be
             * ! >1 (because of previous overshoot) when a layer desaturates. Fluxes at the
             * ! beginning of a time step and their partial derivs wrt S or h of upper and
             * ! lower layers or boundaries are stored in q, qya and qyb.
             */
            isatbot    = 0;
            xtblbot    = 0;
            dt         = 0.0;
            fac        = 0.0;
            initpond   = false;
            extraction = false;
            dwoff      = 0.0;
            ti         = 0.0;
            infili     = 0.0;

            if (nex > 0)
            {
                extraction = true;
            }

            if (sLength != sd.n)
            {
                Console.WriteLine("solve: Size of S differs from table data.");
                Environment.Exit(1);
            }

            //-----set up for boundary conditions
            if (botbc == "constant head") // !h at bottom bdry specified
            {
                if (hbot < sd.he[sd.n])
                {
                    isatbot = 0;
                    xtblbot = Sbot;
                }
                else
                {
                    isatbot = 1;
                    xtblbot = hbot - sd.he[sd.n];
                }
            }
            //-----end set up for boundary conditions
            //-----initialise
            t       = ts;
            nsteps0 = nsteps;
            nsat    = 0;
            //initialise saturated regions
            for (int x = 1; x < sLength; x++)
            {
                if (S[x] >= 1.0)
                {
                    isat[x] = 1;
                    h[x]    = sd.he[x];
                }
                else
                {
                    isat[x] = 0;
                    h[x]    = sd.he[x] - 1.0;
                }
            }

            if (nsol > 0)
            {
                //set solute info
                thi = MathUtilities.Multiply(sd.ths, S); //initial th
                dwexs.Populate2D(0);                     //initial water extracted from layers

                ti      = t;
                infili  = infil;
                sinfili = sinfil;
                double c0Temp = c0[1];
                if (h0 > 0 && cin.Any(x => x != c0Temp)) // count(c0 /= cin) > 0)
                {
                    initpond = true;                     //initial pond with different solute concn
                }
                else
                {
                    initpond = false;
                }
                c.Populate2D(0); //temp storage for soln concns
            }
            //-----end initialise
            //-----solve until tfin

            while (t < tfin)
            {
                //-----take next time step
                for (iflux = 1; iflux <= 2; iflux++) //sometimes need twice to adjust h at satn
                {
                    nsatlast = nsat;                 // for detecting onset of profile saturation
                    nsat     = isat.Sum();
                    sig      = 0.5;
                    if (nsat != 0)
                    {
                        sig = 1.0; //time weighting sigma
                    }
                    rsig = 1.0 / sig;
                    //-----get fluxes and derivs
                    //     get table entries
                    for (int x = 1; x < isat.Length; x++)
                    {
                        if (isat[x] == 0)
                        {
                            xtbl[x] = S[x];
                        }
                        else
                        {
                            xtbl[x] = h[x] - sd.he[x];
                        }
                    }

                    //get surface flux
                    qpme   = qprec - qevap;                  //input rate at saturation
                    qprec1 = qprec;                          //may change qprec1 to maintain pond if required
                    if (h[1] <= 0 && h0 <= 0 && nsat < sd.n) //no ponding
                    {
                        ns = 1;                              //start index for eqns
                        sd.GetQ(0, new [] { 0, 0, isat[1] }, new [] { 0, 0, xtbl[1] }, out q[0], out qya[0], out qyb[0]);
                        if (q[0] < qpme)
                        {
                            q[0]   = qpme;
                            qyb[0] = 0;
                        }
                        maxpond = false;
                    }
                    else //ponding
                    {
                        ns = 0;
                        sd.GetQ(0, new [] { 0, 1, isat[1] }, new [] { 0, h0 - sd.he[1], xtbl[1] }, out q[0], out qya[0], out qyb[0]);
                        if (h0 >= h0max && qpme > q[0])
                        {
                            maxpond = true;
                            ns      = 1;
                        }
                        else
                        {
                            maxpond = false;
                        }
                    }

                    //get profile fluxes
                    for (i = 1; i <= sd.n - 1; i++)
                    {
                        sd.GetQ(i, new [] { 0, isat[i], isat[i + 1] }, new [] { 0, xtbl[i], xtbl[i + 1] }, out q[i], out qya[i], out qyb[i]);
                    }

                    //get bottom flux
                    switch (botbc)
                    {
                    case "constant head":
                        sd.GetQ(sd.n, new [] { 0, isat[sd.n], isatbot }, new [] { 0, xtbl[sd.n], xtblbot }, out q[sd.n], out qya[sd.n], out qyb[sd.n]);
                        break;

                    case "0.0 flux":
                        q[sd.n]   = 0;
                        qya[sd.n] = 0;
                        break;

                    case "free drainage":
                        sd.GetK(sd.n, isat[sd.n], xtbl[sd.n], out q[sd.n], out qya[sd.n]);
                        break;

                    case "seepage":
                        if (h[sd.n] <= -0.5 * sd.dx[sd.n])
                        {
                            q[sd.n]   = 0;
                            qya[sd.n] = 0;
                        }
                        else
                        {
                            sd.GetQ(sd.n, new int[] { 0, isat[sd.n], 1 }, new double[] { 0, xtbl[sd.n], -sd.he[sd.n] }, out q[sd.n], out qya[sd.n], out qyb[sd.n]);
                        }
                        break;

                    default:
                        Console.Out.WriteLine("solve: illegal bottom boundary condn");
                        Environment.Exit(1);
                        break;
                    }

                    if (extraction) //get rate of extraction
                    {
                        sink.Wsinks(t, isat, xtbl, sd.he, ref qwexs, ref qwexsd);

                        for (int x = 1; x < qwexs.GetLength(1); x++)
                        {
                            qwex[x] = Matrix <double> .Build.DenseOfArray(qwexs).Column(x).Sum();

                            qwexd[x] = Matrix <double> .Build.DenseOfArray(qwexsd).Column(x).Sum();
                        }
                    }
                    again = false; //flag for recalcn of fluxes
                    //-----end get fluxes and derivs
                    //----estimate time step dt
                    dmax = 0;
                    dSdt = MathUtilities.CreateArrayOfValues(0, dSdt.Length);
                    for (int x = 1; x <= sd.n; x++)
                    {
                        if (isat[x] == 0)
                        {
                            dSdt[x] = Math.Abs(q[x] - q[x - 1] + (extraction ? qwex[x] : 0)) / (sd.ths[x] * sd.dx[x]);
                        }
                    }

                    dmax = MathUtilities.Max(dSdt); //Max derivative | dS / dt |
                    if (dmax > 0)
                    {
                        dt = dSmax / dmax;
                        // if pond going adjust dt
                        if (h0 > 0 && (q[0] - qpme) * dt > h0)
                        {
                            dt = (h0 - 0.5 * h0min) / (q[0] - qpme);
                        }
                    }
                    else //steady state flow
                    {
                        if (qpme >= q[sd.n]) //step to finish -but what if extraction varies with time ???
                        {
                            dt = tfin - t;
                        }
                        else
                        {
                            dt = -(h0 - 0.5 * h0min) / (qpme - q[sd.n]); //pond going so adjust dt
                        }
                    }

                    if (dt > dtmax)
                    {
                        dt = dtmax; //user's limit
                    }
                    // if initial step, improve h where S>= 1
                    if (nsteps == nsteps0 && nsat > 0 && iflux == 1)
                    {
                        again = true;
                        dt    = 1.0e-20 * (tfin - ts);
                    }
                    if (nsat == sd.n && nsatlast < sd.n && iflux == 1)
                    {
                        //profile has just become saturated so adjust h values
                        again = true;
                        dt    = 1.0e-20 * (tfin - ts);
                    }
                    if (t + 1.1 * dt > tfin) //step to finish
                    {
                        dt = tfin - t;
                        t  = tfin;
                    }
                    else
                    {
                        t = t + dt; //tentative update
                    }
                    //-----end estimate time step dt
                    //-----get and solve eqns
                    rsigdt = 1.0 / (sig * dt);
                    //aa, bb, cc and dd hold coeffs and rhs of tridiag eqn set
                    for (int x = ns; x <= sd.n - 1; x++)
                    {
                        aa[x + 1] = qya[x];
                        cc[x]     = -qyb[x];
                    }
                    if (extraction)
                    {
                        for (int x = 1; x <= sd.n; x++)
                        {
                            dd[x] = -(q[x - 1] - q[x] - qwex[x]) * rsig;
                        }
                    }
                    else
                    {
                        for (int x = 1; x <= sd.n; x++)
                        {
                            dd[x] = -(q[x - 1] - q[x]) * rsig;
                        }
                    }

                    iok  = 0;        //flag for time step test
                    itmp = 0;        //counter to abort if not getting solution
                    while (iok == 0) //keep reducing time step until all ok
                    {
                        itmp  = itmp + 1;
                        accel = 1.0 - 0.05 * Math.Min(10, Math.Max(0, itmp - 4)); //acceleration
                        if (itmp > 20)
                        {
                            Console.Out.WriteLine("solve: too many iterations of equation solution");
                            Environment.Exit(1);
                        }
                        if (ns < 1)
                        {
                            bb[0] = -qya[0] - rsigdt;
                            dd[0] = -(qpme - q[0]) * rsig;
                        }

                        for (int x = 1; x <= sd.n; x++)
                        {
                            bb[x] = qyb[x - 1] - qya[x];
                            if (isat[x] == 0)
                            {
                                bb[x] -= sd.ths[x] * sd.dx[x] * rsigdt;
                            }

                            if (extraction)
                            {
                                bb[x] -= qwexd[x];
                            }
                        }

                        Tri(ns, sd.n, aa, ref bb, cc, dd, ref ee, ref dy);
                        //dy contains dS or, for sat layers, h values
                        iok = 1;
                        if (!again)
                        {
                            //check if time step ok, if not then set fac to make it less
                            iok = 1;
                            for (i = 1; i <= sd.n; i++)
                            {
                                if (isat[i] == 0) //check change in S
                                {
                                    if (Math.Abs(dy[i]) > dSfac * dSmax)
                                    {
                                        fac = Math.Max(0.5, accel * Math.Abs(dSmax / dy[i]));
                                        iok = 0;
                                        break;
                                    }
                                    if (-dy[i] > dSmaxr * S[i])
                                    {
                                        fac = Math.Max(0.5, accel * dSmaxr * S[i] / (-dSfac * dy[i]));
                                        iok = 0;
                                        break;
                                    }
                                    if (S[i] < 1.0 && S[i] + dy[i] > Smax)
                                    {
                                        fac = accel * (0.5 * (1.0 + Smax) - S[i]) / dy[i];
                                        iok = 0;
                                        break;
                                    }
                                    if (S[i] >= 1.0 && dy[i] > 0.5 * (Smax - 1.0))
                                    {
                                        fac = 0.25 * (Smax - 1.0) / dy[i]; iok = 0;
                                        break;
                                    }
                                }
                            }
                            if (iok == 1 && ns < 1 && h0 < h0max && h0 + dy[0] > h0max + dh0max)
                            {
                                //start of runoff
                                fac = (h0max + 0.5 * dh0max - h0) / dy[0];
                                iok = 0;
                            }
                            if (iok == 1 && ns < 1 && h0 > 0.0 && h0 + dy[0] < h0min)
                            {
                                //pond going
                                fac = -(h0 - 0.5 * h0min) / dy[0];
                                iok = 0;
                            }
                            if (iok == 0) //reduce time step
                            {
                                t      = t - dt;
                                dt     = fac * dt;
                                t      = t + dt;
                                rsigdt = 1.0 / (sig * dt);
                                nless  = nless + 1; //count step size reductions
                            }
                            if (isat[1] != 0 && iflux == 1 && h[1] < 0.0 && h[1] + dy[1] > 0.0)
                            {
                                //incipient ponding - adjust state of saturated regions
                                t      = t - dt;
                                dt     = 1.0e-20 * (tfin - ts);
                                rsigdt = 1.0 / (sig * dt);
                                again  = true;
                                iok    = 0;
                            }
                        }
                    } //end while

                    //-----end get and solve eqns
                    //-----update unknowns
                    ih0 = 0;
                    if (!again)
                    {
                        dwoff = 0.0;
                        if (ns < 1)
                        {
                            h0 = h0 + dy[0];
                            if (h0 < 0.0 && dy[0] < 0.0)
                            {
                                ih0 = 1; //pond g1.0
                            }
                            evap = evap + qevap * dt;
                            //note that fluxes required are q at sigma of time step
                            dwinfil = (q[0] + sig * (qya[0] * dy[0] + qyb[0] * dy[1])) * dt;
                        }
                        else
                        {
                            dwinfil = (q[0] + sig * qyb[0] * dy[1]) * dt;
                            if (maxpond)
                            {
                                evap = evap + qevap * dt;
                                if (qprec > qprecmax) // set input to maintain pond
                                {
                                    qpme   = q[0] + sig * qyb[0] * dy[1];
                                    qprec1 = qpme + qevap;
                                    dwoff  = 0.0;
                                }
                                else
                                {
                                    dwoff = qpme * dt - dwinfil;
                                }
                                runoff = runoff + dwoff;
                            }
                            else
                            {
                                evap = evap + qprec1 * dt - dwinfil;
                            }
                        }
                        infil = infil + dwinfil;
                        if (nsol > 0)     //get surface solute balance
                        {
                            if (initpond) //pond concn != cin
                            {
                                if (h0 > 0.0)
                                {
                                    if (ns == 1) // if max pond depth
                                    {
                                        dy[0] = 0.0;
                                    }
                                    for (int x = 1; x < cav.Length; x++)
                                    {
                                        cav[x] = ((2.0 * h0 - dy[0]) * c0[x] + qprec1 * dt * cin[x]) / (2.0 * h0 + dwoff + dwinfil);
                                    }
                                    for (int x = 1; x < c0.Length; x++)
                                    {
                                        c0[x] = 2.0 * cav[x] - c0[x]; //This needs to be tested from FORTRAN; no example in original code.
                                    }
                                }
                                else
                                {
                                    for (int x = 1; x < cav.Length; x++)
                                    {
                                        cav[x] = ((h0 - dy[0]) * c0[x] + qprec1 * dt * cin[x]) / (dwoff + dwinfil);
                                    }
                                    initpond = false; //pond gone
                                    c0       = cin;   // for output if any pond at end
                                }
                                soff   = MathUtilities.Add(soff, MathUtilities.Multiply_Value(cav, dwoff));
                                sinfil = MathUtilities.Add(sinfil, MathUtilities.Multiply_Value(cav, dwinfil));
                            }
                            else
                            {
                                soff   = MathUtilities.Add(soff, MathUtilities.Multiply_Value(cav, dwoff));
                                sinfil = MathUtilities.Add(sinfil, MathUtilities.Multiply_Value(cin, qprec1 * dt - dwoff));
                            }
                        }

                        // There's a condition based on botbc in the FORTRAN here, but both paths
                        // resolve to the same equation.
                        drn = drn + (q[sd.n] + sig * qya[sd.n] * dy[sd.n]) * dt;

                        if (extraction)
                        {
                            Matrix <double> dwexsM = Matrix <double> .Build.DenseOfArray(dwexs);

                            Matrix <double> qwexsM = Matrix <double> .Build.DenseOfArray(qwexs);

                            Matrix <double> qwexsdM = Matrix <double> .Build.DenseOfArray(qwexsd);

                            Matrix <double> wexM = Matrix <double> .Build.DenseOfArray(wex);

                            Vector <double> wexV;
                            Vector <double> dwexsV;
                            Vector <double> qwexsV;
                            Vector <double> qwexsdV;
                            if (nsol > 0)
                            {
                                //dwexs = dwexs + (qwexs + sig * qwexsd * spread(dy(1:n), 2, nex)) * dt
                                for (i = 1; i <= nex; i++)
                                {
                                    dwexsV  = dwexsM.Column(i);
                                    qwexsV  = qwexsM.Column(i);
                                    qwexsdV = qwexsdM.Column(i);
                                    dwexsV  = dwexsV + (qwexsV + sig * qwexsdV * Vector <double> .Build.DenseOfArray(dy.Slice(1, sd.n))) * dt;
                                    dwexsM.Column(i, dwexsV);
                                }
                                dwexs = dwexsM.ToArray();
                            }

                            //wex = wex + (qwexs + sig * qwexsd * spread(dy(1:n), 2, nex)) * dt
                            if (wex.GetLength(0) > 1) // analog for if (present(wex))
                            {
                                for (i = 1; i <= nex; i++)
                                {
                                    qwexsV  = qwexsM.Column(i);
                                    qwexsdV = qwexsdM.Column(i);
                                    wexV    = wexM.Column(i);
                                    wexV    = wexV + (qwexsV + sig * qwexsdV * Vector <double> .Build.DenseOfArray(dy.Slice(1, sd.n))) * dt;
                                    wexM.Column(i, wexV);
                                }
                                wex = wexM.ToArray();
                            }
                        }
                    }

                    for (i = 1; i <= sd.n; i++)
                    {
                        if (isat[i] == 0)
                        {
                            if (!again)
                            {
                                S[i] = S[i] + dy[i];
                                if (S[i] > 1.0 && dy[i] > 0.0) //saturation of layer
                                {
                                    isat[i] = 1;
                                    h[i]    = sd.he[i];
                                }
                            }
                        }
                        else
                        {
                            h[i] = h[i] + dy[i];
                            if (i == 1 && ih0 != 0 && h[i] >= sd.he[i])
                            {
                                h[i] = sd.he[i] - 1.0; //pond gone
                            }
                            if (h[i] < sd.he[i])       //desaturation of layer
                            {
                                isat[i] = 0;
                                h[i]    = sd.he[i];
                            }
                        }
                    }
                    //-----end update unknowns
                    if (!again)
                    {
                        break;
                    }
                }

                if (dt <= dtmin)
                {
                    Console.WriteLine("solve: time step = " + dt);
                    Environment.Exit(1);
                }
                //-----end take next time step
                //remove negative h0 (optional)
                if (h0 < 0.0 && isat[1] == 0)
                {
                    infil = infil + h0;
                    S[1]  = S[1] + h0 / (sd.ths[1] * sd.dx[1]);
                    h0    = 0.0;
                }
                nsteps = nsteps + 1;
                //solve for solute transport if required
                if (nwsteps * (nsteps / nwsteps) == nsteps)
                {
                    // This is function getsolute() in FORTRAN. Inline here as it uses a huge number of vars and is only used once.
                    if (nsol > 0 && t > ti)
                    {
                        thf = MathUtilities.Multiply(sd.ths, S);                                        //final th before call
                        win = infil - infili;                                                           //water in at top over time interval
                        cav = MathUtilities.Divide_Value(MathUtilities.Subtract(sinfil, sinfili), win); //average concn in win
                        Solute(ti, t, thi, thf, dwexs, win, cav, sd.n, nsol, nex, sd.dx, jt, dsmmax, ref sm, ref sdrn, ref nssteps, ref c, ref sex, extraction, sol);
                        ti  = t;
                        thi = thf;
                        dwexs.Populate2D(0);
                        infili  = infil;
                        sinfili = sinfil; // for next interval
                    }
                }
            }
            //-----end solve until tfin
            //finalise solute transport if required
        }
Exemple #8
0
        public void Solve()
        {
            SolProps sol = new SolProps(10,2);
            SoilData sd = new SoilData();
            Soil.SoilProperties = new Dictionary<string, SoilProps>();
            Fluxes.FluxTables = new Dictionary<string, FluxTable>();
            Program.GenerateFlux();

            sd.GetTables(10, new [] { 0, 103, 103, 103, 103, 109, 109, 109, 109, 109, 109 }, new double[] { 0, 10, 20, 30, 40, 60, 80, 100, 120, 160, 200 });
            sd.dx = new [] {0.0, 10.0, 10.0, 10.0, 10.0, 20.0, 20.0, 20.0, 20.0, 40.0, 40.0 };
            sd.n = 10;
            sd.ths = new [] { 0, 0.400000006, 0.400000006, 0.400000006, 0.400000006, 0.600000024, 0.600000024, 0.600000024, 0.600000024, 0.600000024, 0.600000024 };
            sd.he = new [] { 0.0, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 };
            Flow.sd = sd;
            double ts = 0.0;
            double tfin=24.0;
            double qprec=1.0;
            double qevap=0.05;
            int nsol=2;
            int nex=0;
            double h0=0;
            double[] S = {0, 0.208238059, 0.208238059, 0.208238059, 0.208238059, 0.698160143, 0.698160143, 0.698160143, 0.698160143, 0.698160143, 0.698160143 };
            double evap=0.0;
            double runoff = 0.0;
            double infil = 0.0;
            double drn = 0.0;
            int nsteps=0;
            int[] jt= { 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2 };
            double[] cin = { 0.0, 0.0, 0.0 };
            double[] c0 =  { 0.0, 0.0, 0.0 };
            double[,] sm = new double[3,11]; sm[1, 1] = 100; sm[2, 1] = 100;
            double[] soff = { 0.0, 0.0, 0.0 };
            double[] sinfil = { 0.0, 0.0, 0.0 };
            double[] sdrn = { 0.0, 0.0, 0.0 };
            int[] nssteps = { 0,0,0 };
            double[,] wex = new double[0, 0];
            double[,,] sex = new double[0, 0, 0];

            Flow.Solve(sol, sd, ts, tfin, qprec, qevap, nsol, nex, ref h0, ref S, ref evap, ref runoff, ref infil, ref drn,
                       ref nsteps, jt, cin, ref c0, ref sm, ref soff, ref sinfil, ref sdrn, ref nssteps, ref wex, ref sex);
        }
Exemple #9
0
        public void Solute()
        {
            int nt = 2;
            int ns = 2;
            SolProps sp = new SolProps(nt, ns);
            double[] bd = new double[nt + 1];
            double[] dis = new double[nt + 1];
            string[] isotype = { "", "Fr", "La" };
            double[,] isopar = { { 0, 0, 0 }, { 0, 1.0, 1.0 }, { 0, 0.5, 0.01 } };

            Array.Copy(new [] { 0, 1.3, 1.3 }, bd, 3);
            Array.Copy(new [] { 0, 20.0, 20.0 }, dis, 3);
            for (int j = 1; j <= nt; j++)
            {
                sp.Solpar(j, bd[j], dis[j]);
                sp.Setiso(j, 2, isotype[j], Extensions.GetRowCol(isopar, j, true));
            }
            int n = 10;
            int nex = 0;
            double ti = 0;
            double tf = 0.421148079;
            double[] thi = { 0, 8.32952248067425E-02, 8.32952248067425E-02, 8.32952248067425E-02, 8.32952248067425E-02, 4.18896102493218E-01, 4.18896102493218E-01, 4.18896102493218E-01, 4.18896102493218E-01, 4.18896102493218E-01, 4.18896102493218E-01 };
            double[] thf = { 0, 1.23293862599989E-01, 8.33055911391525E-02, 8.32952276559487E-02, 8.33189852482353E-02, 4.18882017281567E-01, 4.18896097739775E-01, 4.18896102481653E-01, 4.18896102470398E-01, 4.18896050247009E-01, 4.18896158171224E-01};
            double[,] dwexs = new double[nex, n];
            double win = 0.400090675;
            double[] cin = { 0, 0, 0 };
            double[] dx = { 0, 10, 10, 10, 10, 20, 20, 20, 20, 40, 40 };
            int[] jt = { 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2 };
            int dsmmax = 10;
            double[,] sm = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                                           {0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                                           {0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
            double[] sdrn =  { 0, 0, 0 };
            int[] nssteps =  { 0, 0, 0 };
            double[,] c = new double[ns + 1, n + 1];
            double[,,] sex = new double[0, 0, 0];

            double[,] smOUT = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                                {0, 9.9976219E+01, 2.3781371E-02, 2.2604628E-08, 2.0517180E-14, 1.2161602E-18, 4.8670606E-24, 1.9519348E-29, 7.8282825E-35, 1.2209528E-40, 1.7083828E-46},
                                {0, 9.9983728E+01, 1.6271857E-02, 9.8469378E-11, 5.6908331E-19, 2.1476414E-25, 2.0945763E-31, 2.0471666E-37, 2.0008407E-43, 7.6050509E-50, 2.5932571E-56} };
            double[] sdrnOUT = { 0, 9.10749016E-51, 3.36911468E-61 };
            int[] nsstepsOUT = { 0, 1, 1 };
            double[,] cOUT =  { {0, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00},
                                {0, 1.20054901384821E+03, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00},
                                {0, 7.68024454834764E+02, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00} };

            List<object> res = Flow.TestSolute(ti, tf, thi, thf, dwexs, win, cin, n, ns, nex, dx, jt, dsmmax, sm, sdrn, nssteps, c, sex, false, sp);

            double[,] smRes = res[0] as double[,];
            for (int i = 1; i < smOUT.GetLength(0); i++)
                for (int j = 1; j < smOUT.GetLength(1); j++)
                    Assert.AreEqual(smOUT[i, j], smRes[i, j], Math.Abs(smOUT[i, 1] * 1E-5)); //variations in sm at E-40... I don't think we really care at that point. Also probably outside of float range

            double[] sdrnRes = res[1] as double[];
            for (int i = 1; i < sdrnOUT.Length; i++)
                Assert.AreEqual(sdrnOUT[i], sdrnRes[i], 1E-10); //same as above. We're working with -60 exponents here. No bearing to reality.

            int[] nsstepsRes = res[2] as int[];
            for (int i = 1; i < nsstepsOUT.Length; i++)
                Assert.AreEqual(nsstepsOUT[i], nsstepsRes[i]);

            double[,] cRes = res[3] as double[,];
            for (int i = 1; i < cOUT.GetLength(0); i++)
                for (int j = 1; j < cOUT.GetLength(1); j++)
                    Assert.AreEqual(cOUT[i, j], cRes[i, j], Math.Abs(cOUT[i,j] * 1E-7));
        }
Exemple #10
0
        public void SetIso()
        {
            SolProps sp = new SolProps(2, 10);
            int[] j = { 1, 2 };
            int[] isol = { 2, 2 };
            string[] isotypeji = { "Fr", "La" };
            double[][] isoparji = { new [] { 0, 1, 0.5 }, new [] { 0, 1, 0.01 } };
            double[][] isopar = { new [] { 0, 1, 0.5, 0, 0 }, new [] { 0, 1, 0.01 } };

            for (int i = 0; i < j.Length; i++)
            {
                sp.Setiso(j[i], isol[i], isotypeji[i], isoparji[i]);
                for (int count = 1; count < isopar[i].Length; count++)
                {
                    Assert.AreEqual(isopar[j[i] - 1][count], sp.isopar[isol[i], j[i]][count], Math.Abs(isopar[j[i] - 1][count] * 1E-5));
                }
            }
        }
Exemple #11
0
        public void Isosub()
        {
            SolProps sp = new SolProps(2, 10);
            string iso = "Fr";
            double c = 0;
            double dsmmax = 10;
            double[] p = { 0, 1, 0.5, 0, 0 };
            double[] pOut = { 0, 1, 0.5, 0.01, 10 };
            double f;
            double fd;
            double fOut = 0;
            double fdOut = 10;

            sp.Isosub(iso, c, dsmmax, ref p, out f, out fd);

            for (int i = 0; i < p.Length; i++)
                Assert.AreEqual(pOut[i], p[i], Math.Abs(pOut[i] * 1E-5));
            Assert.AreEqual(fOut, f, Math.Abs(fOut * 1E-5));
            Assert.AreEqual(fdOut, fd, Math.Abs(fdOut * 1E-5));
        }
Exemple #12
0
        static double[,] sm; //(n, ns)

        #endregion Fields

        #region Methods

        //public static void Setup()
        public static void Main(string[] args)
        {
            Soil.SoilProperties = new Dictionary<string, SoilProps>();
            Fluxes.FluxTables = new Dictionary<string, FluxTable>();
            Program.GenerateFlux();

            c0 = new double[ns + 1];
            cin = new double[ns + 1];
            h = new double[n + 1];
            S = new double[n + 1];
            isotype = new string[nt + 1];
            isopar = new double[nt + 1, 2 + 1];
            soff = new double[ns + 1];
            sdrn = new double[ns + 1];
            sinfil = new double[ns + 1];
            SoilData sd = new SoilData();
            Flow.sink = new SinkDripperDrain(); //set the type of sink to use
            jt = new int[n+1];
            nssteps = new int[ns + 1];
            sidx = new int[n+1];
            sm = new double[ns + 1, n + 1];
            //set a list of soil layers(cm).
            x = new double[] { 0, 10.0, 20.0, 30.0, 40.0, 60.0, 80.0, 100.0, 120.0, 160.0, 200.0 };
            for (int c = 1; c <= n; c++)
                sidx[c] = c < 5 ? 103 : 109; //soil ident of layers
                                             //set required soil hydraulic params
            sd.GetTables(n, sidx, x);
            SolProps solProps = new SolProps(nt, ns);
            bd = new double[] {0, 1.3, 1.3 };
            dis = new double[] {0, 20.0, 20.0 };
            //set isotherm type and params for solute 2 here
            isotype[1] = "Fr";
            isotype[2] = "La";
            isopar[1, 1] = 1.0;
            isopar[1, 2] = 1.0;
            isopar[2, 1] = 0.5;
            isopar[2, 2] = 0.01;
            Matrix<double> isoparM = Matrix<double>.Build.DenseOfArray(isopar);
            for (j = 1; j <= nt; j++) //set params
            {
                solProps.Solpar(j, bd[j], dis[j]);
                //set isotherm type and params
                solProps.Setiso(j, 2, isotype[j], isoparM.Column(j).ToArray());
            }
            //initialise for run
            ts = 0.0; //start time
                      //dSmax controls time step.Use 0.05 for a fast but fairly accurate solution.
                      //Use 0.001 to get many steps to test execution time per step.
            Flow.dSmax = 0.01; //0.01 ensures very good accuracy
            for (int c = 1; c <= n; c++)
                jt[c] = c < 5 ? 1 : 2; //!4 layers of type 1, rest of type2
            h0 = 0.0; //pond depth initially zero
            h1 = -1000.0;
            h2 = -400.0; //initial matric heads
            double Sh = 0; //not used for this call but required as C# does not have 'present' operator
            sd.Sofh(h1, 1, out S1, out Sh); //solve uses degree of satn
            sd.Sofh(h2, 5, out S2, out Sh);
            for (int c = 1; c <= n; c++)
                S[c] = c < 5 ? S1 : S2;
            wpi = MathUtilities.Sum(MathUtilities.Multiply(MathUtilities.Multiply(sd.ths, S), sd.dx)); //water in profile initially
            nsteps = 0; //no.of time steps for water soln(cumulative)
            win = 0.0; //water input(total precip)
            evap = 0.0;
            runoff = 0.0;
            infil = 0.0;
            drn = 0.0;
            for (int col = 1; col < sm.GetLength(0); col++)
                sm[col, 1] = 1000.0 / sd.dx[1];
            //initial solute concn(mass units per cc of soil)
            //solute in profile initially
            spi = new []{1000.0, 1000.0};
            Flow.dsmmax = 0.1 * sm[1, 1]; //solute stepsize control param
            Flow.nwsteps = 10;
            MathUtilities.Zero(c0);
            MathUtilities.Zero(cin); //no solute input
            nssteps.Populate(0); //no.of time steps for solute soln(cumulative)
            MathUtilities.Zero(soff);
            MathUtilities.Zero(sinfil);
            MathUtilities.Zero(sdrn);
            qprec = 1.0; //precip at 1 cm / h for first 24 h
            ti = ts;
            qevap = 0.05;// potential evap rate from soil surface
            double[,] wex = new double[1,1]; //unused option params in FORTRAN... must be a better way of doing this
            double[,,] sex=new double[1,1,1];

            //timer here in FORTRAN, this basically runs the solution for 100 days
            for (j = 1; j <= 100; j++)
            {
                tf = ti + 24.0;
                Flow.Solve(solProps, sd, ti, tf, qprec, qevap, ns, Flow.sink.nex, ref h0, ref S, ref evap, ref runoff, ref infil, ref drn, ref nsteps, jt, cin, ref c0, ref sm, ref soff, ref sinfil, ref sdrn, ref nssteps, ref wex,ref sex);
                win = win + qprec * (tf - ti);
                if (j == 1)
                ti = tf;
                qprec = 0.0;
            }
            win = win + qprec * (tf - ti);
            wp = MathUtilities.Sum(MathUtilities.Multiply(MathUtilities.Multiply(sd.ths,S), sd.dx)); //!water in profile
            double hS = 0; //hS is not used used here, but is a required parameter
            for (j = 1; j <= n; j++)
                sd.hofS(S[j], j, out h[j], out hS);
        }