Пример #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;
                }
            }
        }
Пример #2
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;
                }
            }
        }
Пример #3
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));
        }